CoordinatorLayout 中的 ViewPager(用于粘性 header 目的)导致滚动中断

ViewPager within a CoordinatorLayout (for sticky header purposes) leads to broken scrolling

在此处演示问题的测试应用程序:https://github.com/jstubing/ViewPagerTest

我正在使用 CoordinatorLayout/AppBarLayout 组合在可滚动页面中实现粘性 header。在页面的最底部是一个 ViewPager2。当我滑动到新的 ViewPager 页面时,内容会异步加载并填充 ViewPager 中的 RecyclerView。这一切都很好。

当我滑动到新的 ViewPager 页面时出现问题。此时,内容加载并呈现良好,但我无法再在 ViewPager 之外启动滚动。换句话说,在“上页内容”部分(参见 XML)上下滑动没有任何作用。

但是,在 ViewPager 中可以上下滚动。如果我在 ViewPager 中向上或向下滚动,或者甚至只是点击一次 ViewPager,它会修复所有问题,我可以再次从 ViewPager.

外部启动滚动

我尝试切换到原始 ViewPager 而不是 ViewPager2,它确实更可靠,但问题仍然时常发生。

有什么办法可以解决这个问题吗?最终,我只想能够在 ViewPager 页面之间滑动,并让整个 activity 页面保持可滚动。我很困惑为什么我一开始就失去了滚动能力,为什么点击 ViewPager 可以修复它。

<androidx.constraintlayout.widget.ConstraintLayout 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">

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:elevation="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <com.google.android.material.appbar.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/background"
            app:elevation="0dp">

            <androidx.constraintlayout.widget.ConstraintLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="32dp"
                app:layout_scrollFlags="scroll">

                <!-- UPPER PAGE CONTENT -->

            </androidx.constraintlayout.widget.ConstraintLayout>

            <!-- Sticky header -->
            <include
                android:id="@+id/sticky_header"
                layout="@layout/sticky_header"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />

        </com.google.android.material.appbar.AppBarLayout>

        <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/events_pager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior" />

    </androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

The problem occurs when I swipe to a new ViewPager page. At this point, the content loads and renders fine but I am no longer able to initiate a scroll outside of the ViewPager.

I am again able to initiate a scroll from outside of the ViewPager.

所以,问题现在仅限于ViewPager。很可能是因为 ViewPager2 支持通过其内部 RecyclerView 进行嵌套滚动(我不是指页面的 RecyclerView,而是使 ViewPager 在内部起作用的页面。

所以,首先我们需要禁用 ViewPager2:

的嵌套滚动

科特林:

viewPager.children.find { it is RecyclerView }?.let {
        (it as RecyclerView).isNestedScrollingEnabled = false
}

Java:

for (int i = 0; i < viewPager.getChildCount(); i++) {
    View child = viewPager.getChildAt(i);
    if (child instanceof RecyclerView) 
        child.setNestedScrollingEnabled(false);
}

我们不能完全禁用嵌套滚动,因为这会严重影响外部滚动,因此,我们可以通过包裹 ViewPager:

NestedScrollView 来操作它
<androidx.core.widget.NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fillViewport="true"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/events_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent />

</androidx.core.widget.NestedScrollView>