How to get clicks on RecyclerView (NOT the children)
Is there any way to set an
onClickListener
on aRecyclerView
?
No. That is, you can set an OnClickListener
, but RecyclerView
will never call it. RecyclerView
intercepts all touch events, but never calls performClick()
, which is how View
invokes its listener.
You can, however, simulate an OnClickListener
with an OnTouchListener
and a GestureDetector
. For the GestureDetector
's listener, we can use a SimpleOnGestureListener
, implementing just the onSingleTapUp()
method.
class ClickListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onSingleTapUp(MotionEvent e) {
Toast.makeText(HelloActivity.this, "Clicked", 0).show();
return true;
}
};
Then we just need to feed the GestureDetector
the MotionEvent
s from an OnTouchListener
, and check the return to decide whether to consume the event, so as to not interfere with scrolling, dragging, etc.
final GestureDetector detector = new GestureDetector(HelloActivity.this, new ClickListener());
mRecyclerView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(detector.onTouchEvent(event)) {
return true;
}
return false;
}
}
);
Please note that the above solution works with pretty much only a "simple" RecyclerView
, like the one described and given in the question. If you're using more involved item handling, like drag and drop or swipe, we'll need to handle our gesture detection a little further up the touch event chain.
To do this, we can subclass RecyclerView
and perform the detection in the dispatchTouchEvent()
method. If a single tap is detected, we simply call performClick()
, which will fire the RecyclerView
's OnClickListener
.
public class ClickableRecyclerView extends RecyclerView {
private final GestureDetectorCompat detector;
public ClickableRecyclerView(Context context) {
this(context, null);
}
public ClickableRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
detector = new GestureDetectorCompat(context, new ClickListener());
}
private class ClickListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onSingleTapUp(MotionEvent e) {
performClick();
return true;
}
};
@Override
public boolean dispatchTouchEvent(MotionEvent e) {
detector.onTouchEvent(e);
return super.dispatchTouchEvent(e);
}
}
Just use this subclass in place of your regular RecyclerView
, and set an OnClickListener
on it as you normally would. Additional constructors may be necessary, depending on how you're instantiating this.
This is how I have done in Kotlin style
fun RecyclerView.enableClickListener(){
val gesture = object : GestureDetector.SimpleOnGestureListener(){
override fun onSingleTapConfirmed(e: MotionEvent?): Boolean {
[email protected]()
return super.onSingleTapConfirmed(e)
}
}
val detector = GestureDetector(this.context, gesture)
this.setOnTouchListener { v, event -> detector.onTouchEvent(event) }
}
And this is how to use it
yourRecyclerView.apply {
enableClickListener()
setOnClickListener {
// Do what you want ...
}
}
Enjoy :)