material-components / material-components-android

Modular and customizable Material Design UI components for Android
Apache License 2.0
16.38k stars 3.07k forks source link

Child view is cut off when `expandedOffset` is set on BottomSheetBehavior #1336

Closed josh-bartz closed 2 years ago

josh-bartz commented 4 years ago

Description:

When I add a RecyclerView to that FragmentContainerView, the bottom of it is cut off (doesn't let me scroll to the end) by the same amount as I set as the behavior_expandedOffset.

Expected behavior:

I would expect all content to be scrollable.

Source code:

    <androidx.constraintlayout.widget.ConstraintLayout
        style="@style/ONX.Widget.BottomSheet.Light"
        android:id="@+id/query_bottomsheet"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">

        <com.onxmaps.onxmaps.customviews.bottomsheetheader.BottomSheetHeader
            android:id="@+id/map_query_header"
            android:layout_height="wrap_content"
            android:layout_width="0dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"/>

        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/map_query_container"
            android:name="com.onxmaps.onxmaps.bottomsheet.BottomSheetLoadingFragment"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintTop_toBottomOf="@id/map_query_header"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"/>

    </androidx.constraintlayout.widget.ConstraintLayout>

with the following style:

    <style name="ONX.Widget.BottomSheet" parent="Widget.MaterialComponents.BottomSheet">
        <item name="android:paddingTop">@dimen/bottomsheet_padding_top</item>
        <item name="behavior_halfExpandedRatio">@dimen/bottomsheet_halfratio</item>
        <item name="behavior_fitToContents">false</item>
        <item name="behavior_hideable">true</item>
        <item name="shapeAppearance">@null</item>
        <item name="behavior_expandedOffset">150</item>

Android API version: 28

Material Library version: Material Android Library version you are using here (e.g., 1.1.0-alpha07)

implementation 'com.google.android.material:material:1.1.0'

implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta3'

implementation 'androidx.appcompat:appcompat:1.0.2'

I also tried material:1.2.0-alpha06 and constraintlayout:2.0.0-beta5

Device: Samsung Galaxy S8

ckdevrel commented 3 years ago

I was facing the same issue, and I replaced ConstraintLayout to LinearLayout.. Magically everything starts working.. I know it is super weird, but this is the only fix which resolved the issue..

guilucas07 commented 3 years ago

same as @TakeoffAndroid, replace constraintLayout to LinearLayout "solved" the problem

amg commented 3 years ago

I'm using LinearLayout but still having same issue. Here is layout:

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:text="Something here"
        android:textAlignment="textStart"
        android:textAppearance="@style/TextAppearance.App.Headline2" />

    <View
        android:layout_width="match_parent"
        android:layout_height="@dimen/divider_height"
        android:background="@color/divider_color" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/list"
        android:overScrollMode="never"
        android:scrollbars="none"
        android:nestedScrollingEnabled="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/selectableItemBackground"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:padding="12dp">

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:src="@drawable/someImg" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="20dp"
            android:gravity="center|start"
            android:includeFontPadding="false"
            android:text="@string/someText"
            android:textAlignment="viewStart"
            android:textAppearance="@style/TextAppearance.App.Body"/>
    </LinearLayout>
</LinearLayout>

Also bottom sheet dialog is setup as such:

val mBehavior: BottomSheetBehavior<*> = BottomSheetBehavior.from(sheetBinding.root.parent as View)
bottomSheetDialog!!.setOnShowListener {
    // has to be expanded, otherwise recyclerview is not fully visible once more content than a screenfull
    mBehavior.expandedOffset = maxOf(view.dip(56f), binding.root.height - sheetBinding.root.height)
    mBehavior.state = STATE_EXPANDED
//  fitToContents true ignores expandedOffset value but when isFitToContents false it is cutting recyclerview content by offset amount
//  mBehavior.isFitToContents = false
//  mBehavior.peekHeight = minOf(binding.root.height - view.dip(56f), sheetBinding.root.height)
}
drchen commented 2 years ago

It's a bit unfortunate that this is kind of "wai" and might not be something we are able to solve... (I would say it's more like a mistake in API design but we don't really have a way to change a public API.)

I think the cause is that in these case your bottom sheet has a taller height than (screen height - expand offset), so the bottom part of it will be cut.

I don't think we can modify bottom sheet's height from coordinator layout behavior. Probably the best way will be specifying a top margin equals to the expanded offset, so the bottom sheet will get the correct measured height.

I'll close the issue for now. Let me know if none of the workarounds work for you.

janvennemann commented 2 years ago

I ran into this as well but wanted to keep using the ConstraintLayout. Applying a top margin as suggested didn't work for me (or i applied it to the wrong view, not sure). However, what finally did the trick for me was applying a bottom margin to the view that is wrapping my bottom sheet panel content.

Here is a simplified version of the layout i'm using:

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/panelContentView" />

    <androidx.constraintlayout.widget.ConstraintLayout
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/panelBottomSheet"
        app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
        android:behavior_expandedOffset="100dp">
        <com.google.android.material.card.MaterialCardView
            android:id="@+id/panelGrabberArea"
            android:layout_width="32dp"
            android:layout_height="4dp"
            android:backgroundTint="#333333"
            app:layout_constraintStart_toStartOf="parent"
            android:layout_marginTop="10dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:id="@+id/panelBottomSheetContentView"
            android:backgroundTint="#00FF00"
            android:layout_marginTop="10dp"
            android:layout_marginBottom="100dp"
            app:layout_constraintTop_toBottomOf="@+id/panelGrabberArea"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintBottom_toBottomOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

Notice the android:layout_marginBottom="100dp" on the FrameLayout which matches the expandedOffset on the BottomSheet / ConstraintLayout. That way the content remains on the screen and is not cut off.