49

I'm using the BottomSheetBehavior from Google recently released AppCompat v23.2. The height of my bottom sheet depends on the content displayed inside of the bottom sheet (similar to the what Google does themselves in their Maps app).

It works fine with the data loaded initially, but my application changes the content displayed during runtime and when this happens the bottom sheet retains at it's old height, which either leads to unused space at the bottom or a cut of view.

Is there any way to inform the bottom sheet layout to recalculate the height used for expanded state (when height of the ViewGroup is set to MATCH_HEIGHT) or any way to manually set the required height?


EDIT: I also tried to manually call invalidate() on the ViewGroup and the parent of it but without any success.

miho
  • 11,765
  • 7
  • 42
  • 85
  • you could do a view height set to wrap_parent and then invalidate view after your content is loaded – Rakeeb Rajbhandari Feb 29 '16 at 09:27
  • view height is set to wrap_parent, but my issue is that once the view is invalidated it doesn't recalculate the height of it and the bottom sheet does just stay as it was before – miho Feb 29 '16 at 09:41
  • Looking at the code of the BottomSheetBehavior it calculates the height in the the onLayoutChild method which is called by CoordinatorLayout. Have you tried invalidating that by calling requestLayout() on it? – appmattus Mar 04 '16 at 01:36
  • 3
    https://code.google.com/p/android/issues/detail?id=205226 here is reported issue with status FutureRelease. So maybe they fixed it one day. – wrozwad Jun 13 '16 at 09:55

10 Answers10

39

I had the same problem with RelativeLayout as my bottom sheet. The height won't be recalculated. I had to resort to setting the height by the new recalculated value and call BottomSheetBehavior.onLayoutChild.

This is my temporary solution:

coordinatorLayout = (CoordinatorLayout)findViewById(R.id.coordinator_layout);
bottomSheet = findViewById(R.id.bottom_sheet);

int accountHeight = accountTextView.getHeight();
accountTextView.setVisibility(View.GONE);

bottomSheet.getLayoutParams().height = bottomSheet.getHeight() - accountHeight;
bottomSheet.requestLayout();
behavior.onLayoutChild(coordinatorLayout, bottomSheet, ViewCompat.LAYOUT_DIRECTION_LTR);
iCantSeeSharp
  • 3,880
  • 4
  • 42
  • 65
Phyrum Tea
  • 2,623
  • 1
  • 19
  • 20
  • 2
    I'd love to see a better solution, but right now this seems the only way to do it. – miho Mar 09 '16 at 10:44
  • 4
    for me `bottomSheet.requestLayout()` was enough after altering the view – Odys May 20 '16 at 01:57
  • 1
    taking back my previous comment, instant-run was fooling with me – Odys May 21 '16 at 20:49
  • If you get the LayoutParams first modify them and set them back then requestLayout will be called internally for you. – koufa Jan 04 '17 at 20:06
  • 1
    I'm using this in onCreateView and setPeekHeight, requestLayout, and setState aren't doing anything. Where should I be putting this code? – Code Wiget Mar 26 '20 at 17:54
16

You can use BottomSheetBehavior#setPeekHeight for that.

FrameLayout bottomSheet = (FrameLayout) findViewById(R.id.bottom_sheet);
BottomSheetBehavior<FrameLayout> behavior = BottomSheetBehavior.from(bottomSheet);
behavior.setPeekHeight(newHeight);

This does not automatically move the bottom sheet to the peek height. You can call BottomSheetBehavior#setState to adjust your bottom sheet to the new peek height.

behavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
Yuichi Araki
  • 3,438
  • 1
  • 19
  • 24
  • 2
    No, what I'm looking for is the expanded size. I'm already using the collapsed state with the peek height. However my peek size is always limited to a preview header and the expanded size depends on how much content is available. (For ex. the description of the objects.) – miho Feb 29 '16 at 09:49
  • 1
    I'm using this in onCreateView and setPeekHeight and setState aren't doing anything. Where should I be putting this code? – Code Wiget Mar 26 '20 at 17:54
4

Though the issue has been resolved in >=24.0.0 support library, if for some reason you still have to use the older version, here is a workaround.

mBottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
        @Override
        public void onStateChanged(@NonNull final View bottomSheet, int newState) {
            bottomSheet.post(new Runnable() {
                @Override
                public void run() {
                    //workaround for the bottomsheet  bug
                    bottomSheet.requestLayout();
                    bottomSheet.invalidate();
                }
            });
        }

        @Override
        public void onSlide(@NonNull View bottomSheet, float slideOffset) {
        }
    });
Ankush Chugh
  • 164
  • 8
3

For bottom sheet dialog fragment, read this: Bottom Sheet Dialog Fragment Expand Full Height

@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
   super.onActivityCreated(savedInstanceState);

   BottomSheetDialog dialog = (BottomSheetDialog) getDialog();

   FrameLayout bottomSheet = dialog.findViewById(com.google.android.material.R.id.design_bottom_sheet);
   BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
   behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
   behavior.setPeekHeight(0);
}
Huy Nguyen
  • 1,032
  • 14
  • 38
1

I faced the same issue, when trying to update the peek height based on its contents, the height from a previous layout was found. This makes sense as the new layout had not taken place yet. By posting on the UI thread the layout height is calculated after the new layout, and another layout request is made to update the bottom sheet to the right height.

void show() {
    setVisibility(View.VISIBLE);
    post(new Runnable() {
        @Override
        public void run() {
            mBottomSheetBehavior.setPeekHeight(findViewById(R.id.sheetPeek).getHeight());
            requestLayout();
        }
    })
}
Harald Unander
  • 111
  • 2
  • 6
1

I've been struggling with a problem similar to yours.

Manually setting the height of the bottomSheet was the solution for me.

Having a view viewA that has the BottomSheetBehaviour and a custom method modifyHeight() that modifies the height of the view:

viewA?.modifyHeight()
viewA?.measure(
            MeasureSpec.makeMeasureSpec(
                width,
                MeasureSpec.EXACTLY
            ),
            MeasureSpec.makeMeasureSpec(
                0,
                MeasureSpec.UNSPECIFIED
            )
        )
val layoutParams = LayoutParams(viewA.measuredWidth, viewA.measuredHeight)
val bottomSheet = BottomSheetBehavior.from(viewA)
layoutParams.behavior = bottomSheet
viewA.layoutParams = layoutParams

My layout would be something like:

    <com.yourpackage.ViewA
    android:id="@+id/viewA"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:behavior_peekHeight="50dp"
    app:layout_behavior="@string/bottom_sheet_behavior" />

It is important to reuse the bottomSheetBehaviour of the old layoutParams because it contains the peekHeight and listeners you may have attached.

Pol
  • 181
  • 2
  • 10
0

I was facing the same issue when I used a recyclerview inside a BottomSheet and the items changed dynamically. As @sosite has mentioned in his comment, the issue is logged and they have fixed it in the latest release. Issue log here

Just update your design support library to version 24.0.0 and check.

0

I've followed @HaraldUnander advice, and it gave me an idea which has actually worked. If you run a thread (couldn't make it work with the post method as him) after the BottomSheetBehavior.state is set up programmatically to STATE_COLLAPSED, then you can already obtain the height of your views and set the peekHeight depending on it's content.

So first you set the BottomSheetBehavior:

BottomSheetBehavior.from(routeCaptionBottomSheet).state = BottomSheetBehavior.STATE_COLLAPSED

And then you set the peekHeight dynamically:

thread {
    activity?.runOnUiThread {
        val dynamicHeight = yourContainerView.height
        BottomSheetBehavior.from(bottomSheetView).peekHeight = dynamicHeight
    }
}

If using Java (I'm using Kotlin with Anko for threads), this could do:

new Thread(new Runnable() {
    public void run() {
        int dynamicHeight = yourContainerView.getHeight();
        BottomSheetBehavior.from(bottomSheetView).setPeekHeight(dynamicHeight);
    }
}).start();
xarlymg89
  • 2,552
  • 2
  • 27
  • 41
0

Below code snippet helped me solve this issue where i am toggling between visibility of different views in layout and height is automatically changing for my bottom sheet.

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    return inflater.inflate(R.layout.your_bottom_sheet_layout, container, false)
}

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    val dialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog
    dialog.setContentView(R.layout.your_bottom_sheet_layout)

    dialog.setOnShowListener {
        val castDialog = it as BottomSheetDialog
        val bottomSheet = castDialog.findViewById<View?>(R.id.design_bottom_sheet)
        val behavior = BottomSheetBehavior.from(bottomSheet)
        behavior.state = BottomSheetBehavior.STATE_EXPANDED
        behavior.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
            override fun onStateChanged(bottomSheet: View, newState: Int) {
                if (newState == BottomSheetBehavior.STATE_DRAGGING) {
                    behavior.state = BottomSheetBehavior.STATE_EXPANDED
                }
            }

            override fun onSlide(bottomSheet: View, slideOffset: Float) {}
        })
    }

    return dialog
}
Arpit
  • 1,052
  • 2
  • 13
  • 25
0

Here is the toggle button click listener I have implement to set pick height of bottom sheet with animation

FrameLayout standardBottomSheet = findViewById(R.id.standardBottomSheet);

BottomSheetBehavior<FrameLayout> bottomSheetBehavior = BottomSheetBehavior.from(standardBottomSheet);    

btnToggleBottomSheet.setOnClickListener(new HPFM_OnSingleClickListener() {
                @Override
                public void onSingleClick(View v) {
                    if (bottomSheetBehavior.getPeekHeight() == 0) {
                        ObjectAnimator.ofInt(bottomSheetBehavior, "peekHeight", 200).setDuration(300).start();
                    }
                    else {
                        ObjectAnimator.ofInt(bottomSheetBehavior, "peekHeight", 0).setDuration(300).start();
                    }
                }
            });
Manthan Patel
  • 1,784
  • 19
  • 23