0

我有一个ListView,其中每一行都有一些文本和两个ImageView:一个对于每一行都是相同的,另一个取决于当前项目。

这是我的适配器:

mArrayAdapter(Context context, int layoutResourceId, ArrayList<Exhibition>  data) {
    super(context, layoutResourceId, data);
    this.context = context;
    this.layoutResourceId = layoutResourceId;
    this.list = data;
    this.originalList = data;
    viewHolder = new ViewHolder();
    final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
    final int cacheSize = maxMemory / 8;

    mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
        @Override
        protected int sizeOf(String key, Bitmap bitmap) {
            return bitmap.getByteCount() / 1024;
        }
    };

}

@Override
@NonNull
public View getView(final int position, View convertView, @NonNull final ViewGroup parent) {
    View row;
    final Exhibition ex;
    if(convertView==null){
        row = LayoutInflater.from(getContext()).inflate(R.layout.row,parent,false);

        viewHolder.expand = (ImageView)row.findViewById(R.id.expand);
        row.setTag(viewHolder);
    }
    else {
        row = convertView;
        viewHolder = (ViewHolder)row.getTag();
    }
    ex = list.get(position);


    descr = (TextView)row.findViewById(R.id.descr);
    ttl = (TextView)row.findViewById(R.id.title);
    city = (TextView)row.findViewById(R.id.city);
    dates = (TextView)row.findViewById(R.id.dates);
    museum = (TextView)row.findViewById(R.id.location);
    header = (ImageView)row.findViewById(R.id.hd);

    ttl.setText(ex.name);
    descr.setText(ex.longdescr);
    museum.setText(ex.museum);
    city.setText(ex.city);

    final Bitmap bitmap = getBitmapFromMemCache(ex.key);
    if (bitmap != null) {
        header.setImageBitmap(bitmap);
    } else {
        header.setImageBitmap(ex.getHeader());
        addBitmapToMemoryCache(ex.key,ex.header);
    }


    SimpleDateFormat myFormat = new SimpleDateFormat("dd/MM/yyyy", Locale.ITALY);
    Date start = new Date(ex.getStart()), end = new Date(ex.getEnd());

    String startEx = myFormat.format(start);
    String endEx = myFormat.format(end);

    String finalDate = getContext().getResources().getString(R.string.ex_date, startEx, endEx);

    dates.setText(finalDate);

    viewHolder.expand.setId(position);

    if(position == selectedId){
        descr.setVisibility(View.VISIBLE);
        ttl.setMaxLines(Integer.MAX_VALUE);
        dates.setMaxLines(Integer.MAX_VALUE);
        museum.setMaxLines(Integer.MAX_VALUE);
        city.setMaxLines(Integer.MAX_VALUE);
    }else{
        descr.setVisibility(View.GONE);
        ttl.setMaxLines(1);
        dates.setMaxLines(1);
        museum.setMaxLines(1);
        city.setMaxLines(1);
    }

    viewHolder.expand.setOnClickListener(this.onCustomClickListener);

    return row;
}

public void setDescr(int p){
    selectedId = p;
}

public void setOnCustomClickListener(final View.OnClickListener onClickListener) {
    this.onCustomClickListener = onClickListener;
}

public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
    if (getBitmapFromMemCache(key) == null) {
        mMemoryCache.put(key, bitmap);
    }
}

public Bitmap getBitmapFromMemCache(String key) {
    return mMemoryCache.get(key);
}


@Override
public int getCount()
{
    return list.size();
}

@Override
public boolean isEnabled(int position)
{
    return true;
}

@Override
public Exhibition getItem (int pos){
    return list.get(pos);
}

void resetData() {

    list = originalList;
}

private class ViewHolder {

    ImageView expand,header;

}

@Override
@NonNull
public Filter getFilter() {
    if (valueFilter == null) {
        Log.d("SEARCH1","New filter");
        valueFilter = new ValueFilter();
    }
    return valueFilter;
}

private class ValueFilter extends Filter {
    @Override
    protected FilterResults performFiltering(CharSequence constraint) {

        FilterResults results = new FilterResults();
        if(constraint == null || constraint.length() == 0){
            results.values = originalList;
            results.count = originalList.size();
        }
        else {

            List<Exhibition> nExhList = new ArrayList<>();

            for(Exhibition e : list){
                Log.d("NAMEE",e.name + " " + constraint.toString());
                if (e.getName().toUpperCase().contains(constraint.toString().toUpperCase()) || e.getCity().toUpperCase().contains(constraint.toString().toUpperCase())
                        ||e.getMuseum().toUpperCase().contains(constraint.toString().toUpperCase()) || e.getLongDescription().toUpperCase().contains(constraint.toString().toUpperCase())
                        || e.getDescription().toUpperCase().contains(constraint.toString().toUpperCase()) || e.getCategory().toUpperCase().contains(constraint.toString().toUpperCase())){
                    nExhList.add(e);
                }
            }
            results.values= nExhList;
            results.count=nExhList.size();
        }
        return results;
    }

    @Override
    protected void publishResults(CharSequence constraint,
                                  FilterResults results) {
        if(results.count==0){
            notifyDataSetInvalidated();
        }
        else{
            list = (ArrayList<Exhibition>)results.values;
            notifyDataSetChanged();
        }
    }
}

第一个ImageViewBitmap存储在Exhibition变量中。第二个更改文本的可见性以获得类似可扩展的效果(因为现在我无法将 转换ListView为 a ExpandableListView)。我尝试了不同的东西,比如缓存,一个AsyncTask,删除自定义点击监听器,把所有东西都放进去,ViewHolder但滚动充满了微滞后。适配器有什么问题我不明白吗?

4

2 回答 2

2

为了使您的列表顺利,您可以尝试以下选项,

  1. 您可以尝试使用流行的库,如GlidePicasso或其他一些开源库,而不是使用您自己的位图缓存方式
  2. 尽量避免在getView中进行耗时的操作,例如-日期转换可以在构建模型对象时移至对象级别,每个对象一次。
  3. 你可以试试 Recyclerview 而不是 ListView
于 2018-10-16T21:09:24.660 回答
1

您可以做几件事来提高性能。

弄清楚到底什么是慢的

了解性能分析,它可以告诉您哪些函数被调用最多和/或完成时间最长。通过这种方式,您可以决定在哪里投入时间修复或更改代码。

请参阅https://developer.android.com/studio/profile/android-profilerhttps://developer.android.com/studio/profile/

ViewHolder 模式

您在滥用ViewHolder 模式ViewHolder在您的代码中,您在适配器的viewHolder字段中有一个实例。然后,您可以在函数内部使用此字段,getView()就像使用常规局部变量一样。

row.findViewById()然后您多次调用,即使convertView不是null。调用很慢,视图持有者的findViewById()优点是您只需在展开后每个视图调用一次(在 convertView==null 的分支中if)。

相反,每个行视图应该有 1 个视图持有者。请注意,您不是在创建一个新ViewHolder的分配 with setTag(),而是在重用同一个。然后,而不是诸如descr, ttl,之类的变量city应该是 的字段,ViewHolder因此可以快速引用。

创建不必要的对象

内存分配也很慢。

每次getView()被调用时,您也会创建对象,您可以改为创建一次并重复使用。

一个这样的例子是SimpleDateFormat可以在适配器构造函数中创建一次并简单地用于生成文本。

研究如何避免创建如此多String的对象。使用字符串缓冲区或类似的东西进行格式化。您没有显示Exhibition该类的源代码,因此不清楚为什么需要Date使用调用getStart()and的结果创建一个对象getEnd()

如果对象的 'start' 和 'end' 字段Exhibition从未用作longs,请考虑Date在 JSON 解析期间将它们转换为不可变的 s,而不是每次使用它们时。

UI线程中潜在的慢速调用

该类的源代码Exhibition未显示,因此我们无法确定该Exhitition.getHeader()函数的作用。如果有位图下载和/或解码,将其移动到后台线程(并在位图准备好后更新)将提高ListViews 滚动性能。

不必要的电话

即使不需要,也会执行一些呼叫。例如在getView(). convertView当您进行通货膨胀时(何时是) ,您可以只设置一次,null因为所有行都使用相同的侦听器。

避免填满内存

您提到每个Exhibition对象都有一个Bitmap在解析 JSON 时设置的字段。这意味着所有位图一直都在内存中。这意味着在这种情况下,LRU 缓存不是必需的,因为始终存在对位图的强引用。

这也意味着随着列表中项目数量的增加,所需的内存也会增加。随着更多内存的使用,垃圾收集 (GC) 需要更频繁地发生,并且 GC 很慢并且可能导致卡顿或冻结。分析可以告诉您您遇到的冻结是否是由于 GC。

如果内存中一次只有几个位图、当前在列表中可见的项目所需的位图以及更多位图,则位图缓存将很有用。如果在缓存中找不到所需的位图,则应从磁盘加载或从网络下载。

附言

请记住,您有一个setOnCustomClickListener()仅将引用分配给该字段的公共函数。如果您使用新的侦听器调用它,您当前的代码将在所有未使用新引用刷新和更新的行上使用旧侦听器。

于 2018-10-17T06:51:07.947 回答