美文网首页
进一步优化ListView和RecyclerView的复用

进一步优化ListView和RecyclerView的复用

作者: SimonLeeeeeeeee | 来源:发表于2018-10-31 12:13 被阅读24次

这篇文章只是简单记录一下昨天遇到的一个问题。

ListViewRecyclerView的复用,应该除了小白大家都知道了。但是昨天在使用下拉刷新加载数据的时候,我发现调用notifyDataSetChanged()后loading动画会卡顿一下,约100-200毫秒。强迫症患者肯定不能忍,结合Choreographer进行排查,发现是在getView(...)的时候,inflate(...)非常耗时。

大家熟知,如果是当前已经有了一页数据,convertView可以直接复用。但如果当前没有数据呢?这时候convertView为null,就一定会走inflate(...)导致掉帧。但如果我们能提前完成inflate(...),后面来数据了直接复用就可以避免这个问题。

昨天经过郭霖大佬点播将ListView设置为INVISIBLE,默认加载一些空数据不显示出来,等正真的数据来了在设置为VISIBLE。这算是一个比较好的思路了,提前inflate(...)一页View,加载数据的时候直接复用即可。

但是我们现在来看一个使用场景:先隐藏加载了一页空数据,然后用户条件筛选,获取到数据集A,这时候数据集A为空,那么这些隐藏的item不会被复用,而是直接remove。然后用户更改条件再次请求数据,来了数据集B,这时候B不为空,getView(...)的时候没有可复用的convertView,依然会inflate(...)导致掉帧。这时候INVISIBLE大法就不好用了。

经过仔细斟酌,我想如果在Adapter中自己缓存一个View集合,在前面这个使用场景,来数据集B的时候,如果convertView为null不可复用,我还可以从自己的缓存集合中获取。下面放出示例代码:

public class ExampleAdapter<T> extends BaseAdapter {

    private final LayoutInflater mLayoutInflater;

    private List<T> mDataList = new ArrayList<>();

    private List<View> mConvertViewCaches = new ArrayList<>();

    protected ExampleAdapter(Context context) {
        mLayoutInflater = LayoutInflater.from(context);
    }

    @Override
    public int getCount() {
        if (mConvertViewCaches.size() == 0) {
            return 999;
        }
        return mDataList.size();
    }

    @Override
    public T getItem(int position) {
        if (position >= 0 && position < mDataList.size()) {
            return mDataList.get(position);
        }
        return null;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder;
        if (convertView == null) {
            for (View cache : mConvertViewCaches) {
                if (cache.getParent() == null) {
                    convertView = cache;
                    break;
                }
            }
        }
        if (convertView == null) {
            viewHolder = new ViewHolder();
            convertView  =  mLayoutInflater.inflate(R.layout.item_example, parent, false);
            //省略viewHolder操作
            convertView.setTag(viewHolder);
            mConvertViewCaches.add(convertView);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }
        T item = getItem(position);
        if (item != null) {
            //省略viewHolder操作
        }
        return convertView;
    }

}

代码很简单,就是多了一个mConvertViewCaches,用来缓存在getView(...)中通过inflate(...)解析出来的convertView,当convertView为null无法复用时,从mConvertViewCaches中获取可以复用的convertView,以减少inflate(...)操作。同时,在getCount()方法中通过判断mConvertViewCaches的大小,来区分是否ListView是否第一次加载,如果是第一次加载,返回一个足够大的数字来保证getView(...)解析出足够一页的布局候用。另外注意避免数据越界以及做好非空判断即可。

最后总结一下,尽量避免频繁的inflate操作,尤其是有动画的情况下,掉帧非常明显。另外,RecyclerViewListView,在Adapter中对convertView进行缓存即可,示例代码就不放出了。

相关文章

网友评论

      本文标题:进一步优化ListView和RecyclerView的复用

      本文链接:https://www.haomeiwen.com/subject/byootqtx.html