I have a RecyclerView with 2 items that don\'t fill the whole screen. How can I detect that the user clicked on the empty part of the RecyclerView (meaning clicked directly on t
You can subclass RecyclerView
and override the dispatchTouchEvent()
method to accomplish this. Using the findChildViewUnder()
method, we can determine if a touch event occurs outside of the child Views, and use an interface
to notify a listener if it is. In the following example, the OnNoChildClickListener
interface
provides that functionality.
public class TouchyRecyclerView extends RecyclerView
{
// Depending on how you're creating this View,
// you might need to specify additional constructors.
public TouchyRecyclerView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
private OnNoChildClickListener listener;
public interface OnNoChildClickListener
{
public void onNoChildClick();
}
public void setOnNoChildClickListener(OnNoChildClickListener listener)
{
this.listener = listener;
}
@Override
public boolean dispatchTouchEvent(MotionEvent event)
{
// The findChildViewUnder() method returns null if the touch event
// occurs outside of a child View.
// Change the MotionEvent action as needed. Here we use ACTION_DOWN
// as a simple, naive indication of a click.
if (event.getAction() == MotionEvent.ACTION_DOWN
&& findChildViewUnder(event.getX(), event.getY()) == null)
{
if (listener != null)
{
listener.onNoChildClick();
}
}
return super.dispatchTouchEvent(event);
}
}
NB: This is adapted for RecyclerView
from my answer here concerning GridView
.
You just need to set a TouchListener
on the RecyclerView
like shown above :
categoryTable.setAdapter(new CatgoriesAdapter(categories.getWrappedList()));
categoryTable.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN
&& categoryTable.findChildViewUnder(event.getX(), event.getY()) == null)
{
// Touch outside items here, you do whatever you want
HideCategoryMenu();
}
return false;
}
});
@Angel Kjoseski answer would look like this in Kotlin:
yourRecyclerView.addOnItemTouchListener(object : RecyclerView.OnItemTouchListener {
override fun onInterceptTouchEvent(recyclerView: RecyclerView, motionEvent: MotionEvent): Boolean {
return when {
motionEvent.action != MotionEvent.ACTION_UP || recyclerView.findChildViewUnder(motionEvent.x, motionEvent.y) != null -> false
else -> {
// do something here
true
}
}
}
override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {}
override fun onTouchEvent(recyclerView: RecyclerView, motionEvent: MotionEvent) {}
})
As mentioned in the comment
mRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
@Override
public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent) {
if (motionEvent.getAction() != MotionEvent.ACTION_UP) {
return false;
}
View child = recyclerView.findChildViewUnder(motionEvent.getX(), motionEvent.getY());
if (child != null) {
// tapped on child
return false;
} else {
// Tap occured outside all child-views.
// do something
return true;
}
}
@Override
public void onTouchEvent(RecyclerView recyclerView, MotionEvent motionEvent) {
}
});
@driss-bounouar's answer is almost right although this will prevent the user from scrolling the recycler view as any down event will cause your action to happen. With a slight modification where we record the down event and then check on the up event if the coordinates have not changed much, then fire the event.
private MotionEvent lastRecyclerViewDownTouchEvent;
myRecyclerView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN && myRecyclerView.findChildViewUnder(event.getX(), event.getY()) == null) {
lastRecyclerViewDownTouchEvent = event;
} else if (event.getAction() == MotionEvent.ACTION_UP && myRecyclerView.findChildViewUnder(event.getX(), event.getY()) == null
&& lastRecyclerViewDownTouchEvent != null) {
// Check to see if it was a tap or a swipe
float xDelta = Math.abs(lastRecyclerViewDownTouchEvent.getX() - event.getX());
float yDelta = Math.abs(lastRecyclerViewDownTouchEvent.getY() - event.getY());
if (xDelta < 30 && yDelta < 30) {
// Do action
}
lastRecyclerViewDownTouchEvent = null;
}
return false;
}
});
shortcutDeviceRecly.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_UP){
roomClickEvent(true, v);
}
return false;
}
});