首页 > 代码库 > 从头开始学 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;
}
});
三种方式对比
以上三种方式分别是:
- 通过RecyclerView已有的方法addOnItemTouchListener()实现
- 对holder.ItemView添加点击监听
- 当ItemView attach RecyclerView时实现
从以上三种方式的实现过程可知:
- 三种均可实现ItemView的点击事件和长按事件的监听.
- 第一种方式可以很方便获取用户点击的坐标. 但不支持TV上的click事件
- 第二种和第三种方式可以很方便对ItemView中的子View进行监听.
- 第一、三种方式可以写在单独的类中,相对于第二种可使代码更独立整洁
综上所述:
如果你想监听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》
从头开始学 RecyclerView(二) 添加item点击事件