RecyclerView的优化

     自从recyclerview出来以后我们就基本上不用listview,但是recy有些地方并不是很完美,需要我们去优化,解决。

     1.给item设置点击事件、长按事件

     通常情况下我们设置点击事件或者长按事件都是通过下面的方式设置:

    @Override
    public void onBindViewHolder(XXViewHolder holder, int position) {
        holder.mItem.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //点击事件处理
            }
        });
        holder.mItem.setOnLongClickListener(new View.OnLongClickListener() {

            @Override
            public boolean onLongClick(View v) {
                //长按事件处理
                return false;
            }
        });
    }
通过上面这种方法处理,简单暴力,但是如果假如数据有上万条,快速滑动,内存中会创建大量的对象,很显然这种方法不是很好,需要优化,怎么优化呢?还好rec给我提供了addOnItemTouchListener这个方法,然后我们可以模仿view的点击事件是如何处理的来进行事件处理,这里我就直接引用网上一个处理的方式吧,非常不错:
public  class OnRecyclerItemClickListener implements RecyclerView.OnItemTouchListener {
    private GestureDetectorCompat mGestureDetectorCompat;//手势探测器
    private RecyclerView mRecyclerView;

    public OnRecyclerItemClickListener(RecyclerView recyclerView) {
        mRecyclerView = recyclerView;
        mGestureDetectorCompat=new GestureDetectorCompat(recyclerView.getContext(),new   GestureDetector.SimpleOnGestureListener(){
            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                View childViewUnder = mRecyclerView.findChildViewUnder(e.getX(), e.getY());
                if (childViewUnder != null) {
                    RecyclerView.ViewHolder childViewHolder = mRecyclerView.getChildViewHolder(childViewUnder);
                    onItemClick(childViewHolder);
                }
                return true;
            }

            @Override
            public void onLongPress(MotionEvent e) {
                View childViewUnder = mRecyclerView.findChildViewUnder(e.getX(), e.getY());
                if (childViewUnder != null) {
                    RecyclerView.ViewHolder childViewHolder = mRecyclerView.getChildViewHolder(childViewUnder);
                    onLongClick(childViewHolder);
                }
            }
        });
    }

    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        mGestureDetectorCompat.onTouchEvent(e);
        return false;
    }

    @Override
    public void onTouchEvent(RecyclerView rv, MotionEvent e) {
        mGestureDetectorCompat.onTouchEvent(e);
    }

    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
    }

    public void onItemClick(RecyclerView.ViewHolder viewHolder) {
    }

    public void onLongClick(RecyclerView.ViewHolder viewHolder) {
    }
}
如何使用呢,也很简单:

rec.addOnItemTouchListener(new OnRecyclerItemClickListener(rec) {
            @Override
            public void onItemClick(RecyclerView.ViewHolder viewHolder) {
                
            }
        });
好了,到此我们就可以优雅的给rec设置item点击事件了。

2.给item、item中的任意控件设置点击、长按事件

   下面说的这种方法既可以给item、也可给item中的任意控件设置点击、长按事件,不过给item设置还是建议上面的方法比较优雅。好了,还是那句话,如果不进行优化处理我们还是会通过上面的方法给item的控件设置事件,问题也一样,需要优化,优化代码如下:

    1)在onBindViewHolder方法中将position通过tag方式绑定到控件上

    @Override
    public void onBindViewHolder(AViewHolder holder, int position) {
        //设置数据,绑定tag
        holder.mBtn.setText(mDatas.get(position));
        holder.mBtn.setTag(position);
    }
至于为什么需要通过设置tag才能拿到position,我觉得不需要过多解释,因为控件的复用。

  2) viewholder中给控件设置点击事件,但是需要在构造器中将数据传递进来

static class AViewHolder extends RecyclerView.ViewHolder {
        Button mBtn;
        /**
         * 将数据通过构造器传递进来
         */
        ArrayList mDatas;

        public AViewHolder(View itemView, ArrayList data) {
            super(itemView);
            this.mDatas = data;
            mBtn = (Button) itemView.findViewById(R.id.button);
            mBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int position = (int) v.getTag();
                    Log.e("TAG", "onClick: " + mDatas.get(position).toString());
                }
            });
        }
    }
通过这种方式我们内存中会创建多少个点击事件对象呢?经过测试发现时屏幕显示个数+3个(有些不是很懂为什么多3个)
3.通过上面两个方式我们优雅的给item以及item中的控件设置了点击事件。那么下面我们再来优雅的来设计一个可展开的二级菜单rec。

   1)先看我们要实现的效果图效果:

RecyclerView的优化_第1张图片

   2)需求分析:

      一般情况下,后台返给我们的数据,解析出来javabean就基本上是下面这个样子:

public class DataBean {
    public ArrayList parent;

    static class ParentDataBean {
        public String name;
        public ArrayList childs;
        
        static class ChildDataBean {
            public String childName;
            public int parentId;
        }
    }
}
我们实现展开的二级列表的思路很简单,就是多类型的item来实现。那么这个时候就有个问题,就是我们adapter中的数据需要一个集合,而且集合中只能有一个类型的javabean,但是如果我们想要通过多类型item来实现,就需要面临一个问题,就是把childbean和parentbean弄成平级的,而不是现在这种父子级别(其实也不是父子级别,只不过这么说好理解)。怎么办呢?我们可以通过一个接口来实现:

public interface interfaceType {
    int itemType();
}
然后让父类、子类都实现这个接口,javabean就成了下面这个样子了,get\set方法省略掉暂时:

public class DataBean {
    public ArrayList parent;

    static class ParentDataBean implements interfaceType {
        public String name;
        public ArrayList childs;
        /**
         * 我们自己增加的字段,表示是否展开
         */
        public boolean isOpen;

        /**
         * 0表示父类 1表示子类
         *
         * @return
         */
        @Override
        public int itemType() {
            return 0;
        }

        static class ChildDataBean implements interfaceType {
            public String childName;
            public int parentId;

            @Override
            public int itemType() {
                return 1;
            }
        }
    }
}
其实我们写这个接口还有一个好处,就是我们的返回值,可以作为类型值:
    @Override
    public int getItemViewType(int position) {
        return mDatas.get(position).itemType();
    }
这里的数据集合mDatas就是ArrayList,这样就实现了父子的平级了。好了数据有了,然后类型也有了,然后我们设置数据、设置点击事件就可以了,当点击父亲的时候,我们需要展开子view,当父亲是展开的时候我们需要收起子view,这里我就贴出来展开收起的关键代码,具体代码后面demo会有:

public void openParent(int position, RecyclerView.ViewHolder viewHolder) {
        ArrayList childs = ((DataBean.ParentDataBean)  mDatas.get(position)).getChilds();
        onBindViewHolder((SecondViewHolder) viewHolder, position);
        mDatas.addAll(position + 1, childs);
        notifyItemRangeInserted(position + 1, childs.size());
    }
public void closeParent(int position, RecyclerView.ViewHolder viewHolder) {
        ArrayList childs = ((DataBean.ParentDataBean) mDatas.get(position)).getChilds();
        onBindViewHolder((SecondViewHolder) viewHolder, position);
        mDatas.removeAll(childs);
        notifyItemRangeRemoved(position + 1, childs.size());
    }
这么我们就实现了二级菜单的展开功能。

demo地址: http://download.csdn.net/download/zhq217217/10209951



你可能感兴趣的:(工作记录)