Android小记:RecyclerView添加Item点击事件
自从Android5.0,Google发布RecyclerView
以来,逐渐代替了传统的ListView
,因为RecyclerView更加强大,更加灵活,但是,由于官方未实现RecyclerView的Item点击事件,所以需要自行实现。下面就是我在使用RecyclerView的过程中,总结的添加点击事件的几种方法。
方式一:通过RecyclerView的addOnItemTouchListener()方法
使用RecyclerView提供的addOnItemTouchListener()
方法,为Item添加触摸回调,在触摸事件处理中,实现Item点击;
通过源码得知,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(@NonNull OnItemTouchListener listener) {
mOnItemTouchListeners.add(listener);
}
此方法需要传入一个OnItemTouchListener
,OnItemTouchListener代码如下:
public interface OnItemTouchListener {
boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
}
同时,RecyclerView还提供了一个子类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(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
return false;
}
@Override
public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
}
}
我们可以通过此类实现Item点击功能;
写一个类ItemTouchHelper
类继承自SimpleOnItemTouchListener
,重写onInterceptTouchEvent
方法,在onInterceptTouchEvent中进行事件处理;
@Override
public boolean onInterceptTouchEvent(@NonNull RecyclerView recyclerView,
@NonNull MotionEvent motionEvent) {
return mGestureDetectorCompat.onTouchEvent(motionEvent);
}
这里的mGestureDetectorCompat
,是一个GestureDetectorCompat
辅助工具,继续观察源码,GestureDetectorCompat构造器中需要我们传入Context
和OnGestureListener
,下面是OnGestureListener源码:
public interface OnGestureListener {
// 用户按下屏幕就会触发
boolean onDown(MotionEvent e);
// 如果是按下的时间超过瞬间,而且在按下的时候没有松开或者是拖动的,那么onShowPress就会执行
void onShowPress(MotionEvent e);
// 一次单独的轻击抬起操作,也就是轻击一下屏幕,就是普通点击事件
boolean onSingleTapUp(MotionEvent e);
// 在屏幕上拖动事件
boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
// 长按触摸屏,超过一定时长,就会触发这个事件
void onLongPress(MotionEvent e);
// 滑屏,用户按下触摸屏、快速移动后松开
boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
}
通过源码发现,onSingleTapUp
方法会在触摸抬起时触发;同样,官方也为我们提供了一个OnGestureListener
的简单实现类SimpleOnGestureListener
,这个类实现了所有方法,但都是空实现,我们只需要重写onSingleTapUp
方法,并处理事件即可:
GestureDetector.OnGestureListener gl = new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
if (child != null && mListener != null) {
mListener.onItemClick(recyclerView.getChildViewHolder(child));
return true;
}
return false;
}
}
mGestureDetectorCompat = new GestureDetectorCompat(recyclerView.getContext(), gl);
RecyclerView提供了findChildViewUnder
方法,我们可以通过事件点击的坐标获取点击的Item,再通过RecyclerView的getChildViewHolder
方法获取Item的ViewHolder,进而执行我们想要的操作,至此,我们可以将点击事件回调出去。方法一:通过View.setOnClickListener()方法
方式二:将点击事件添加到Item的根布局上,通过Adapter回调的方式添加事件监听:
public class MyAdapter extends RecyclerView.Adapter<ViewHolder> {
...
@Override
public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder viewHolder, int position) {
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (listener != null) {
listener.onItemClick(viewHolder.itemView, viewHolder.getAdapterPosition());
}
}
});
}
private OnItemClickListener listener;
public void setOnItemClickListener(OnItemClickListener listener) {
this.listener = listener;
}
public interface OnItemClickListener {
void onItemClick(View itemView, int position);
}
}
方式三:在Item添加到RecyclerView时添加点击事件
RecyclerView还提供了一个addOnChildAttachStateChangeListener
方法,可以为RecyclerView提供一个OnChildAttachStateChangeListener
,监听一个View添加到RecyclerView和离开RecyclerView,我们可以在这个时候执行一些操作来实现Item点击:
OnChildAttachStateChangeListener listener = new RecyclerView.OnChildAttachStateChangeListener() {
@Override
public void onChildViewAttachedToWindow(@NonNull View view) {
final RecyclerView.ViewHolder viewHolder = mRecyclerView.getChildViewHolder(view);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mListener != null){
mListener.onItemClick(viewHolder);
}
}
});
}
@Override
public void onChildViewDetachedFromWindow(@NonNull View view) {}
}
mRecyclerView.addOnChildAttachStateChangeListener(listener);
小结
以上三种实现方式是在开发中遇到,并在网上获取的实现方法,三种方式各有优劣,在实际开发中,可以灵活选择。
第一种直接从触摸事件入手,可举一反三,通过不同的逻辑进行其他手势的监听和处理;第二种和第三种大同小异,均是通过View
类的setOnClickListener
方法入手实现,同样的也可以通过其他方式实现不同效果。
本文仅代表个人看法,如果有误欢迎指正,如果您有更好的实现,欢迎指教,谢谢!