Android开发笔记:RecyclerView(二)刷新与点击事件

RecyclerView(一)主要介绍了RecyclerView基础的使用方式,用于列表展示大量数据,介绍了一些方法和类的关系。列表中的数据通过一个LIst数据结构保存,通过adapter进行绑定和渲染,在使用RecyclerView时,不可避免的会遇到展示列表信息的变更,即List内容的更改,RecyclerView不会自动的展示更改,需要进行刷新操作。

RecyclerView的刷新

每个列表都对应了一个List的数据结构,对于类表的增删改都是通过对List进行的,本文在上一篇文章的基础上增加一个按钮,点击按钮后,增加adapter中list的数据:

btn = (Button)findViewById(R.id.mbtn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ArrayList data = new ArrayList<>();
                for(int i=10;i<20;i++){
                    data.add(i);
                }
                adapter.addData(data);
            }
        });

adapter中addData方法,直接在原来的List后添加新的数据,如下:

public void addData(ArrayList data){
        this.data.addAll(data);
    }

运行的效果为:

Android开发笔记:RecyclerView(二)刷新与点击事件_第1张图片

可以发现按钮点击后,只改变list数据,列表并没有自动更新。上篇文章中设置了自定义间隔,点击按钮前,5号item是最后一个,所以右边的间隔设置为50,点击按钮后,由于item5不是list数据中的最后一位,渲染时间隔就变为了12.

要使列表随着list的更新变化,就要进行刷新操作,说白了就是要告诉RecyclerView,数据发生了变化。RecyclerVIew的刷新我把他分为三个等级:全部刷新、局部刷新、控件刷新。

  • 全部刷新顾名思义,对于列表中的所有items都进行刷新,重新绘制,对应的方法是:notifyDataSetChanged,这样可以把新的数据也刷新出来。ListView的刷新方式就是全部刷新,缺点就是对于没有变化的item数据,也要强制刷新,效率不高。
  • 局部刷新是指只刷新部分items,有两个对应的方法:
    //更新指定位置
    public final void notifyItemChanged(int position) {
                mObservable.notifyItemRangeChanged(position, 1);
            }
    
    /**
     * 更新一部分位置
     * 从positionStart开始itemCount个数据
     */
    public void notifyItemRangeChanged(int positionStart, int itemCount) {
                notifyItemRangeChanged(positionStart, itemCount, null);
            }

     

  • 控件刷新是更小一层的刷新,只刷新item中的一个控件,比如很多人刷新时遇到的图片闪动问题,就是在刷新时,虽然Item中图片没有发生变化,但是由于整个item都重新绘制,所以图片出现了闪动,这个时候就需要用到控件刷新,只刷新部分控件。可以使用使用:
    public final void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
                mObservable.notifyItemRangeChanged(positionStart, itemCount, payload);
            }

    当payload为NULL时,即是局部刷新,需要重写onBindViewHolder(ViewHolder holder, int position, List payloads) 方法来实现具体控件的更新逻辑

    @Override
        public void onBindViewHolder(ViewHolder holder, int position, List payloads) {
            //为空时刷新整个item
            if (payloads.isEmpty()) {
                super.onBindViewHolder(holder, position, payloads);
            } else {
            //实现ViewHolder中控件的更新逻辑
                ...
            }
        } 

    RecyclerView列表的点击事件

    列表除了展示数据之外,还需要对点击事件进行响应,ListView中可以直接通过setItemClickListener来设置列表点击的监听,而RecyclerView没有给我们提供这样的接口,所以对于RecyclerView列表的点击需要我们自己创建,目前主流的方法有两种种

    • 对Item的View添加点击事件
    • 通过RecyclerView的addOnItemTouchListener()来实现

    1.Item的View添加点击事件

    虽然RecyclerView没有提供item的点击事件接口,但是由于每个item的视图是由我们自己定义的,可以通过对视图控件的点击事件进行监听,从而实现item的点击监听。

    列表再滚动的时候,会通过onbind()方法得到要显示的视图,可以再此方法中对View进行点击监听。这里最好建一个全局的listener,通过view.getID或getTag等方式判断点击的是哪个item,而不是给每个view 都创建一个监听器,这样在列表滑动时可以避免大量的对象创建和销毁。保证滑动的性能。

    @NonNull
        @Override
        public MyAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            context = parent.getContext();
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.rv_item_view,parent,false);
            MyAdapter.ViewHolder viewHolder = new MyAdapter.ViewHolder(view);
            view.setOnClickListener(this);
            view.setOnLongClickListener(this);
            return viewHolder;
        }

    在adapter中实现View点击后的具体逻辑即可,

    @Override
        public void onClick(View v) {
            int position = mRecyclerView.getChildAdapterPosition(v);
            Toast.makeText(context,"第"+position+"个被点击",Toast.LENGTH_SHORT).show();
        }
    @Override
        public boolean onLongClick(View v) {
            int position = mRecyclerView.getChildAdapterPosition(v);
            ...//添加长按处理的逻辑
            return true;
        }

    但是使用ListView的时候,我们的点击事件是通过adapter.setOnItemClickListener设置的,直接在Activity中就可以设置,比较方便,封装更好,所以我们也要想办法将接口暴露出来,方便调用。

    首先创建一个接口

    interface ItemClickListener{
            void onclick(int position);
            void onLongClick(int position);
        }
    

    在adapter中View的点击事件处理中,不写具体处理逻辑,只执行接口的方法,具体逻辑由实现了接口方法的类去实现

    private ItemClickListener itemClickListener;
    
    @Override
        public boolean onLongClick(View v) {
            int position = mRecyclerView.getChildAdapterPosition(v);
            itemClickListener.onLongClick(position);
        }
    
    public void onClick(View v) {
            int position = mRecyclerView.getChildAdapterPosition(v);
            itemClickListener.onclick(position);
        }

    然后将实现了接口的类传进来,就可以实现调用。例如我们在Activity中实现接口:

    mAdapter.setItemClickListener(new MyAdapter.ItemClickListener() {
                @Override
                public void onclick(int position) {
                    Toast.makeText(MainActivity.this,"第"+position+"位置点击",Toast.LENGTH_SHORT).show();
                }
    
                @Override
                public void onLongClick(int position) {
                    //长按事件
                }
            });

    2.addOnItemTouchListener()实现监听

    addOnItemTouchListener是RecyclerVIew中的一个内部接口,可以对RecyclView中的触摸事件进行拦截,接口方法如下:

    @Override
        public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
            return false;
        }
        
        @Override
        public void onTouchEvent(RecyclerView rv, MotionEvent e) {
    
        }
        
        @Override
        public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
    
        }

    在onInterceptTouchEvent中可以获取当前触摸位置对应的子item view,使用GestureDetector判断点击状态,决定是否要把事件拦截,在拦截的时候同时添加一个回调方法处理。实现起来比较麻烦,后续再写。

    你可能感兴趣的:(Android)