Passing MotionEvents from RecyclerView.OnItemTouchListener to GestureDetectorCompat
Asked Answered
L

2

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) {

    }
}
Lavella answered 23/10, 2014 at 8:23 Comment(0)
D
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.

Diarmuid answered 23/10, 2014 at 13:55 Comment(6)
Thanks for the help! How do you know whether e.getX() or e.getRawX() should be used?Falsetto
@BinoyBabu, I've answered the question on my own :) basing on some example.Diarmuid
@NelsonOsacky getX() returns x coordinate relative to the view, getRawX() returns absolute coordinate relative to device screen.Diarmuid
@Diarmuid 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 onDownalways shows visual feedback even i am scrolling. Could you pls tell me how to solve the problem. thanksHeptachord
recyclerView.findChildViewUnder(e.getX(), e.getY()) gives me wrong children after scrollingIgnacioignacius
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 meOatis
U
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

Ule answered 20/2, 2015 at 13:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.