Android: горизонтальный Recyclerview внутри вертикального Recycliewiew

Это, по-видимому, довольно распространенная картина в Android. Я пытаюсь создать нечто похожее на то, как Facebook отображает их рекламные объявления:

Объявления Facebook

Вы можете видеть, что они имеют внешний вертикальный recyclerview с внутренним горизонтальным recyclerview как один из элементов внешнего адаптера вертикального recyclerview.

Я последовал этому руководству из Google в разделе «Управление событиями в трансляции в ViewGroup» – http://developer.android.com/training/gestures/viewgroup.html, поскольку Recyclerview расширяет ViewGroup, а код похож на то, что я хочу сделать.

Я немного настроил его так, чтобы он обнаруживал движения по оси Y вместо движений по оси X и применял их к внешнему вертикальному 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; } } 

Мой xml-макет:

 <?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> 

Когда я пытаюсь перетащить внутренний горизонтальный recyclerview (надеясь, что touchhevent будет перехвачен внешним вертикальным recyclerview), внешний recyclerview не прокручивает вертикально вообще.

Это также приводит к ошибке:

Прокрутка ошибок; Индекс указателя для id -1 не найден. Пропустили ли какие-либо MotionEvents?

Кто-нибудь знает, как правильно это сделать?

У меня была аналогичная проблема, и я не нашел решения в Интернете. В моем приложении у меня есть список пользовательских интерактивных элементов, с которыми вы можете взаимодействовать, прокручивая по горизонтали (и в этом случае прокрутка должна быть заблокирована), но при прокрутке по вертикали она должна прокручиваться. Согласно вашим комментариям, вы уже решили свою проблему, но для тех, чья проблема похожа на мою и чьи исследования привели их сюда, я отправлю свое решение. Я немного под капотом RecyclerView , и вот что я нашел. «Ошибка обработки прокрутки, индекс указателя для идентификатора -1 не найден. Пропустили ли какие-либо действия MotionEvents?» – проблема в том, что переменная mScrollPointerId , которая используется для получения index , устанавливается в onTouchEvent когда ACTION_DOWN ACTION_DOWN. В моем конкретном случае, когда ACTION_DOWN происходит, я возвращаю false из onInterceptTouchEvent , так как в тот момент я не знаю, что пользователь погоды хочет Прокручивать или взаимодействовать с элементом списка. И в этом случае onTouchEvent не вызывается. Позже, когда я узнаю, что пользователь хочет взаимодействовать с элементом, я возвращаю false из onInterceptTouchEvent и onTouchEvent , но он не может обрабатывать прокрутку, потому что mScrollPointerId не установлен. Решение здесь – это просто вызвать onTouchEvent из onInterceptTouchEvent когда происходит ACTION_DOWN .

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