RecyclerViewItemTouchListener
A helper class for handling RecyclerView's click events
At the time of writing this article, there is no support for RecyclerView's long-item-click event.
In a need to detect and capture such an event, I decided to create RecyclerViewItemTouchListener, and recently extended it to support single and double click events.
RecyclerViewItemTouchListener wraps a GestureDetector and uses it to detect three different gestures:
OnGestureListener#onLongPress(MotionEvent)
OnGestureListener#onDoubleTap(MotionEvent)
OnGestureListener#onSingleTapConfirmed(MotionEvent)
These three events are exported via OnItemClickEventListener, which a client should implement:
public interface OnItemClickEventListener {
/**
* Called when an item is long clicked.
* @param longClickedView The long clicked view.
* @param adapterPosition The position of the long clicked view
* in the adapter.
*/
void onItemLongClick(View longClickedView, int adapterPosition);
/**
* Called when an item is clicked.
* @param clickedView The clicked view.
* @param adapterPosition The position of the clicked view
* in the adapter.
*/
void onItemClick(View clickedView, int adapterPosition);
/**
* Called when an item is double clicked.
* @param doubleClickedView The clicked view.
* @param adapterPosition The position of the clicked view
* in the adapter.
*/
void onItemDoubleClick(View doubleClickedView,
int adapterPosition);
}
A private inner class, GestureDelegator, overrides GestureDetector.SimpleOnGestureListener's three different click events, and invokes the relevant method in OnItemClickEventListener, if one is indeed provided.
private class GestureDelegator extends SimpleOnGestureListener {
@Override
public void onLongPress(MotionEvent e) {
if (mOnItemClickListener != null) {
if (mChildView != null) {
mOnItemClickListener.onItemLongClick(mChildView,
mChildViewAdapterPosition);
}
}
}
@Override
public boolean onDoubleTap(MotionEvent e) {
if (mOnItemClickListener != null) {
if (mChildView != null) {
mOnItemClickListener.onItemDoubleClick(mChildView,
mChildViewAdapterPosition);
return true;
}
}
return false;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
if (mOnItemClickListener != null) {
if (mChildView != null) {
mOnItemClickListener.onItemClick(mChildView,
mChildViewAdapterPosition);
return true;
}
}
return false;
}
}
Notice that unlike OnGestureListener#onSingleTapUp(MotionEvent)
,
OnGestureListener#onSingleTapConfirmed(MotionEvent)
is called only after the detector is confident that the user's first tap is not followed by a second tap - leading to a double-tap gesture.
As you can see, GestureDelegator listens to three different click events and delegates them to the matching call in OnItemClickEventListener.
onLongPress(MotionEvent)
delegates toonItemLongClick(View, int)
onDoubleTap(MotionEvent)
delegates toonItemDoubleClick(View, int)
onSingleTapConfirmed(MotionEvent)
delegates toonItemClick(View, int)
To tie OnItemClickEventListener and GestureDelegator together, we let our RecyclerViewItemTouchListener extend RecyclerView.SimpleOnItemTouchListener, and override onInterceptTouchEvent(RecyclerView, MotionEvent)
, to silently observe and take over touch events sent to the RecyclerView before they are handled by either the RecyclerView itself or its child views :
@Override
public boolean onInterceptTouchEvent(RecyclerView recyclerView,
MotionEvent motionEvent) {
float x = motionEvent.getX();
float y = motionEvent.getY();
mChildView = recyclerView.findChildViewUnder(x, y);
if (mChildView != null) {
int pos = recyclerView.getChildAdapterPosition(mChildView);
mChildViewAdapterPosition = pos;
mGestureDetector.onTouchEvent(motionEvent);
}
return false;
}
We find the clicked child under MotionEvent's provided coordinates, grab the corresponding adapter position, and pass motionEvent to our GestureDetector member.
Notice that we return false; from onInterceptTouchEvent(RecyclerView, MotionEvent)
to continue with the current behavior and observe future events in the gesture.
You can integrate RecyclerViewItemTouchListener to your RecyclerView by adding it as a OnItemTouchListener like so:
mRecyclerView.addOnItemTouchListener(new RecyclerViewItemTouchListener(this,this));
Where the first parameter is a Context and the second is your OnItemClickEventListener implementation.
You're welcome to use to complete source code:
Thank you for stopping by and reading this article!
If you find it helpful or just want to say something - please leave a comment.