Макет Android: горизонтальный Recyclerview внутри вертикального Recyclerview внутри Viewpager с поведением прокрутки

Это приложение, которое я пытаюсь построить со всеми элементами, отображенными ниже:

Veggies app

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

когда я положил палец на части" StrawBerry Plant " в recyclerview и прокрутите вверх, прокрутите панель инструментов:

strawberry plant

если я положил палец на горизонтальный scrollview и прокрутите вверх, он не прокручивает панель инструментов вообще.

ниже приведен мой код макета xml до сих пор.

на активность XML-макета:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/fragment_container"
    android:clipChildren="false">

    <android.support.design.widget.CoordinatorLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/container"
        >

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appBarLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:minHeight="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_scrollFlags="scroll|enterAlways">

        </android.support.v7.widget.Toolbar>

        <android.support.design.widget.TabLayout
            android:id="@+id/sliding_tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?attr/colorPrimary"
            style="@style/CustomTabLayout"
            />

    </android.support.design.widget.AppBarLayout>
    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        />

    </android.support.design.widget.CoordinatorLayout>

</FrameLayout>

на "фрукты" фрагмент 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">

    <ProgressBar
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/progressBar"
        android:visibility="gone"
        android:layout_centerInParent="true"
        android:indeterminate="true"/>

<!--    <android.support.v7.widget.RecyclerView-->
    <com.example.simon.customshapes.VerticallyScrollRecyclerView
        android:id="@+id/main_recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</RelativeLayout>

я использовал пользовательский класс VerticallyScrollRecyclerView, который следует примеру google обработки событий касания в viewgroup. Его цель-перехватить и использовать все события вертикальной прокрутки, чтобы прокручивать панель инструментов:http://developer.android.com/training/gestures/viewgroup.html

код VerticallyScrollRecyclerView это ниже:

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 super.onInterceptTouchEvent(ev); // Do not intercept touch event, let the child handle it
        }
        switch (action) {
            case MotionEvent.ACTION_MOVE: {
                Log.e("VRecView", "its moving");
                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 (Math.abs(yDiff) > 5) {
                    // Start scrolling!
                    Log.e("Scroll", "we are scrolling vertically");
                    mIsScrolling = true;
                    return true;
                }
                break;
            }
        }
        return super.onInterceptTouchEvent(ev);
    }


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

}

на "подходящий" макет который является recyclerview в вертикальном recyclerview:

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:background="@color/white"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Favourite"
        android:layout_marginTop="8dp"
        android:layout_marginBottom="8dp"
        android:layout_marginLeft="16dp"
        android:id="@+id/header_fav"/>

    <android.support.v7.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_below="@+id/header_fav"
        android:id="@+id/recyclerview_fav">
    </android.support.v7.widget.RecyclerView>

</RelativeLayout>

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

5 очков Гриффиндору за правильный ответ и, конечно же, репутация указывает на SO.

4 ответов


протестированное решение:

все, что вам нужно, это позвонить mInnerRecycler.setNestedScrollingEnabled(false); на RecyclerViews


объяснение:

RecyclerView имеет поддержку вложенной прокрутки, введенной в API 21 в рамках реализации NestedScrollingChild интерфейс. Это ценная функция, когда у вас есть вид прокрутки внутри другого, который прокручивается в том же направлении, и вы хотите прокрутить внутренний View только при фокусировке.

в любом случае RecyclerView по умолчанию звонки RecyclerView.setNestedScrollingEnabled(true); на себя при инициализации. Теперь вернемся к проблеме, так как ваш RecyclerViews находятся в пределах одного и того же ViewPager что есть AppBarBehavior, the CoordinateLayout должен решить, какой свиток реагировать, когда вы прокручиваете из своего внутреннего RecyclerView; когда ваш внутренний RecyclerViewвложенная прокрутка включена, она получает фокус прокрутки и CoordinateLayout будет отвечать на его прокрутку по внешней RecyclerViewс прокруткой. Дело это, так как ваш внутренний RecyclerViews не прокручивать по вертикали, нет изменения вертикальной прокрутки (от CoordinateLayoutточка зрения), и если нет никаких изменений, то AppBarLayout не менялись.

в вашем случае, потому что ваш внутренний RecyclerViewс прокруткой в другом направлении, вы можете отключить его, тем самым причинив CoordinateLayout игнорировать его прокрутку и реагировать на внешний RecyclerView ' s прокрутка.


обратите внимание:

атрибут xml android:nestedScrollingEnabled="boolean" не предназначен для использования с RecyclerView, и попытка использовать android:nestedScrollingEnabled="false" в результате java.lang.NullPointerException так, по крайней мере сейчас, вам придется сделать это в коде.


протестированное решение, использовать специальный NestedScrollView().

код:

public class CustomNestedScrollView extends NestedScrollView {
    public CustomNestedScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
         // Explicitly call computeScroll() to make the Scroller compute itself
            computeScroll();
        }
        return super.onInterceptTouchEvent(ev);
    }
}

Я немного опоздал, но это определенно будет работать для других, стоящих перед той же проблемой

 mRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
            @Override
            public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
                int action = e.getAction();
               // Toast.makeText(getActivity(),"HERE",Toast.LENGTH_SHORT).show();
                switch (action) {
                    case MotionEvent.ACTION_POINTER_UP:
                        rv.getParent().requestDisallowInterceptTouchEvent(true);

                        break;
                }
                return false;
            }

Я бы предложил вам добавить горизонтальный recyclerview внутри фрагментов, таких как приложение google