This seems to be a fairly common pattern in android. I'm trying to create something similar to how Facebook displays their adverts:
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?
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;
}
...
}
...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With