Android BottomSheetDialogFragment does not expand completely

36,993

Solution 1

By deeper UI inspection, we find that there is another CoordinatorLayout that wraps our coordinator layout. The parent CoordinatorLayout has a FrameLayout with a BottomSheetBehaviorwith the id design_bottom_sheet. The peek height set from our code above was getting constrained due the match_parent height of the FrameLayout with the id design_bottom_sheet

By setting the peek height of the FrameLayout with the id design_bottom_sheet , this issue was resolved

    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    getDialog().setOnShowListener(new DialogInterface.OnShowListener() {
        @Override
        public void onShow(DialogInterface dialog) {
            BottomSheetDialog d = (BottomSheetDialog) dialog;
            coordinatorLayout = (CoordinatorLayout) d.findViewById(R.id.locUXCoordinatorLayout);
            bottomSheetInternal = d.findViewById(R.id.locUXView);
            bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetInternal);
            bottomSheetBehavior.setHidable(false);
            BottomSheetBehavior.from((View)coordinatorLayout.getParent()).setPeekHeight(bottomSheetInternal.getHeight());
            bottomSheetBehavior.setPeekHeight(bottomSheetInternal.getHeight());
            coordinatorLayout.getParent().requestLayout();

        }
    });

Solution 2

Using this code in onCreateView.

getDialog().setOnShowListener(new DialogInterface.OnShowListener() {
        @Override
        public void onShow(DialogInterface dialog) {
            BottomSheetDialog d = (BottomSheetDialog) dialog;
            FrameLayout bottomSheet = (FrameLayout) d.findViewById(R.id.design_bottom_sheet);
            CoordinatorLayout coordinatorLayout = (CoordinatorLayout) bottomSheet.getParent();
            BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);
            bottomSheetBehavior.setPeekHeight(bottomSheet.getHeight());
            coordinatorLayout.getParent().requestLayout();
        }
    });

Solution 3

I've found another solution. Maybe for future readers it can be useful.

@Override
public void setupDialog(Dialog dialog, int style) {
    super.setupDialog(dialog, style);
    final View root = View.inflate(getContext(), R.layout.fragment_bottom_sheet_choose_time, null);
    dialog.setContentView(root);
    initView(root);

    CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) ((View) root.getParent()).getLayoutParams();
    CoordinatorLayout.Behavior behavior = params.getBehavior();

    if (behavior != null && behavior instanceof BottomSheetBehavior) {
        mBottomSheetBehavior = (BottomSheetBehavior) behavior;
        mBottomSheetBehavior.setBottomSheetCallback(mBottomSheetBehaviorCallback);

        root.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                root.getViewTreeObserver().removeGlobalOnLayoutListener(this);
                int height = root.getMeasuredHeight();
                mBottomSheetBehavior.setPeekHeight(height);
            }
        });
    }
}

As @Anthonyeef mentioned, here ViewTreeObserver is aimed to get the exact measure height after the view is really measured and the GlobalOnLayoutListener is removed for better performance.

But please, before using in production, test this solution on different devices and screens, because if your content in bottom sheet is higher than your screen it can produce some strange swipe behavior.

Solution 4

Kotlin safe way of achieving this is:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    dialog.setOnShowListener {
        val dialog = it as BottomSheetDialog
        val bottomSheet = dialog.findViewById<View>(R.id.design_bottom_sheet)
        bottomSheet?.let { sheet ->
            dialog.behavior.peekHeight = sheet.height
            sheet.parent.parent.requestLayout()
        }
    }
}

NOTE: No need to wrap your layout in coordinator layout.

Works like charm.

Solution 5

Thanks @athysirus for the neat approach. Here is the version I ended up with, in case somebody wants to have a working kotlin sample.

Important to note is, that you should also remove the global layout listener, once done.

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    view.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            val bottomSheet = (dialog as BottomSheetDialog).findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)
            BottomSheetBehavior.from<View>(bottomSheet).apply {
                state = BottomSheetBehavior.STATE_EXPANDED
                peekHeight = 0
            }
            view.viewTreeObserver.removeOnGlobalLayoutListener(this)
        }
    })
Share:
36,993
Nandish A
Author by

Nandish A

Updated on February 28, 2021

Comments

  • Nandish A
    Nandish A about 3 years

    I have the following test bottom sheet implementation.

    When I set the peekHeight to a value less than 500, it works. After some value, any increase in peek height will not change how the bottom sheet is expanded. It Just remains there to only drag manually. How do we set the peekHeight programmatically to ensure that the bottom sheet is auto expanded to the peek height.

    enter image description here

    bottom_sheet_dialog_main

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/locUXCoordinatorLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    
        <LinearLayout
            android:id="@+id/locUXView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:fitsSystemWindows="true"
            android:orientation="vertical"
            app:behavior_hideable="false"
            app:behavior_peekHeight="0dp"
            app:layout_behavior="@string/bottom_sheet_behavior">
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="1 Value" />
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="2 Value" />
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="3 Value" />
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="4 Value" />
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="5 Value" />
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="6 Value" />
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="7 Value" />
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="8 Value" />
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="9 Value" />
    
            <TextView
                android:layout_width="match_parent"
                android:layout_height="100dp"
                android:text="First Value" />
        </LinearLayout>
    </android.support.design.widget.CoordinatorLayout>
    

    Java code

    public class MyBottomSheetDialogFragment extends BottomSheetDialogFragment {
    
        private static BottomSheetBehavior bottomSheetBehavior;
        private static View bottomSheetInternal;
        private static MyBottomSheetDialogFragment INSTANCE;
    
        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            getDialog().setOnShowListener(new DialogInterface.OnShowListener() {
                @Override
                public void onShow(DialogInterface dialog) {
                    BottomSheetDialog d = (BottomSheetDialog) dialog;
                    CoordinatorLayout coordinatorLayout = (CoordinatorLayout)d.findViewById(R.id.locUXCoordinatorLayout);
                    bottomSheetInternal = d.findViewById(R.id.locUXView);
                    bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetInternal);
                    bottomSheetBehavior.setPeekHeight(bottomSheetInternal.getHeight());
                    bottomSheetInternal.requestLayout();
                    coordinatorLayout.getLayoutParams().height = bottomSheetInternal.getHeight();
                    Toast.makeText(getActivity(), "Height is" + bottomSheetInternal.getHeight() + "  " + coordinatorLayout.getLayoutParams().height, Toast.LENGTH_LONG).show();
    
                }
            });
            INSTANCE = this;
            return inflater.inflate(R.layout.bottom_sheet_dialog_main, container, false);
        }
    }