盘他,RecyclerView中设置OnClickListener的性能优化

前言:

对于RecyclerView,在Adapter中为item中的一个View设置点击事件,图方便的写法是使用OnClickListener的匿名内部类,因为有IDE的自动补全功能,啪的一下好几行代码就出来了

    @Override
    public void onBindViewHolder (ViewHolder holder,int position){
        holder.testBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("onClick", "testBtn" + String.valueOf(position));
            }
        });
    }

可是这样的方式就导致列表在滚动的时候,生成大量多余的OnClickListener对象,因为列表的循环机制虽然让item复用了,但是却每次都生成了新的OnClickListener对象。这会导致性能浪费甚至掉帧,GC频次增加甚至卡顿。

改进:

Google搜到的优化方式大多是这样的:
复用OnClickListener对象,然后在onBindViewHolder()方法中通过setTag(position) 和getTag() 的方式,来传递点击事件的position给listener。

public class TestAdapter extends RecyclerView.Adapter implements View.OnClickListener{
   ...
   @Override
   public void onBindViewHolder(final Holder holder, final int position) {
       holder.itemView.setOnClickListener(this);
       holder.itemView.setTag(position);
       ...
   }
   @Override
   public void onClick(View v) {
       int position = (Integer) v.getTag();
       Log.d("onClick", "testBtn" + String.valueOf(position));
   }
}

这样的方式其实也还可以,不过如果开发者在某个adapter中忘了执行setTag(position) ,AS是不会给任何提示的,而且也能正常展示界面,直到用户点击时才会出现顺序错乱。这种方式就要完全靠开发者记性和测试人员的严谨,不过这种靠人工的方法觉得不太工程化,于是琢磨用更科学的方式进行优化。

我的方式:

我觉得ViewHolder作为OnClickListener是比较合适的。然后ViewHolder是继承了RecyclerView.ViewHolder的,阅读该类源码,在里面搜搜position关键字,确实发现了几个现成的方法:


盘他,RecyclerView中设置OnClickListener的性能优化_第1张图片
RecyclerView.ViewHolder的代码

有那么三个。getPosition(),在adapter的数据更新时,会因为异步处理而令返回的值有歧义,所以用@Deprecated注解标记为不推荐使用。剩下getLayoutPosition()和getAdapterPosition(),只看方法名的话感觉getAdapterPosition()方法靠谱,Google一下,的确如此,(参考:https://stackoverflow.com/questions/29684154/recyclerview-viewholder-getlayoutposition-vs-getadapterposition)

所以,我优化的方式整理如下,以比较复杂的item做例子:

public class TestAdapter extends RecyclerView.Adapter {

    @Override
    public void onBindViewHolder(final ViewHolder holder, final int position) {
        holder.testBtn.setOnClickListener(holder);
        holder.testTv.setOnClickListener(holder);
        holder.testImage.setOnClickListener(holder);
        holder.itemView.setOnClickListener(holder);
    }

    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            int wrapperPosition = getAdapterPosition();
            //如果你项目中的RecyclerView是用常规的方式封装成可以添加header的话,那么要减去header的数量
            //int wrapperPosition = getAdapterPosition() - headersCount;

            switch (v.getId()) {
                case R.id.test_btn:
                    //todo
                    break;
                case R.id.test_tv:
                    //todo
                    break;
                case R.id.test_image:
                    //todo
                    break;
            }
            if (itemView == v) {//itemView的判断
                //todo
            }
        }
    }
}

你可能感兴趣的:(盘他,RecyclerView中设置OnClickListener的性能优化)