自从开始使用 RecyclerView 代替 ListView ,会发现有很多地方需要学习.前一段时间的学习记录有:
RecyclerView的滚动事件研究 - DevWiki
RecyclerView的ViewHolder和Adapter的封装优化 - DevWiki
RecyclerView问题记录 - DevWiki
今天来学习一下如何实现 RecyclerView 的Item的点击事件.实现Item的点击事件有三种方式:
通过 RecyclerView 已有的方法 addOnItemTouchListener() 实现
在创建 ItemView 时添加点击监听
当 ItemView attach RecyclerView 时实现
- 通过 RecyclerView 的 addOnItemTouchListener() 实现
1.1 查看源码
查看 RecyclerView 源码可以看到, RecyclerView 预留了一个Item的触摸事件方法:
/**
- Add an {@link OnItemTouchListener} to intercept touch events before they are dispatched
- to child views or this view's standard scrolling behavior.
- <p>Client code may use listeners to implement item manipulation behavior. Once a listener
- returns true from
- {@link OnItemTouchListener#onInterceptTouchEvent(RecyclerView, MotionEvent)} its
- {@link OnItemTouchListener#onTouchEvent(RecyclerView, MotionEvent)} method will be called
- for each incoming MotionEvent until the end of the gesture.</p>
- @param listener Listener to add
- @see SimpleOnItemTouchListener
*/
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
/**
- An implementation of {@link RecyclerView.OnItemTouchListener} that has empty method bodies and
- default return values.
<
p>
You may prefer to extend this class if you don't need to override all methods. Another
benefit of using this class is future compatibility. As the interface may change, we'll
always provide a default implementation on this class so that your code won't break when
-
you update to a new version of the support library.
*/
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 来解析用户的操作.
1.2 实现点击事件监听
写一个 ItemClickListener 类继承 SimpleOnItemTouchListener ,构造时传入 RecyclerView 对象和Item点击回调,并覆写父类的 boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) 方法,具体代码如下:
/**
- 点击事件
- Created by DevWiki on 2016/7/16.
*/
public class ItemClickListener extends RecyclerView.SimpleOnItemTouchListener {
private OnItemClickListener clickListener;
private GestureDetectorCompat gestureDetector;
public interface OnItemClickListener {
void onItemClick(View view, int position);
void onItemLongClick(View view, int position);
}
public ItemClickListener(final RecyclerView recyclerView,
OnItemClickListener listener) {
this.clickListener = listener;
gestureDetector = new GestureDetectorCompat(recyclerView.getContext(),
new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
View childView = recyclerView.findChildViewUnder(e.getX(), e.getY());
if (childView != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
clickListener.onItemClick(childView, recyclerView.getChildAdapterPosition(childView));
}
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) {
gestureDetector.onTouchEvent(e);
return false;
}
}
在 GestureDetectorCompat 的手势回调中我们覆写:
boolean onSingleTapUp(MotionEvent e)
void onLongPress(MotionEvent e)
1.3 使用事件监听
在 RecyclerView 的对象中添加 addOnItemTouchListener() 方法,然后在回调中处理你需要的事件:
recyclerView.addOnItemTouchListener(new SingleItemClickListener(recyclerView,
new SingleItemClickListener.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
DevLog.i("touch click name:" + position);
Toast.makeText(SingleActivity.this, "touch click:" + position, Toast.LENGTH_SHORT).show();
}
@Override
public void onItemLongClick(View view, int position) {
DevLog.i("touch long click:" + position);
Toast.makeText(SingleActivity.this, "touch long click:" + position, Toast.LENGTH_SHORT).show();
}
}));
- 在创建 ItemView 时添加点击监听
这种方法和 ListView 一样,在Adapter里面创建 View 时添加点击事件.比如:
@Override
public void bindCustomViewHolder(SingleHolder holder, final int position) {
Person person = getItem(position);
holder.nameView.setText(person.getName());
holder.ageView.setText(String.valueOf(person.getAge()));
if (clickListener != null) {
holder.nameView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
clickListener.onNameClick(position);
}
});
holder.ageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
clickListener.onAgeClick(position);
}
});
}
}
然后在Adapter对象上添加监听回调:
singleAdapter.setClickListener(new SingleAdapter.OnSingleItemClickListener() {
@Override
public void onNameClick(int position) {
DevLog.i("adapter click name:" + position);
Toast.makeText(SingleActivity.this, "adapter click name:" + position, Toast.LENGTH_SHORT).show();
}
@Override
public void onAgeClick(int position) {
DevLog.i("adapter click age:" + position);
Toast.makeText(SingleActivity.this, "adapter click name:" + position, Toast.LENGTH_SHORT).show();
}
});
- 当 ItemView attach RecyclerView 时实现
该实现方法是在阅读国外的一篇博客时发现的,原文链接如下: Getting your clicks on RecyclerView
实现的代码如下:
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);
}
}
上面的代码中给 RecyclerView 设置了 OnChildAttachStateChangeListener 事件监听,当子 View attach RecyclerView 时设置事件监听.
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) {}
};
- 三种方式对比
以上三种方式分别是:
通过 RecyclerView 已有的方法 addOnItemTouchListener() 实现
在创建 ItemView 时添加点击监听
当 ItemView attach RecyclerView 时实现
从以上三种方式的实现过程可知:
三种均可实现 ItemView 的点击事件和长按事件的监听.
第一种方式可以很方便获取用户点击的坐标.
第二种和第三种方式可以很方便对 ItemView 中的子 View 进行监听.
第一种方式和第三种方式可以写在单独的类中,相对于第二种写在 Adapter 的方式可使代码更独立整洁
综上所述:
如果你只想监听 ItemView 的点击事件或长按事件,三种方式均可.
如果你想监听 ItemView 中每个子 View 的点击事件,采用第二种或者第三种比较方面.