Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect click on RecyclerView outside of items

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 the RecyclerView and not one of its items)?

like image 355
Omar Avatar asked Dec 30 '14 10:12

Omar


4 Answers

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) {
  }
});
like image 175
Angel Kjoseski Avatar answered Nov 16 '22 05:11

Angel Kjoseski


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.

like image 24
Mike M. Avatar answered Nov 16 '22 04:11

Mike M.


@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;
                    }
                });
like image 4
odiggity Avatar answered Nov 16 '22 05:11

odiggity


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;
        }
    });
like image 1
Driss Bounouar Avatar answered Nov 16 '22 05:11

Driss Bounouar