Запретить прокрутку RecyclerView в AppBarLayout до того, как AppBarLayout будет свернут

Я создаю заголовок RecyclerView с заголовком, где заголовок сжимается при прокрутке вверх по RecyclerView. Я могу достичь этого очень близко к макету ниже, с прозрачными AppBarLayout и MyCoolView, которые являются заголовком. Эффект параллакса отлично работает.

Однако, если заголовок все еще отображается и я бросаю RecyclerView, RV медленно прокручивается вверх, а некоторые элементы находятся под панелью инструментов, пока RV не достигнет вершины представления. Я играл с scrollFlags, но не достиг желаемого результата. Любые предложения о том, как улучшить опыт броска, чтобы предметы не обрезались?

Посмотрите видео и посмотрите, когда его разворачивается --- https://www.dropbox.com/s/jppd6m7zo41k23z/20160609_151309.mp4?dl=0

<android.support.design.widget.CoordinatorLayout>

     <android.support.design.widget.AppBarLayout
         android:background="#00000000">

         <android.support.design.widget.CollapsingToolbarLayout
            app:layout_scrollFlags="scroll|exitUntilCollapsed">

             <com.android.myapp.MyCoolView
                app:layout_collapseMode="parallax"/>

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

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

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

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

Ответ 1

Возможное решение (непроверено). Добавьте OnOffsetChangedListener в свой AppBarLayout и обратите внимание на значение смещения. Сначала объявите это поле:

private boolean shouldScroll = false;

Затем, onCreate:

AppBarLayout appbar = findViewById(...);
appbar.addOnOffsetChangedListener(new OnOffsetChangedListener() {
    @Override
    void onOffsetChanged(AppBarLayout appbar, int offset) {
        // Allow recycler scrolling only if we started collapsing.
        this.shouldScroll = offset != 0;
    }
});

Теперь добавьте прослушиватель прокрутки в свой RecyclerView. Всякий раз, когда он пытается выполнить прокрутку, верните прокрутку, если AppBarLayout все еще расширен:

RecyclerView recycler = findViewById(...);
recycler.addOnScrollListener(new OnScrollListener() {
    @Override
    void onScrolled(RecyclerView recycler, int dx, int dy) {
        // If AppBar is fully expanded, revert the scroll.
        if (!shouldScroll) {
            recycler.scrollTo(0,0);
        }
    }
});

Возможно, это потребует некоторой настройки. Я вижу два вопроса:

  • Возможное переполнение стека, если scrollTo() вызывает onScrolled() назад. Может быть решена с помощью логического значения или путем удаления/добавления прослушивателя прокрутки
  • Возможно, вы хотите предотвратить прокрутку не только, когда AppBarLayout полностью развернута, но более широко, когда AppBarLayout не скомпенсирован. Это означает, что вам не нужно проверять offset != 0, а скорее на offset == appBarLayout.getTotalScrollRange(). Я думаю.

Ответ 2

Возможно, вы можете добавить layout_behavior = "@string/appbar_scrolling_view_behavior" в свой RecylerView, как это.

<android.support.v7.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" />

Ответ 3

Обтекание RecyclerView в FrameLayout решает эту проблему.

Вам также нужно переместить appbar_scrolling_view_behavior из RecyclerView в FrameLayout, чтобы он был правильно расположен под AppBarLayout.

<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    >

    <!--This RecyclerView MUST be wrapped in a FrameLayout-->
    <!--This prevents the RecyclerView from going behind the AppBarLayout-->
    <android.support.v7.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</FrameLayout>