5

This seems to be a fairly common pattern in android. I'm trying to create something similar to how Facebook displays their adverts:

Facebook ads

You can see that they have an outer vertical recyclerview with an inner horizontal recyclerview as one of the items in the outer vertical recyclerview's adapter.

I followed this guide from Google on "Managing Touch Events in a ViewGroup" - http://developer.android.com/training/gestures/viewgroup.html as Recyclerview extends ViewGroup and the code there is similar to what I want to do.

I have customized it a little bit so that it detects movements in Y axis instead of movements in X axis and applied it to the outer vertical recyclerview.

/**
 * Created by Simon on 10/11/2015.
 *///This is the recyclerview that would allow all vertical scrolls
public class VerticallyScrollRecyclerView extends RecyclerView {


    public VerticallyScrollRecyclerView(Context context) {
        super(context);
    }

    public VerticallyScrollRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public VerticallyScrollRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }


    ViewConfiguration vc = ViewConfiguration.get(this.getContext());
    private int mTouchSlop = vc.getScaledTouchSlop();
    private boolean mIsScrolling;
    private float startY;

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = MotionEventCompat.getActionMasked(ev);
        // Always handle the case of the touch gesture being complete.
        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
            // Release the scroll.
            mIsScrolling = false;
            startY = ev.getY();
            return false; // Do not intercept touch event, let the child handle it
        }
        switch (action) {
            case MotionEvent.ACTION_MOVE: {
                if (mIsScrolling) {
                    // We're currently scrolling, so yes, intercept the
                    // touch event!
                    return true;
                }

                // If the user has dragged her finger horizontally more than
                // the touch slop, start the scroll

                // left as an exercise for the reader
                final float yDiff = calculateDistanceY(ev.getY());
                Log.e("yDiff ", ""+yDiff);
                // Touch slop should be calculated using ViewConfiguration
                // constants.
                if (yDiff > mTouchSlop) {
                    // Start scrolling!
                    Log.e("Scroll", "we are scrolling vertically");
                    mIsScrolling = true;
                    return true;
                }
                break;
            }
        }
        return false;
    }

    private float calculateDistanceY(float endY) {
        return startY - endY;
    }

}

My xml layout:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/main_recyclerview_holder">

    <com.example.simon.customshapes.VerticallyScrollRecyclerView
        android:id="@+id/main_recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
/>

</RelativeLayout>

When I try to drag the inner horizontal recyclerview (hoping that the touchevent would be intercepted by the outer vertical recyclerview), the outer recyclerview does not scroll vertically at all.

It also gives me an error saying:

Error processing scroll; pointer index for id -1 not found. Did any MotionEvents get skipped?

Does anyone know how to get this working right?

Simon
  • 19,658
  • 27
  • 149
  • 217
  • 1
    Why do you need to create your custom `VerticallyScrollRecyclerView`? Can't you just use an horizontal `LinearLayoutManager`? – rciovati Oct 21 '15 at 18:24
  • I'm using a horizontal LinearLayoutManager in the inner horizontal recyclerview. The outer recyclerview is using a vertical LinearLayoutManager. This question is based around the correct code for implementing touchevents and I need the VerticallyScrollRecyclerView because I need it to intercept all move touchevents so that it will scroll properly. If not, there are going to be touch conflicts between the inner and outer recycler view. – Simon Oct 21 '15 at 18:35
  • Were you able to find a solution. – Shubham Dec 24 '15 at 10:52
  • Try this solution here: http://stackoverflow.com/questions/33456216/android-layout-horizontal-recyclerview-inside-a-vertical-recyclerview-inside-a – Simon Dec 25 '15 at 11:21

1 Answers1

1

I had a similar problem, and didn't find any solution online. In my app I have list of custom interactive elements, with which you can interact swiping horizontally (and in this case scrolling should be locked), but when you swipe vertically it should scroll. According to your comments you already solved you problem, but for those, whose problem is similar to mine and whose research led them here, I will post my solution. I looked a bit under the hood of RecyclerView, and here is what I found. "Error processing scroll; pointer index for id -1 not found. Did any MotionEvents get skipped?" - the problem is that the variable mScrollPointerId, which is used to get index, is set in onTouchEvent when ACTION_DOWN happens.In my particular case when ACTION_DOWN happens I return false from onInterceptTouchEvent, since at that moment I don't know weather user wants to scroll or interact with list element. And in this case onTouchEvent is not called. Later, when I find out that user wants to intercact with element, I return false from onInterceptTouchEvent and onTouchEvent is called, but it can't process scrolling because mScrollPointerId is not set. The solution here is just to call onTouchEvent from onInterceptTouchEvent when ACTION_DOWN happens.

@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
    ...
    switch (action) {
        case MotionEvent.ACTION_DOWN: {
            onTouchEvent(e);
            ...
            break;
            }
        ...
        }
    ...
}
Yurii
  • 86
  • 1
  • 7