4

I have a Fragment that implemets RecyclerView.OnItemTouchListener. How do I pass click and long-click motion events only from the RecyclerView to the GestureDetectorCompat. That is I mean I only want to handle clicks and long-clicks, rest of the events should be handled by the RecyclerView as it would happen normally. How can I set this up?

public class MyFragment extends Fragment implements RecyclerView.OnItemTouchListener,
        GestureDetector.OnGestureListener {

    protected RecyclerView recyclerView;
    protected RecyclerView.Adapter adapter;
    protected LinearLayoutManager layoutManager;
    private GestureDetectorCompat detector;

    public MyFragment() {
        // Required empty public constructor
    }


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.myfrag, container, false);

        recyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview);

        layoutManager = new LinearLayoutManager(getActivity());
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.addOnItemTouchListener(this);

        adapter = new MyAdapter(myData));
        recyclerView.setAdapter(adapter);
        return rootView;
    }


    @Override
    public boolean onDown(MotionEvent e) {
        return false;
    }

    @Override
    public void onShowPress(MotionEvent e) {

    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        return false;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        return false;
    }

    @Override
    public void onLongPress(MotionEvent e) {

    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        return false;
    }

    @Override
    public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent event) {
        return false;
    }

    @Override
    public void onTouchEvent(RecyclerView recyclerView, MotionEvent event) {

    }
}
Binoy Babu
  • 16,699
  • 17
  • 91
  • 134

2 Answers2

28

You have to initialize GestureDetectorCompat in onCreateView() method:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.myfrag, container, false);

    detector = new GestureDetectorCompat(getActivity(), new RecyclerViewOnGestureListener());

    recyclerView = (RecyclerView) rootView.findViewById(R.id.recyclerview);

    layoutManager = new LinearLayoutManager(getActivity());
    recyclerView.setLayoutManager(layoutManager);
    recyclerView.addOnItemTouchListener(this);

    adapter = new MyAdapter(myData));
    recyclerView.setAdapter(adapter);
    return rootView;
}

RecyclerViewOnGestureListener is your own inner class extending SimpleOnGestureListener (that provides empty implementation of OnGestureListener methods)

private class RecyclerViewOnGestureListener extends SimpleOnGestureListener {

    @Override
    public boolean onSingleTapConfirmed(MotionEvent e) {
        View view = recyclerView.findChildViewUnder(e.getX(), e.getY());
        int position = recyclerView.getChildPosition(view);

        // handle single tap

        return super.onSingleTapConfirmed(e);
    }

    public void onLongPress(MotionEvent e) {
        View view = recyclerView.findChildViewUnder(e.getX(), e.getY());
        int position = recyclerView.getChildPosition(view);

        // handle long press

        super.onLongPress(e);
    }
}

Now look at line (from onCreateView() method):

recyclerView.addOnItemTouchListener(this);

In our case 'this' is OnItemTouchListener containing two methods we need to implement:

@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
    detector.onTouchEvent(e);
    return false;
}

@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
}

Here is an explanation what these methods mean: https://developer.android.com/reference/android/support/v7/widget/RecyclerView.OnItemTouchListener.html

It's all you need to handle single tap and long press events from RecyclerView.

Shashanth
  • 4,995
  • 7
  • 41
  • 51
wpiwonski
  • 1,284
  • 12
  • 6
  • Thanks for the help! How do you know whether e.getX() or e.getRawX() should be used? – Nelson Osacky Oct 31 '14 at 20:16
  • @BinoyBabu, I've answered the question on my own :) basing on some example. – wpiwonski Nov 03 '14 at 09:52
  • @NelsonOsacky getX() returns x coordinate relative to the view, getRawX() returns absolute coordinate relative to device screen. – wpiwonski Nov 03 '14 at 09:54
  • 1
    @wpiwonski Your solution works great. But I have a problem with my background selector. How can I add visual feedback? I tried to `setPressed` in `onShowPress` but fast simple click does not show anything. `setPressed` in `onDown`always shows visual feedback even i am scrolling. Could you pls tell me how to solve the problem. thanks – user579619 Dec 23 '14 at 21:52
  • recyclerView.findChildViewUnder(e.getX(), e.getY()) gives me wrong children after scrolling – m02ph3u5 Jun 12 '15 at 13:25
  • in fragment am doing what you suggest like that in ontouch and onlong press not called even log messages also not in logcat please help me – Harsha Aug 26 '16 at 08:53
1

I might be late but for visual feedback add this to your list_item layout

android:background="@drawable/tranparent_selector"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"

Also, onSingleTapUp can be used instead of onSingleTapConfirmed as tapping is usually fast. So if you tap fast to other items you wont get it working. For fast tapping I prefer onSingleTapUp

Keshav
  • 577
  • 3
  • 10