material-components / material-components/material-components-android

[BottomSheetBehavior] - ViewPager2 Compatibility Issue

Open
#2,689 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug Widget: BottomSheet
Dominant language
Java
Stars
17.4k
Forks
3.2k
PR merge metrics
No merged PRs in 30d

Description

**Description:**
1) since findScrollingChild() is not public, it is not readily possible to override it.
2) ViewPager2 uses a RecyclerView as part of it's implementation. The algorithm in `findScrollingChild` doesn't take this into consideration, and the ViewPager2's embedded RecyclerView gets set as the scrolling child.

To attempt to address this in my app, I copied in the BottomSheetBehavior class (from the code with same tag as the version of material components in my app, ~~1.5.0~~ 1.6.0), added `@SuppressLint("RestrictedApi")`, and made `findScrollingChild` public. I then sub-classed it:

```kotlin
@Suppress("unused")
class MyBottomSheetBehavior @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
MaterialBottomSheetBehavior(context, attrs) {

override fun findScrollingChild(view: View?): View? {

view?.let {
if (ViewCompat.isNestedScrollingEnabled(it)) {
return it
}
}

if (view is ViewGroup) {
(view as? ViewPager2)?.let { pager ->
(pager.getChildAt(0) as? RecyclerView)?.let { pagerChild ->
pagerChild.layoutManager?.findViewByPosition(pager.currentItem)?.let {
findScrollingChild(it)
}
}
} ?: run {
var i = 0
val count = view.childCount
while (i < count) {
val scrollingChild = findScrollingChild(view.getChildAt(i))
if (scrollingChild != null) {
return scrollingChild
}
i++
}
}

}
return null
}

}
```

This causes the findScrollingChild to skip the RecyclerView embedded in the ViewPager2, and use the nested scroll view from the currently selected tab. To keep this in sync, I attach a page change listener to the ViewPager2 that gets the hosting `CoordinatorLayout`, and calls `requestLayout()` on it after the page change.

**Expected behavior:**
Scrolling down will open the bottom sheet, and then once open will scroll the content in the tab.
Scrolling up will scroll the content in the tab, when the top is reached, move the bottom sheet to the half expanded state.

**Actual behavior:**
Scrolling down will open the bottom sheet, and then once open will scroll the content in the tab.
Scrolling up will immediately collapse the bottom sheet to the half expanded state *unless* scrolling down was performed first on the same touch. e.g., if you scroll down, and then up, without lifting your finger, it works as expected. This only occurs when the ViewPager2 is present - a lone RecyclerView for example works as expected.

I also tried disabling swiping on the ViewPager2 instance, thinking it might be interfering with the scroll up gesture's touch being taken by the RecyclerView in the ViewPager2 tab, but it didn't seem to help.

**The Ask(s):**
1) Please make findScrollingChild public so that it can be overridden to more easily accommodate this and similar use-cases. This would prevent having to add a dependency on ViewPager2, or other libraries.

2) Help fixing the scroll behavior when the ViewPager2 is present. I'm not sure where the issue comes from - is it ViewPager2? Is it RecyclerView? Is it BottomSheetBehavior?

**Other Component Versions:**
ViewPager2 - 1.1.0-beta01
RecyclerView - 1.2.1

**Android API version:** 31

**Material Library version:** 1.6.0

**Device:** x86 emulator, arm64 emulator, pixel 3a, samsung galaxy S22 ultra

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at BottomSheetBehavior.findScrollingChild and reproduce the described gesture behavior with ViewPager2, its embedded RecyclerView, and the nested-scrolling content in the selected tab. Compare it with the lone RecyclerView case and determine whether the method visibility or child selection causes the failure. Done means upward scrolling behaves as expected with ViewPager2 present and the requested override use case is addressed.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, kotlin
Domain
mobile
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.