RecyclerView 中 item 点击事件的优化

RecyclerView 是个列表,从android版本5.0引进的,可以替代 ListView 实现一些功能,用法和 ListView 差不多,但解耦效果更好,下面在 Activity 中写一个简单的例子

Activity:

    private void init(){
        RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycle);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(linearLayoutManager);
        NumAdapter adapter = new NumAdapter(this);
        recyclerView.setAdapter(adapter);
    }
public class NumAdapter extends RecyclerView.Adapter<NumAdapter.NumViewHolder> {

    private Context context;

    public NumAdapter(Context context) {
        this.context = context;
    }


    @Override
    public NumViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        TextView tv = new TextView(context);
        tv.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 100));
        NumViewHolder holder = new NumViewHolder(tv);
        return holder;
    }

    @Override
    public void onBindViewHolder(final NumViewHolder holder, final int position) {
        holder.tv.setText(" this is  " +  position );

    }

    @Override
    public int getItemCount() {
        return 15;
    }

    class NumViewHolder extends RecyclerView.ViewHolder {
        TextView tv;

        public NumViewHolder(final View itemView) {
            super(itemView);
            tv = (TextView) itemView;
        }

    }

}

这个里面,我写的比较简单,固定的15个条目,每个item都是一个 TextView,宽度为铺满父容器,高度为100px。注意到 onCreateViewHolder() 方法中,在这个方法中创建 itemView 和 ViewHolder,我们直接创建,然后在 onBindViewHolder() 中使用;ListView 的适配器中 getView() 方法中,我们也会创建布局,使用ViewHolder是为了复用,并且还需要我们对
convertView 进行判空,自己实现复用功能,而 RecyclerView 的适配器就没这么麻烦了,它把 getView() 方法中的功能一拆为二,并且源码中已经替我们是想了 ViewHolder 的复用功能,我们只需要把创建的功能和UI改变的功能写好就行。

上述方法中,我们如果想实现item的点击事件和长点击事件怎么办?这就显出 RecyclerView 的不足处了,系统没有直接提供这些方法,还得我们自己动手来封装,最粗糙的写法,定义回调,每个item设置点击事件。比如设置个回调接口    
 

  public interface OnItemClickListener {

        void onItemClick(View view, int position);
    
        boolean onItemLongClick(View view, int position);

    }


    OnItemClickListener simpleClike = new OnItemClickListener(){

        @Override
        public void onItemClick(View view, int position) {
            Toast.makeText(getApplicationContext(), " onItemClick  " + position,Toast.LENGTH_LONG).show();
        }

        @Override
        public boolean onItemLongClick(View view, int position) {
            Toast.makeText(getApplicationContext(), " onItemLongClick  " + position,Toast.LENGTH_LONG).show();
        return true;
        }

    };
    


这样回调就定义好了,我们在回调中写个Toast事件,现在把回调设置到适配器中,适配器对外暴露接口;然后针对 item 设置点击事件和长按点击事件,在事件中写入回调,如下

public class NumAdapter extends RecyclerView.Adapter<NumAdapter.NumViewHolder> {

    private Context context;
    private OnItemClickListener mOnItemClickListener;

    public NumAdapter(Context context) {
        this.context = context;
    }

    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.mOnItemClickListener = onItemClickListener;
    }

    @Override
    public NumViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        TextView tv = new TextView(context);
        tv.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 100));
        NumViewHolder holder = new NumViewHolder(tv);
        return holder;
    }

    @Override
    public void onBindViewHolder(final NumViewHolder holder, final int position) {
        holder.tv.setText(" this is  " +  position );
        holder.tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(mOnItemClickListener != null){
                    mOnItemClickListener.onItemClick(v, position);
                }
            }
        });
        holder.tv.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                if(mOnItemClickListener != null){
                    return mOnItemClickListener.onItemLongClick(v, position);
                }
                return false;
            }
        });
    }

    @Override
    public int getItemCount() {
        return 15;
    }

    class NumViewHolder extends RecyclerView.ViewHolder {
        TextView tv;

        public NumViewHolder(final View itemView) {
            super(itemView);
            tv = (TextView) itemView;
        }

    }

}

这样写是实现了需求,但有个问题。系统源码为我们实现了 ViewHolder 的复用,但 onBindViewHolder() 方法是每个item出现时都会调用,也就是说,如果我们不停的上下滑动 RecyclerView, onBindViewHolder() 会被不停的调用,setOnClickListener 方法会不停创建对象,怎么减少无谓的对象创建呢?这便提到了优化的策略。由于 onCreateViewHolder() 方法有缓存策略,所以我们可以把点击事件写在这个里面,在创建 ViewHolder 中想想办法,这时候需要注意的是如何获取 position,没办法直接穿进去,好在适配器有个方法

public class NumAdapter extends RecyclerView.Adapter<NumAdapter.NumViewHolder> {

    private Context context;
    private OnItemClickListener mOnItemClickListener;

    public NumAdapter(Context context) {
        this.context = context;
    }

    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.mOnItemClickListener = onItemClickListener;
    }

    @Override
    public NumViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        TextView tv = new TextView(context);
        tv.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 100));
        NumViewHolder holder = new NumViewHolder(tv);
        return holder;
    }

    @Override
    public void onBindViewHolder(final NumViewHolder holder, final int position) {
        holder.tv.setText(" this is  " +  position );

    }

    @Override
    public int getItemCount() {
        return 15;
    }

    class NumViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener{
        TextView tv;

        public NumViewHolder(final View itemView) {
            super(itemView);
            tv = (TextView) itemView;
            tv.setOnClickListener(this);
            tv.setOnLongClickListener(this);
        }

        @Override
        public void onClick(View v) {
            if(mOnItemClickListener != null){
                mOnItemClickListener.onItemClick(v, getLayoutPosition());
            }
        }

        @Override
        public boolean onLongClick(View v) {
            if(mOnItemClickListener != null){
                return mOnItemClickListener.onItemLongClick(v, getLayoutPosition());
            }
            return false;
        }
    }

}


如果说我们想把集合中的对象传递出来怎么办?这里就要用到 View 的 setTag() 这个方法了,我们先在 styles.xml 中自定义一个属性:<item name="holder_id" type="id" />。然后在 onBindViewHolder() 方法中把值给设置进去,然后在点击事件中从view中拿出来,以简单的position为例   

@Override
    public void onBindViewHolder(final NumViewHolder holder, final int position) {
        holder.tv.setText(" this is  " +  position );
        holder.tv.setTag(R.id.holder_id, position);
    }

    class NumViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener{
        TextView tv;

        public NumViewHolder(final View itemView) {
            super(itemView);
            tv = (TextView) itemView;
            tv.setOnClickListener(this);
            tv.setOnLongClickListener(this);
        }

        @Override
        public void onClick(View v) {
            if(mOnItemClickListener != null){
                mOnItemClickListener.onItemClick(v, (Integer) tv.getTag(R.id.holder_id));
            }
        }

        @Override
        public boolean onLongClick(View v) {
            if(mOnItemClickListener != null){
                return mOnItemClickListener.onItemLongClick(v, (Integer) tv.getTag(R.id.holder_id));
            }
            return false;
        }
    }

这样,就可以了。如果说我们想传递的是其他类型,比如 String 或是 对象等,怎么办?同样道理,先修改回调接口。   

 public interface OnItemClickListener<T> {
        void onItemClick(View view, T value);
        void onItemLongClick(View view, T value);

    }

    OnItemClickListener simpleClike = new OnItemClickListener(){

        @Override
        public void onItemClick(View view, Object value) {
            Toast.makeText(getApplicationContext(), " onItemClick "  + value,Toast.LENGTH_LONG).show();
        }

        @Override
        public void onItemLongClick(View view, Object value) {
            Toast.makeText(getApplicationContext(), " onItemLongClick "  + value,Toast.LENGTH_LONG).show();

        }

    };

NumAdapter:   

@Override
    public void onBindViewHolder(final NumViewHolder holder, final int position) {
        holder.tv.setText(" this is  " +  position );
        holder.tv.setTag(R.id.holder_id, holder.tv.getText());
    }

    class NumViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener{
        TextView tv;

        public NumViewHolder(final View itemView) {
            super(itemView);
            tv = (TextView) itemView;
            tv.setOnClickListener(this);
            tv.setOnLongClickListener(this);
        }

        @Override
        public void onClick(View v) {
            if(mOnItemClickListener != null){
                mOnItemClickListener.onItemClick(v,  tv.getTag(R.id.holder_id));
            }
        }

        @Override
        public boolean onLongClick(View v) {
            if(mOnItemClickListener != null){
                return mOnItemClickListener.onItemLongClick(v,  tv.getTag(R.id.holder_id));
            }
            return false;
        }
    }

我们定义回调时用到了泛型,大部分情况,我们在使用 RecyclerView 的时候已经知道集合中的对象的类型,所以可以再稍作修改
   

OnItemClickListener simpleClike = new OnItemClickListener<String>(){

        @Override
        public void onItemClick(View view, String value) {
            Toast.makeText(getApplicationContext(), " onItemClick "  + value,Toast.LENGTH_LONG).show();
        }

        @Override
        public void onItemLongClick(View view, String value) {
            Toast.makeText(getApplicationContext(), " onItemLongClick "  + value,Toast.LENGTH_LONG).show();

        }

    });

适配器里面不用修改,这样就可以了。适配器里返回的对象,我们只需要考虑回调中实现自己的逻辑即可,即 Toast 的部分。

扫描二维码关注公众号,回复: 9780313 查看本文章
发布了176 篇原创文章 · 获赞 11 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/Deaht_Huimie/article/details/100745772