Reputation: 2141
Short version:
How do I set the NestedScrollingChild
of a NestedScrollingParent
with multiple number of such child.
Long version
I implemented a BottomSheetDialogFragment
whose layout consists of a ViewPager
, and the adapter of this viewpager contains a RecyclerView
.
Now, the issue is, since a NestedScrollingParent
which in this time the coordinator layout of the bottomsheet supports only one direct NestedScrollingChild
, only the first fragment of the adapter can be nest-scrolled.
What I mean is, whenever setAdapter
is called on the viewpager, the first item supports nested scrolling. But after I change the page, the new page now does not scroll. Then when I go back to the previous page, it still supports scrolling.
Also, I noticed that if the fragment or the page that can scroll is destroyed, the succeeding page now can scroll, which means that the latter page becomes the scrolling child of the bottom sheet. The problem is that page which now gained the scrolling ability is not the current item but a preceding one (my adapter must maintain 3 fragments).
Summary:
After setAdapter
Upvotes: 8
Views: 3825
Reputation: 882
In addition to @saiday's answer, these codes are working perfectly for me (API 21 and above);
Here what I have in my XML :
One BottomSheet containing a ViewPager.
The ViewPager with two pages containing one Fragment each.
Each Fragment Containing one NestedScrollView at the root view.
and my ViewPagerAdapter code (java):
@Override
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
super.setPrimaryItem(container, position, object);
Fragment currentFragment = (Fragment) object;
if (currentFragment.getView() != null) {
for (int i = 0; i < getCount(); i++) {
((NestedScrollView) container.getChildAt(i)).setNestedScrollingEnabled(false);
((NestedScrollView) container.getChildAt(i)).getChildAt(0).setNestedScrollingEnabled(false); //this is recyclerview's nestedscroll
}
NestedScrollView currentNestedScrollView = ((NestedScrollView) currentFragment.getView());
currentNestedScrollView.setNestedScrollingEnabled(true);
currentNestedScrollView.getChildAt(0).setNestedScrollingEnabled(true); //this is recyclerview's nestedscroll, I used getChildAt(0) because I have one element in NestedScrollView.
container.requestLayout();
}
}
My First and Second Layout (just the IDs are different):
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:id="@+id/marketMembersFragmentScrollView"
android:fillViewport="true">
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/marketMembersFragmentRecyclerView"/>
</androidx.core.widget.NestedScrollView>
Upvotes: 0
Reputation: 1008
When using Androidx libraries, RecyclerView implements NestedScrollingChild
instead of NestedScrollView
.
override fun setPrimaryItem(container: ViewGroup, position: Int, `object`: Any) {
super.setPrimaryItem(container, position, `object`)
val currentFragment = `object` as Fragment
if (currentFragment.view != null) {
for (i in 0 until count) {
(container.getChildAt(i) as? NestedScrollingChild)?.isNestedScrollingEnabled = false
}
val currentNestedScrollView: NestedScrollingChild = currentFragment.view as NestedScrollingChild
currentNestedScrollView.isNestedScrollingEnabled = true
container.requestLayout()
}
}
Upvotes: 0
Reputation: 129
I too ran into this problem, trying to use a ViewPager with scrolling views in almost every page, inside a BottomSheetDialog. I found this solution made by laenger (https://github.com/laenger/ViewPagerBottomSheet), and adopted it so it fits with a BottomSheetDialog instead. Hope it may help some, who runs into the same issue :)
Upvotes: 0
Reputation: 519
I had some troubles with these previous responses.
In fact, in my case the @NonNull Object object
in setPrimaryItem
method return a fragment and cannot be cast to a NestedScrollView
.
Here what I have in my XML :
BottomSheet
containing a ViewPager
.ViewPager
with two pages containing one Fragment
each.Fragment
Containing one NestedScrollView
at the root view.Here is my ViewPagerAdapter
(Kotlin)
class MyViewPagerAdapter(val id: String) :
FragmentStatePagerAdapter(supportFragmentManager) {
val titles = arrayOf(getString(R.string.title1),
getString(R.string.title2))
override fun getItem(position: Int) = when (position) {
0 -> MyFragment1.newInstance(id)
1 -> MyFragment2.newInstance(id)
else -> Fragment()
}
override fun getCount() = 2
override fun getPageTitle(position: Int): CharSequence? {
return titles[position]
}
override fun setPrimaryItem(container: ViewGroup, position: Int, `object`: Any) {
super.setPrimaryItem(container, position, `object`)
val currentFragment = `object` as Fragment
if (currentFragment.view != null) { //The first time the view isn't created yet
for (i in 0 until count) {
(container.getChildAt(i) as NestedScrollView).isNestedScrollingEnabled = false
}
val currentNestedScrollView: NestedScrollView = currentFragment.view as NestedScrollView
currentNestedScrollView.isNestedScrollingEnabled = true
container.requestLayout()
}
}
}
Upvotes: 4
Reputation: 1477
I've simplified saiday's
solution a bit. Important to know is that I am returning ViewBindings instead of Views from instantiateItem
.
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
super.setPrimaryItem(container, position, object);
for (int i = 0; i < container.getChildCount(); i++) {
//First disable nested scrolling for all instantiated child views
((NestedScrollView)container.getChildAt(i)).setNestedScrollingEnabled(false);
}
//Enable nested scrolling for the primary item
ViewDataBinding binding = (ViewDataBinding) object;
NestedScrollView current = ((NestedScrollView)binding.getRoot());
current.setNestedScrollingEnabled(true);
container.requestLayout();
}
Upvotes: 0
Reputation: 1300
I had the exact same issue and figure out very similar solution.
Providing my solution for reference:
ViewPagerAdapter.java
....
@Override
public void setPrimaryItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
super.setPrimaryItem(container, position, object);
NestedScrollView current = ((NestedScrollView)object);
current.setNestedScrollingEnabled(true);
for (int i = 0; i < getCount(); i++) {
if (i != position) {
NestedScrollView otherScrollView = container.findViewWithTag(i);
otherScrollView.setNestedScrollingEnabled(false);
}
}
container.requestLayout();
}
...
Also , I write a blog post on this topic: Cannot scroll scrollable content inside ViewPager as BottomSheet of CoordinatorLayout
Upvotes: 1
Reputation: 2141
After digging into the source code, I found that the problem lies on a faulty algorithm used in finding the NestedScrollingChild
of the bottomsheet (folks at Google did not take into account the possiblity of a ViewPager
inside the bottomsheet).
See the method here: findScrollingChild()
What this method does is it will return the first NestedScrollingChild
it encounters on a given view (the bottomsheet in this case), which in the case of a viewpager with 3 pages, the one preceding the current page. Also, this method is triggered during the layout phase of the children of the CoordinatorLayout
wrapper of the bottomsheet.
With this in mind, one can devise many solutions including subclassing the behavior itself.
Also, one can limit the NestedScrollingChild
inside the viewpager by adding and removing one instance of such child (remove from the old page, then add in the current page), which is what I did. You can do this inside setPrimaryItem
of the adapter or on a OnPageChangeListener
. Just be sure to call requestLayout
on the coordinator layout of the bottomsheet. (This solution depends on the kind of layout/structure of the pager adapter so I won't post my exact solution).
Upvotes: 4