首页 > 代码库 > 从头开始学 RecyclerView(二) 添加item点击事件

从头开始学 RecyclerView(二) 添加item点击事件

不管了,先来张图
技术分享

偶吐了个槽


item点击事件必须手动添加,默认并没有一个显式的API接口可供调用。
为了节约学习时间,网上找了篇很不错的文章。这里基本就复制了。

添加点击事件


RecyclerView#addOnItemTouchListener

  • 分析

查看RecyclerView源码可以看到,RecyclerView预留了一个Item的触摸事件方法:

public void addOnItemTouchListener(OnItemTouchListener listener) {
    mOnItemTouchListeners.add(listener);
}

通过注释我们可知,此方法是在滚动事件之前调用.需要传入一个OnItemTouchListener对象.OnItemTouchListener的代码如下:

public static interface OnItemTouchListener { 

    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e);

    public void onTouchEvent(RecyclerView rv, MotionEvent e);

    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
}

此接口还提供了一个实现类,且官方推荐使用该实现类SimpleOnItemTouchListener,它就是一个空实现:

public static class SimpleOnItemTouchListener implements RecyclerView.OnItemTouchListener {
    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        return false;
    }

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

    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
    }

在触摸接口中,当触摸时会回调一个MotionEvent对象,通过使用GestureDetectorCompat来解析用户的操作.

  • 代码实现
    RecyclerItemClickListener:
/**
 * from http://blog.devwiki.net/index.php/2016/07/17/Recycler-View-Adapter-ViewHolder-optimized.html
 * 点击事件
 * Created by DevWiki on 2016/7/16.
 */

public class RecyclerItemClickListener extends RecyclerView.SimpleOnItemTouchListener {
//public class RecyclerItemClickListener extends RecyclerView.OnItemTouchListener {

    private OnItemClickListener clickListener;
//    private GestureDetector gestureDetector;
    private GestureDetectorCompat gestureDetector; //v4 兼容包中

    public interface OnItemClickListener {
        /**
         * 点击时回调
         *
         * @param view 点击的View
         * @param position 点击的位置
         */
        void onItemClick(View view, int position);

        /**
         * 长点击时回调
         *
         * @param view 点击的View
         * @param position 点击的位置
         */
        void onItemLongClick(View view, int position);
    }

    public RecyclerItemClickListener(final RecyclerView recyclerView, OnItemClickListener listener) {
        this.clickListener = listener;
        gestureDetector = new GestureDetectorCompat(recyclerView.getContext(),
                new GestureDetector.SimpleOnGestureListener() {
                    @Override
                    public boolean onSingleTapUp(MotionEvent e) {
                        return true;
                    }

                    @Override
                    public void onLongPress(MotionEvent e) {
                        View childView = recyclerView.findChildViewUnder(e.getX(), e.getY());
                        if (childView != null && clickListener != null) {
                            clickListener.onItemLongClick(childView,
                                    recyclerView.getChildAdapterPosition(childView));
                        }
                    }
                });
    }

    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        View childView = rv.findChildViewUnder(e.getX(), e.getY());
        if (childView != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
            clickListener.onItemClick(childView, rv.getChildAdapterPosition(childView));
            return true;
        }
        return false;
    }
//
//    @Override
//    public void onTouchEvent(RecyclerView rv, MotionEvent e) {
//
//    }
//
//    @Override
//    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
//
//    }
}

RV实现:

mRecyclerView.addOnItemTouchListener(new RecyclerItemClickListener(mRecyclerView,
        new RecyclerItemClickListener.OnItemClickListener() {
    @Override
    public void onItemClick(View view, int position) {
        System.out.println("onItemClick " + adapter.getItem(position));
    }

    @Override
    public void onItemLongClick(View view, int position) {
        System.out.println("onItemLongClick " + position);
    }
}));

 

对ItemView添加点击监听

  • 在adapter的bindCustomViewHolder()中,对holder.itemView添加监听
private static class ClickAdapter extends BaseAdapter<String, SimplifyVH> {

    private RecyclerItemClickListener.OnItemClickListener mListener;

    public ClickAdapter(Context context) {
        super(context);
    }

    public ClickAdapter(Context context, List<String> list) {
        super(context, list);
    }

    public void setListener(RecyclerItemClickListener.OnItemClickListener listener) {
        mListener = listener;
    }

    @Override
    public int getCustomViewType(int position) {
        return 0;
    }

    @Override
    public SimplifyVH createCustomViewHolder(ViewGroup parent, int viewType) {
        return new SimplifyVH(
                LayoutInflater.from(
                        parent.getContext()).inflate(R.layout.basic_simple, null, false));
    }

    @Override
    public void bindCustomViewHolder(SimplifyVH holder, final int position) {

        holder.itemView.setFocusable(true);//加了这句,电视上就能滚动了

        TextView tvTitle = (TextView) holder.itemView.findViewById(R.id.tv_title);
        tvTitle.setText(getItem(position));

        View vImg = holder.itemView.findViewById(R.id.v_img);
        vImg.setBackgroundColor(getColor());

        final SimplifyVH vh = (SimplifyVH) holder;
        vh.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mListener != null) {
                    mListener.onItemClick(v, position);
                }
            }
        });
        vh.itemView.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                if (mListener != null) {
                    mListener.onItemLongClick(v, position);
                }
                return true;
            }
        });
    }
}

RV实现:

final ClickAdapter adapter = new ClickAdapter(this, mList);
adapter.setListener(new RecyclerItemClickListener.OnItemClickListener() {
     @Override
     public void onItemClick(View view, int position) {
         clickAnim(view);
         System.out.println("onItemClick " + adapter.getItem(position));
     }

     @Override
     public void onItemLongClick(View view, int position) {
         System.out.println("onItemLongClick " + position);
     }
 });

mRecyclerView.setAdapter(adapter);

这种方式,在adaper中,定义一个listener;bind-viewHolder时,对itemView添加点击监听。

 

  • 在adapter的createCustomViewHolder时,传入listener
    在ViewHolder中关联一个listener
private static class SimplifyVH extends BaseHolder {

    RecyclerItemClickListener.OnItemClickListener listener;

    public SimplifyVH(ViewGroup parent, @LayoutRes int resId) {
        super(parent, resId);
    }

    public SimplifyVH(View view, final RecyclerItemClickListener.OnItemClickListener listener) {
        super(view);
        this.listener = listener;
        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (listener != null) {
                    listener.onItemClick(v, getAdapterPosition());
                }
            }
        });
        view.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                if (listener != null) {
                    listener.onItemLongClick(v, getAdapterPosition());
                }
                return true;
            }
        });
    }

}

相应的adaper实现:

private static class ClickAdapter3 extends BaseAdapter<String, SimplifyVHWithListener> {

    private RecyclerItemClickListener.OnItemClickListener mListener;

    public ClickAdapter3(Context context) {
        super(context);
    }

    public ClickAdapter3(Context context, List<String> list) {
        super(context, list);
    }

    public void setListener(RecyclerItemClickListener.OnItemClickListener listener) {
        mListener = listener;
    }

    @Override
    public int getCustomViewType(int position) {
        return 0;
    }

    @Override
    public SimplifyVHWithListener createCustomViewHolder(ViewGroup parent, int viewType) {
        return new SimplifyVHWithListener(
                LayoutInflater.from(
                        parent.getContext()).inflate(R.layout.basic_simple, null, false),
                new RecyclerItemClickListener.OnItemClickListener() {
                    @Override
                    public void onItemClick(View view, int position) {
                        clickAnim(view);
                        System.out.println("onItemClick " + getItem(position));
                    }

                    @Override
                    public void onItemLongClick(View view, int position) {
                        System.out.println("onItemLongClick " + position + "__" + getItem(position));
                    }
                });
    }

    @Override
    public void bindCustomViewHolder(SimplifyVHWithListener holder, final int position) {

        holder.itemView.setFocusable(true);//加了这句,电视上就能滚动了

        TextView tvTitle = (TextView) holder.itemView.findViewById(R.id.tv_title);
        tvTitle.setText(getItem(position));

        View vImg = holder.itemView.findViewById(R.id.v_img);
        vImg.setBackgroundColor(getColor());
    }
}

RV实现:

ClickAdapter3 adapter = new ClickAdapter3(this, mList);
mRecyclerView.setAdapter(adapter);

这种,跟前一个,只是写法上有区别,本质一样。都是对holder.itemview添加点击监听。

 

当ItemView attach RecyclerView时实现

主要基于RecyclerView.OnChildAttachStateChangeListener。

public class ItemClickSupport {
    private final RecyclerView mRecyclerView;
    private OnItemClickListener mOnItemClickListener;
    private OnItemLongClickListener mOnItemLongClickListener;

    private View.OnClickListener mOnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            if (mOnItemClickListener != null) {
                RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
                mOnItemClickListener.onItemClicked(mRecyclerView, holder.getAdapterPosition(), v);
            }
        }
    };

    private View.OnLongClickListener mOnLongClickListener = new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
            if (mOnItemLongClickListener != null) {
                RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v);
                return mOnItemLongClickListener.onItemLongClicked(mRecyclerView, holder.getAdapterPosition(), v);
            }
            return false;
        }
    };

    private RecyclerView.OnChildAttachStateChangeListener mAttachListener
            = new RecyclerView.OnChildAttachStateChangeListener() {
        @Override
        public void onChildViewAttachedToWindow(View view) {
            if (mOnItemClickListener != null) {
                view.setOnClickListener(mOnClickListener);
            }
            if (mOnItemLongClickListener != null) {
                view.setOnLongClickListener(mOnLongClickListener);
            }
        }

        @Override
        public void onChildViewDetachedFromWindow(View view) {}
    };

    private ItemClickSupport(RecyclerView recyclerView) {
        mRecyclerView = recyclerView;
        mRecyclerView.setTag(R.id.item_click_support, this);
        mRecyclerView.addOnChildAttachStateChangeListener(mAttachListener);
    }

    public static ItemClickSupport addTo(RecyclerView view) {
        ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
        if (support == null) {
            support = new ItemClickSupport(view);
        }
        return support;
    }

    public static ItemClickSupport removeFrom(RecyclerView view) {
        ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support);
        if (support != null) {
            support.detach(view);
        }
        return support;
    }

    public ItemClickSupport setOnItemClickListener(OnItemClickListener listener) {
        mOnItemClickListener = listener;
        return this;
    }

    public ItemClickSupport setOnItemLongClickListener(OnItemLongClickListener listener) {
        mOnItemLongClickListener = listener;
        return this;
    }

    private void detach(RecyclerView view) {
        view.removeOnChildAttachStateChangeListener(mAttachListener);
        view.setTag(R.id.item_click_support, null);
    }

    public interface OnItemClickListener {
        void onItemClicked(RecyclerView recyclerView, int position, View v);
    }

    public interface OnItemLongClickListener {
        boolean onItemLongClicked(RecyclerView recyclerView, int position, View v);
    }
}

RV实现:

final ClickAdapter1 adapter = new ClickAdapter1(this, mList);
mRecyclerView.setAdapter(adapter);
ItemClickSupport.addTo(mRecyclerView).setOnItemClickListener(new ItemClickSupport.OnItemClickListener() {
    @Override
    public void onItemClicked(RecyclerView recyclerView, int position, View v) {
        System.out.println("onItem " + position + "  " + adapter.getItem(position));
    }
}).setOnItemLongClickListener(new ItemClickSupport.OnItemLongClickListener() {
    @Override
    public boolean onItemLongClicked(RecyclerView recyclerView, int position, View v) {
        System.out.println("onItemLongClick " + position);
        return true;
    }
});

三种方式对比

以上三种方式分别是:

  1. 通过RecyclerView已有的方法addOnItemTouchListener()实现
  2. 对holder.ItemView添加点击监听
  3. 当ItemView attach RecyclerView时实现

从以上三种方式的实现过程可知:

  1. 三种均可实现ItemView的点击事件和长按事件的监听.
  2. 第一种方式可以很方便获取用户点击的坐标. 但不支持TV上的click事件
  3. 第二种和第三种方式可以很方便对ItemView中的子View进行监听.
  4. 第一、三种方式可以写在单独的类中,相对于第二种可使代码更独立整洁

综上所述:
如果你想监听ItemView的点击事件或长按事件,三种方式均可.
如果你只想监听ItemView中每个子View的点击事件,采用第二种或者第三种比较方便.
如果想支持TV上的click事件,只能采用第二种或第三种

参考


http://blog.devwiki.net/index.php/2016/07/24/three-ways-click-recyclerview-item.html 《三种方式实现RecyclerView的Item点击事件》

http://www.littlerobots.nl/blog/Handle-Android-RecyclerView-Clicks/ 《Getting your clicks on RecyclerView》

<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

    从头开始学 RecyclerView(二) 添加item点击事件