盘他,RecyclerView中设置OnClickListene

作者: 黄光华 | 来源:发表于2019-03-19 14:56 被阅读12次

    前言:

    对于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.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中设置OnClickListene

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