NiceToMytyuk
NiceToMytyuk

Reputation: 4307

Inconsistent BottomSheetBehavior: BottomSheet jumps and RecyclerView fails to resize on some devices

I'm experiencing inconsistent behavior with Android's BottomSheetBehavior on some devices. My app layout contains a CoordinatorLayout with an AppBarLayout, a main RecyclerView, and an included BottomSheet. The intended behavior is that when the BottomSheet is expanded, I adjust the bottom padding of the RecyclerView dynamically (in the BottomSheetBehavior’s onSlide callback) so that the RecyclerView resizes and remains fully visible above the bottom sheet.

However, I’ve noticed two problematic issues that occur only on certain devices and in conditions I cannot reliably reproduce on my own test devices:

  1. RecyclerView Resizing: Sometimes the RecyclerView does not resize as expected when the BottomSheet expands. Instead of shrinking its visible area to make room for the bottom sheet, it remains oversized and overlaps with the bottom sheet area.
  2. BottomSheet Jumping: On some devices, the bottom sheet suddenly “jumps” upward and then remains in that state until the user interacts with the screen. This jump seems to happen during the expansion/collapse animation.

This issue occurs more frequently on Android Go devices, though it can also be observed on standard devices. I've recorded a video of the behavior reported by a customer. In the video, in the first few seconds you can see the RecyclerView resizing issue, and after about 1:30 the BottomSheet jumping issue becomes apparent.

Below is a simplified version of my layout and behavior code:

<androidx.drawerlayout.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!-- Other views like AppBarLayout -->

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/listView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"
            android:clipToPadding="false"
            android:paddingBottom="80dp" />

        <!-- Include the bottom sheet -->
        <include layout="@layout/bottom_sheet_pterm" />
    </androidx.coordinatorlayout.widget.CoordinatorLayout>

    <!-- NavigationView omitted for brevity -->

</androidx.drawerlayout.widget.DrawerLayout>

BottomSheet Layout (simplified):

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/bottomSheet"
    android:layout_width="match_parent"
    android:layout_height="406dp"   <!-- Fixed height (56dp + 300dp + 50dp) -->
    android:background="#FFFFFF"
    android:elevation="5dp"
    android:orientation="vertical"
    app:behavior_hideable="false"
    app:behavior_peekHeight="56dp"
    app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottom_navigation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        ... />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerViewTasti"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        ... />

    <RadioGroup
        android:id="@+id/quantita_radio_group"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        ... />
</LinearLayout>

The onSlide:

private final BottomSheetBehavior.BottomSheetCallback bottomSheetCallback = new BottomSheetBehavior.BottomSheetCallback() {
    private int lastBottomPadding = -1;

    @Override
    public void onStateChanged(@NonNull View bottomSheet, int newState) {
        if (newState == BottomSheetBehavior.STATE_COLLAPSED) {
            scrollProductsToBottom();
        }
    }

    @Override
    public void onSlide(@NonNull View bottomSheet, float slideOffset) {
        int bottomPadding = getBottomPadding(bottomSheet, slideOffset);

        // Update padding only if it has changed to reduce redundant layout passes.
        if (bottomPadding != lastBottomPadding) {
            lastBottomPadding = bottomPadding;
            int leftPadding = recyclerProdotti.getPaddingLeft();
            int topPadding = recyclerProdotti.getPaddingTop();
            int rightPadding = recyclerProdotti.getPaddingRight();
            recyclerProdotti.setPadding(leftPadding, topPadding, rightPadding, bottomPadding);
        }

        // Smooth scroll to the last item, throttled
        if (slideOffset > 0.9) {
            scrollProductsToBottom();
        }
    }

    private int getBottomPadding(View bottomSheet, float slideOffset) {
        int extraPadding = (int) (80 * recyclerProdotti.getContext().getResources().getDisplayMetrics().density);
        // Calculate the dynamic bottom padding based on the bottom sheet's state.
        // Here bottomSheet.getPeekHeight() is the starting padding,
        // and we add a proportion of the remaining space as the sheet slides.
        int bottomPadding = (int) (bottomSheetTasti.getPeekHeight() +
                slideOffset * (bottomSheet.getHeight() - bottomSheetTasti.getPeekHeight()));

        // Add the extra 80dp in pixels
        bottomPadding += extraPadding;
        return bottomPadding;
    }
};

Any insights, debugging tips, or potential workarounds would be greatly appreciated!

Upvotes: 0

Views: 24

Answers (0)

Related Questions