Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set Bottom Sheet peek height to show only certain parts initially?

Let's say that my Bottom Sheet has lines of widgets like the following. If I want to show only the first two lines (i.e., the first two LinearLayouts) initially, but not the rest of the widgets below. I do not want those to be seen initially. How can I set the correct peek height? Hard-coding app:behavior_peekHeight probably would not work, so I would need to set it programatically, but how to calculate the height?

Or is there a more recommended way to get the same result? I mean, if I test Google Maps, long pressing a location first shows only the title part as the bottom sheet, but when I try to scroll up the bottom sheet, it feels as if the title part (which might not have been a real bottom sheet) is replaced by a real bottom sheet that contains all the elements. If my explanation is not enough, please try Google Maps yourself.

<android.support.v4.widget.NestedScrollView
    android:id="@+id/bottom_sheet"
    app:layout_behavior="android.support.design.widget.BottomSheetBehavior"
    android:scrollbars="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <TextView/>
            <android.support.v7.widget.AppCompatSpinner/>
        </LinearLayout>
        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <TextView/>
            <TextView/>
        </LinearLayout>
        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <TextView/>
            <TextView/>
        </LinearLayout>
        <android.support.v7.widget.RecyclerView/>
    </LinearLayout>
</android.support.v4.widget.NestedScrollView>
like image 772
Damn Vegetables Avatar asked Dec 01 '22 10:12

Damn Vegetables


2 Answers

I would solve this by using a ViewTreeObserver.OnGlobalLayoutListener to wait for your bottom sheet to be laid out, and then calling BottomSheetBehavior.setPeekHeight() with the y-coordinate of the first view you don't want to see.

private BottomSheetBehavior<View> behavior;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    View bottomSheet = findViewById(R.id.bottomSheet);
    behavior = BottomSheetBehavior.from(bottomSheet);

    final LinearLayout inner = findViewById(R.id.inner);
    inner.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            inner.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            View hidden = inner.getChildAt(2);
            behavior.setPeekHeight(hidden.getTop());
        }
    });
}

In this case, my bottom sheet is a NestedScrollView holding a LinearLayout that holds many TextViews. By setting the peek height to be the top of the third TextView (obtained by getChildAt(2)), my bottom sheet winds up showing exactly two TextViews while collapsed.

enter image description here enter image description here

like image 184
Ben P. Avatar answered Mar 29 '23 23:03

Ben P.


Customized @Ben P.'s answer to target a view id as a reference of the peekHeight and made a function:

/**
 * Gets the bottom part of the target view and sets it as the peek height of the specified @{BottomSheetBehavior}
 *
 * @param layout - layout of the bottom sheet.
 * @param targetViewId - id of the target view. Must be a view inside the 'layout' param.
 * @param behavior - bottom sheet behavior recipient.
 */
private fun <T : ViewGroup> getViewBottomHeight(layout: ViewGroup,
                                                targetViewId: Int,
                                                behavior: BottomSheetBehavior<T>) {
    layout.apply {
        viewTreeObserver.addOnGlobalLayoutListener(
                object : ViewTreeObserver.OnGlobalLayoutListener {
                    override fun onGlobalLayout() {
                        viewTreeObserver.removeOnGlobalLayoutListener(this)
                        behavior.peekHeight = findViewById<View>(targetViewId).bottom
                    }
                })
    }
}

In our use case, we needed to target the bottom part of the view, so we set it that way. It can be adjusted depending on the use-case.

like image 41
AL. Avatar answered Mar 30 '23 01:03

AL.