Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to enable dragging on ViewPager2 inside BottomSheetDialogFragment?

There's a BottomSheetDialogFragment and working good vertically dragging states on fragment layout and opening STATE_EXPANDED mode. There's a recyclerview inside it and dragging vertically works on the bottom sheet but it doesn't work on recyclerview because of scrolling event. How the bottom sheet dragging event to work instead of scroll event on recyclerview when reached top of list and still scrolling up for collapse the bottom sheet?

BottomSheetDialogFragment hierarchy:

FragmentRootLinearLayout -> ...BottomLinearLayout... -> ViewPager2 -> RecyclerView

BottomSheetDialogFragment xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/BookInfoFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/tool_sheet_bg"
    android:orientation="vertical"
    app:layout_behavior="@string/bottom_sheet_behavior"
    app:behavior_hideable="true"
    android:clickable="true"
    android:focusable="true">

    <LinearLayout
        android:id="@+id/tabs_linear_layout"
        style="@style/ThemeSettingsRowContainer"
        android:layout_width="match_parent"
        android:layout_height="550dp"
        android:layout_marginTop="15dp"
        android:background="@drawable/xml_rounded_corner_bg2"
        android:clickable="true"
        android:focusable="true"
        android:paddingTop="0dp"
        android:paddingBottom="0dp">

        <com.google.android.material.tabs.TabLayout
            android:id="@+id/book_loading_tablayout"
            android:layout_width="match_parent"
            android:layout_height="50dp" />

        <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/book_loading_viewpager"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clickable="true"
            android:focusable="true" />

    </LinearLayout>

</LinearLayout>

Edit: The issue is on ViewPager2, when I change it to ViewPager dragging is working good on it. Same issue: BottomSheet + ViewPager2 drag to hide not works

like image 636
ATES Avatar asked May 29 '21 08:05

ATES


1 Answers

The issue is that we need to disable the nested scrolling on the ViewPager2, but the android:nestedScrollingEnabled="false" doesn't work because ViewPager2 is functioning internally using RecyclerView which has the effect of the nested scrolling.

So, you need to disable the nested scrolling of the ViewPager RecyclerView.

The main issue is that the RecyclerView of ViewPager2 is not accessible by default.

Update Feb.2022

Adding a better way to access the RecyclerView of the ViewPager2 instead of using reflections:

Kotlin:

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

Java:

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

This should work, but if still facing issues, then try to disable the over scroll mode of the RecyclerView:

// Kotlin
recyclerView?.overScrollMode = View.OVER_SCROLL_NEVER // Optional
// Java
recyclerView.setOverScrollMode(View.OVER_SCROLL_NEVER); // Optional

Preview:


With Reflections ----

You can access it using java reflection.

And this requires to know the name field of the RecyclerView which can be found in the ViewPager2 definition class which is mRecyclerView

Combining that together in a helper function:

public static RecyclerView getRecyclerView(ViewPager2 viewPager) {
    try {
        Field field = ViewPager2.class.getDeclaredField("mRecyclerView");
        field.setAccessible(true);
        return (RecyclerView) field.get(viewPager);
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    return null;
}

Then you can disable the nested scrolling as follows:

RecyclerView recyclerView = getRecyclerView(viewPager);
if (recyclerView != null)
    recyclerView.setNestedScrollingEnabled(false);
recyclerView.setOverScrollMode(View.OVER_SCROLL_NEVER);

For Kotlin users:

Extension function:

fun ViewPager2.getRecyclerView(): RecyclerView? {
    try {
        val field = ViewPager2::class.java.getDeclaredField("mRecyclerView")
        field.isAccessible = true
        return field.get(this) as RecyclerView
    } catch (e: NoSuchFieldException) {
        e.printStackTrace()
    } catch (e: IllegalAccessException) {
        e.printStackTrace()
    }
    return null
}

And usage:

val recyclerView = viewPager.getRecyclerView()
recyclerView?.isNestedScrollingEnabled = false
recyclerView?.overScrollMode = View.OVER_SCROLL_NEVER // Optional
like image 131
Zain Avatar answered Sep 19 '22 05:09

Zain