Test app demonstrating the problem here: https://github.com/jstubing/ViewPagerTest
I'm using a CoordinatorLayout/AppBarLayout combo to achieve a sticky header within a scrollable page. At the very bottom of the page is a ViewPager2. When I swipe to a new ViewPager
page, content loads asynchronously and populates a RecyclerView
within the ViewPager. This all works well.
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. In other words, swiping up and down in the "UPPER PAGE CONTENT" section (see XML) does nothing.
However, scrolling up and down does work within the ViewPager. If I scroll up or down in the ViewPager, or even just tap the ViewPager once, it fixes everything and I am again able to initiate a scroll from outside of the ViewPager
.
I tried switching to the original ViewPager
instead of ViewPager2 and it has definitely been more reliable, but the problem still happens every so often.
Any ideas how I can fix this? Ultimately I just want to be able to swipe between ViewPager
pages and have the whole entire activity remain scrollable. I'm so confused as to why I lose scrolling ability in the first place, and why tapping on the ViewPager
fixes it.
<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.
So, the problem is now limited to the ViewPager
. Most probably because the ViewPager2
supports nested scrolling through its inner RecyclerView
(I don't mean the RecyclerView
of the page, but the one that makes the ViewPager
functions internally.
So, first thing we need to disable this nested-scrolling of the ViewPager2
:
Kotlin:
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);
}
We can't completely disable the nested scrolling because this will badly affect the scrolling outside, so, we can manipulate this by a NestedScrollView
that wraps the ViewPager
:
<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>