30

I'm getting an wrong behavior when showing an Bottom Sheet Dialog in landscape mode. The problem occurs in the 24.+ version of the design library. According below image the Bottom Sheet is not showing correctly only in landscape. Im using BottomSheetDialog class and i'm following this tutorial: http://www.skholingua.com/blog/bottom-sheet-android, in my published apps the problem also occurs.

I tested the 25.+ version and the problem was not solved.

Error In landscape 24, 25.+ Library

Error In landscape

Same example in 23.+ Library

same example in 23.+ Library

Main Activity

public class MainActivity extends AppCompatActivity {
CoordinatorLayout coordinatorLayout;
private BottomSheetBehavior<View> mBottomSheetBehavior;
private TextView textView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    coordinatorLayout = (CoordinatorLayout) findViewById(R.id.main_content);
    textView = (TextView) findViewById(R.id.textView);
    View bottomSheet = coordinatorLayout.findViewById(R.id.bottom_sheet);

    //For your bottom sheet to be displayable, you need to create a BottomSheetBehavior.
    //This is created by getting a reference to the container view and calling BottomSheetBehavior.from() on that container.
    mBottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);

    mBottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
        @Override
        public void onStateChanged(@NonNull View bottomSheet, int newState) {
            switch (newState) {
                case BottomSheetBehavior.STATE_DRAGGING:
                    break;
                case BottomSheetBehavior.STATE_COLLAPSED:
                    mBottomSheetBehavior.setPeekHeight(0);
                    break;
                case BottomSheetBehavior.STATE_EXPANDED:
                    break;
                case BottomSheetBehavior.STATE_HIDDEN:
                    break;
                case BottomSheetBehavior.STATE_SETTLING:
                    break;
            }
        }

        @Override
        public void onSlide(@NonNull View bottomSheet, float slideOffset) {
        }
    });

}


public void onClick(View v) {
    switch (v.getId()) {
        case R.id.button1:
            /**
             * For persistent bottom sheet to work, your layout should contain a coordinator layout,
             * and then in any child view of your coordinator layout, you can make it as a persistent bottom sheet
             * by adding a custom property app:layout_behavior and use behavior_peekHeight to define how much
             * of the Bottom Sheet you want visible.
             */
            textView.setText(R.string.dynamic_persistent_txt);
            mBottomSheetBehavior.setPeekHeight(300);
            mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
            break;
        case R.id.button2:
            /**
             * You can also display a Dialog in place of a View in the bottom sheet.
             * To do this, get the view from getLayoutInflater and pass it setContentView of the Dialog.
             */
            View view = getLayoutInflater().inflate(R.layout.bottom_sheet_layout, null);
            TextView textView = (TextView) view.findViewById(R.id.textView);
            textView.setText(R.string.dialog_modal_txt);
            BottomSheetDialog dialog = new BottomSheetDialog(this);
            dialog.setContentView(view);
            dialog.show();
            break;
        case R.id.button3:
            /**
             * You can also display a Fragment in place of a View in the bottom sheet.
             * To do this, you class that extends BottomSheetDialogFragment.
             */
            BottomSheetDialogFragment bottomSheetDialogFragment = new BottomSheetDialogFragmentExample();
            bottomSheetDialogFragment.show(getSupportFragmentManager(), bottomSheetDialogFragment.getTag());
            break;
    }
}

activity_main.xml

<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/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingTop="24dp"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <Button
        android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="Dynamic BottomSheet" />

    <Button
        android:id="@+id/button2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="BottomSheetDialog" />

    <Button
        android:id="@+id/button3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="BottomSheetDialogFragment" />
</LinearLayout>

<LinearLayout
    android:id="@+id/bottom_sheet"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:elevation="4dp"
    android:minHeight="120dp"
    android:orientation="vertical"
    android:padding="@dimen/activity_vertical_margin"
    app:behavior_peekHeight="120dp"
    app:layout_behavior="android.support.design.widget.BottomSheetBehavior">

    <include layout="@layout/bottom_sheet_layout" />

</LinearLayout>

</android.support.design.widget.CoordinatorLayout>

bottom_sheet_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/CreamyGreen"
android:orientation="vertical">


<TextView
    android:id="@+id/textView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/static_persistent_txt"
    android:padding="16dp"
    android:textAppearance="?android:attr/textAppearanceMedium"
    android:textColor="@android:color/white" />

<TextView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:text="@string/ipsum"
    android:textColor="@android:color/white"
    android:textSize="16sp" />
</LinearLayout>
Fernando Andrauss
  • 453
  • 1
  • 5
  • 11
  • 1
    Even though I agree it looks wrong and feels stupid, I think this is by intention. The AUTO height setting forces that the bottom sheet does not extend beyond the 16:9 ratio keyline.... which looks stupid in landscape... – Zordid Jan 26 '17 at 10:29
  • 9
    I opened an issue on Android Issue Tracker, and received possible ways to work around the problem, see: https://code.google.com/p/android/issues/detail?id=231964#c6 – Fernando Andrauss Jan 27 '17 at 11:06

10 Answers10

28

This can be achieved in 2 lines (Kotlin version)

class MyBottomSheetFragment : BottomSheetDialogFragment() {
    //....
    override fun onStart() {
        super.onStart()
        //this forces the sheet to appear at max height even on landscape
        val behavior = BottomSheetBehavior.from(requireView().parent as View)
        behavior.state = BottomSheetBehavior.STATE_EXPANDED
    }
}
MatPag
  • 41,742
  • 14
  • 105
  • 114
17

Google guys closed this as working as intended, here is a workaround

There is a magic constant of screen height ratio, which seems to do min(actualwidth, themewidth), which is obviously not working well with phone landscapes, therefore override it to something bigger

<style name="Theme.Main.Reader">
     ...
     <item name="bottomSheetDialogTheme">@style/ReaderBottomSheelDialog</item>
</style>
<style name="ReaderBottomSheelDialog" parent="Theme.Design.BottomSheetDialog">
     <item name="bottomSheetStyle">@style/BottomSheetStyle</item>
</style>

<style name="BottomSheetStyle" parent="Widget.Design.BottomSheet.Modal">
    <item name="behavior_peekHeight">512dp</item>
</style>
urSus
  • 12,492
  • 12
  • 69
  • 89
  • Thanks, this worked great. I just had to use `Theme.Design.Light.BottomSheetDialog` instead to match my app theme. – k2col Sep 27 '19 at 18:06
  • This solution has a side effect - a subtitle disappears in ActionBar after screen rotation. – isabsent Apr 21 '21 at 10:55
12

this is a work around. (Note: this code is in an activity)

View sheetView;//class level variable

private void setUpBottomSheetDialog() {

    bottomSheetDialog = new BottomSheetDialog(this);

    LayoutInflater inflater = LayoutInflater.from(this);
     sheetView = inflater.inflate(R.layout.bottom_sheet_image_source, (ViewGroup) this.getWindow().getDecorView().getRootView(), false);

    bottomSheetDialog.setContentView(sheetView);

    BottomSheetBehavior mBehavior = BottomSheetBehavior.from((View) sheetView.getParent());

    bottomSheetDialog.setOnShowListener(dialogInterface -> {
        mBehavior.setPeekHeight(sheetView.getHeight());//get the height dynamically
    });
}
hyena
  • 755
  • 1
  • 14
  • 24
10

My personal choice to solve this problem is, after onCreateView, you already have the main view of the BottomSheetDialogFragment

@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    mainLayout = inflater.inflate(R.layout.fragment_auction_bottom_sheet_dialog, container, false);
    return mainLayout;
}

And the onStart will be executed after you have the View created, so you can play with the behaviour:

@Override
public void onStart() {
    super.onStart();
    //this expands the bottom sheet even after a config change
    bottomSheetBehavior = BottomSheetBehavior.from((View) mainLayout.getParent());
    bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}

Then you will always have the Bottom Sheet expanded

Carlos Daniel
  • 2,459
  • 25
  • 30
  • This works, but it also makes it full screen in all cases. How can I make it work differently? For example, I've noticed that the sharing bottom sheet differs in case of landscape and portrait. On portrait it takes a portion of the screen, and on landscape it takes the whole screen. I don't know what is the logic exactly. Probably not just a check of landscape because it happens in split screen too (takes entire space). – android developer Sep 28 '19 at 19:58
5

To have a BottomSheetDialogFragment always open fully expanded (also in landscape mode) I do the following.

In onCreateDialog you create the BottomSheetDialog and your view. After adding this view to the BottomSheetDialog you can get the BottomSheetBehavior by using the parent of your view in BottomSheetBehavior.from().

Then in onStart of the BottomSheetDialogFragment you call the BottomSheetBehavior.setState with STATE_EXPANDED.

BottomSheetBehavior mBottomBehavior;

public Dialog onCreateDialog(Bundle savedInstanceState) {
    BottomSheetDialog dialog = new BottomSheetDialog(getContext());
    mBinding = SomeBinding.inflate(LayoutInflater.from(getContext()));
    dialog.setContentView(mBinding.getRoot());
    mBottomBehavior = BottomSheetBehavior.from((View) mBinding.getRoot().getParent());
    return dialog;
}

public void onStart() {
    super.onStart();
    mBottomBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
}

I hope this helps someone.

Pepijn
  • 1,439
  • 17
  • 16
  • This works, but BottomSheet has another bug - in STATE_EXPANDED it removes rounded corners. Classic Googlers - they just can't do anything properly, every single their components has multiple major bugs. Argh Google – qkx Jun 23 '22 at 07:16
2

BottomSheetDialog dialog; BottomSheetBehavior bottomSheetBehavior;

private void createBottomSheet() {
    if (dialog == null){
        dialog = new BottomSheetDialog(this);

        final View view = LayoutInflater.from(this).inflate(R.layout.setting_dialog,
                (ViewGroup)this.getWindow().getDecorView().getRootView(),false);

        dialog.setContentView(view);
        bottomSheetBehavior = BottomSheetBehavior.from((View)view.getParent());
        dialog.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(DialogInterface dialog) {
                bottomSheetBehavior.setPeekHeight(view.getHeight());
            }
        });
    }
}
Tarif Chakder
  • 1,708
  • 1
  • 11
  • 10
2

In androidX, bottom sheet can be expanded as follows:

 BottomSheetDialog bottomSheetDialog = 
       new BottomSheetDialog(requireContext());

  bottomSheetDialog.setContentView(view);

    bottomSheetDialog.show();

    FrameLayout bottomSheet = (FrameLayout)
            bottomSheetDialog.findViewById(com.google.android.material.R.id.design_bottom_sheeet);
    BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
    behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
    behavior.setPeekHeight(0);

This will also be helpful in case of the following error, while getting the bottom sheet behaviour: "the view is not a child of coordinatorlayout"

Sonika
  • 105
  • 7
0

Adding this function to your implementation of the class extending BottomSheetDialogFragment would do the necessary:

@Override
public void onStart() {
    super.onStart();
    BottomSheetBehavior.from((View) requireView().getParent()).setState(BottomSheetBehavior.STATE_EXPANDED);
    //  Opens the bottom sheet in expanded state even in landscape mode
    BottomSheetBehavior.from((View) requireView().getParent()).setSkipCollapsed(true);
    //  Skips the peek view of the bottom sheet
}
Aman Pasricha
  • 61
  • 1
  • 3
0

There is no need for various workarounds. You can just set the height you wish of it. You can choose whatever logic you wish, including of course a percentage of the current height of the screen. Here's an example of setting it to be up to half the height of the screen:

val bottomSheetDialog = BottomSheetDialog(this)
val dialogBinding = BottomSheetDialogBinding.inflate(layoutInflater)
bottomSheetDialog.setContentView(dialogBinding.root)
val screenHeight = resources.displayMetrics.heightPixels 
bottomSheetDialog.behavior.peekHeight = screenHeight / 2
bottomSheetDialog.show()
android developer
  • 114,585
  • 152
  • 739
  • 1,270
0

in my case, i have created bottom sheet's view programmatically. this is my code to present bottomSheet:

private void showButtonSheet() {
        BottomSheetCustomView contentView = new BottomSheetCustomView(getContext());
        BottomSheetDialog bottomSheet = new BottomSheetDialog(getContext(), R.style.BottomSheetDialog);
        bottomSheet.setContentView(contentView);
        BottomSheetBehavior behavior = new BottomSheetBehavior().from((View) contentView.getParent());
        behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
        bottomSheet.setOnShowListener(dialogInterface -> {
            behavior.setPeekHeight(contentView.getHeight());//get the height dynamically
            behavior.setMaxWidth(contentView.getWidth());
        });
        showDialog(bottomSheet);
    }
Sep
  • 147
  • 8