How i can set Half Expanded state for my BottomSheet
Asked Answered
S

7

19

I have layout with bottom sheet.

<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/coordinator_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

        <com.google.android.material.appbar.CollapsingToolbarLayout
            android:id="@+id/collapsing_toolbar"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:contentScrim="@color/colorPrimary"
            app:layout_scrollFlags="scroll|exitUntilCollapsed" />
    </com.google.android.material.appbar.AppBarLayout>


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

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

</androidx.coordinatorlayout.widget.CoordinatorLayout>

Bottom sheet layout

 <?xml version="1.0" encoding="utf-8"?>
    <androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/bottom_sheet"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white"
        android:clipToPadding="true"
        app:behavior_peekHeight="80dp"
        app:layout_behavior="@string/bottom_sheet_behavior">

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

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/weather_recycler"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="8dp"
                tools:listitem="@layout/item_weather" />

        </LinearLayout>


    </androidx.core.widget.NestedScrollView>

It is necessary for me that my bottom sheet opens first half, and after re-dragging it opens to full screen. How is it done in google maps app. But I have no idea how to do this.

Summerville answered 3/4, 2019 at 0:52 Comment(0)
N
50

It is better to use the framework with its full potential. As official documentation states for method setFitToContents :

Sets whether the height of the expanded sheet is determined by the height of its contents, or if it is expanded in two stages (half the height of the parent container, full height of parent container). Default value is true.

So all you need is set setFitToContent to false with:

behavior = BottomSheetBehavior.from(your_bottom_sheet_xml)
behavior.isFitToContents = false
behavior.halfExpandedRatio = 0.6f

With this 3-line-code the bottom sheet will expand till 60% of the screen at first, and afterwards it will fully expand to 100%.

Hope it helps!

Nonrestrictive answered 12/12, 2019 at 13:26 Comment(4)
halfxpandedRatio?Tilla
@ArnoldBrown available in 1.1.0-alpha05Jonejonell
saved ma day!! Thanks for the insightPoppycock
Great solution. You can also use XML for that: app:behavior_fitToContents="false" and app:behavior_halfExpandedRatio="0.6"Causeway
S
7

Just set BottomSheetBehaivor state to BottomSheetBehavior.STATE_HALF_EXPANDED. Also if you need after full expanding let user again go back to half expanded mode, you need to set peek height to half of window height.

val bottomSheetBehavior = BottomSheetBehavior.from<NestedScrollView>(bottom_sheet)
val metrics = resources.displayMetrics
bottomSheetBehavior.peekHeight = metrics.heightPixels / 2
bottomSheetBehavior.state = BottomSheetBehavior.STATE_HALF_EXPANDED
Sverre answered 24/4, 2019 at 7:18 Comment(0)
A
6

I have tried the @Massab and @HeyAlex but didn't match my desired behavior.

With the following solution in kotlin, if your bottomsheet sliding is near the expanded state, it stays expanded, if is near the half state, stays at half and if it's near collapsed, it stays collapsed:

    val bottomSheet = view.findViewById<View>(R.id.bottom_sheet1)
    val mBottomSheetBehavior = BottomSheetBehavior.from(bottomSheet)
    mBottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
    mBottomSheetBehavior.addBottomSheetCallback(object: BottomSheetBehavior.BottomSheetCallback(){
        override fun onStateChanged(bottomSheet: View, newState: Int) {
        }

        override fun onSlide(bottomSheet: View, slideOffset: Float) {
            val upperState = 0.66
            val lowerState = 0.33
            if (bottomSheetEventsFilterBehavior.state == BottomSheetBehavior.STATE_SETTLING ) {
                if(slideOffset >= upperState){
                    mBottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
                }
                if(slideOffset > lowerState && slideOffset < upperState){
                    mBottomSheetBehavior.state = BottomSheetBehavior.STATE_HALF_EXPANDED
                }
                if(slideOffset <= lowerState){
                    mBottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
                }
            }
        }
    })
Alper answered 15/11, 2019 at 13:57 Comment(2)
this breaks fling behaviorTartarean
Thanks @Kochchy, I agree, I recommend this solution https://mcmap.net/q/628076/-how-i-can-set-half-expanded-state-for-my-bottomsheetAlper
M
3

Although this question has been answered, but just got another way to implement this behavior so sharing for others.

Create a global variable and initialize it with the default state of your BottomSheetBehavior, like

int state = BottomSheetBehavior.STATE_COLLAPSED;

Then, in BottomSheetBehavior.BottomSheetCallback update your state variable to the current state

and in BottomSheetBehavior.STATE_DRAGGING, if state is not half expanded,

set the state to BottomSheetBehavior.STATE_HALF_EXPANDED

sheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
        @Override
        public void onStateChanged(@NonNull View view, int i) {
            switch (i) {
                case BottomSheetBehavior.STATE_COLLAPSED:
                    state = BottomSheetBehavior.STATE_COLLAPSED;
                    binder.imgRefresh.setVisibility(View.GONE);
                    break;
                case BottomSheetBehavior.STATE_EXPANDED:
                    binder.imgRefresh.setVisibility(View.VISIBLE);
                    state = BottomSheetBehavior.STATE_EXPANDED;
                    break;
                case BottomSheetBehavior.STATE_DRAGGING:
                    if (state != BottomSheetBehavior.STATE_HALF_EXPANDED) {
                        sheetBehavior.setState(BottomSheetBehavior.STATE_HALF_EXPANDED);
                    }
                    break;
                case BottomSheetBehavior.STATE_HALF_EXPANDED:
                    state = BottomSheetBehavior.STATE_HALF_EXPANDED;
                    break;
            }
        }

        @Override
        public void onSlide(@NonNull View view, float v) {
            binder.viewExtender.setAlpha(1 - v);
        }
    });

This will make your BottomSheet to take three steps , i.e., Collapsed, Half Expanded, Expanded.

Hope it can help someone!

Misdeed answered 19/9, 2019 at 17:37 Comment(1)
what about for the onSlide. i want the half way position to be treated as if the card has already slided to the top. but what i see happening is that the onSlide offset is still a percentage of the total screen.Countable
T
3
class BottomSheetFragment : BottomSheetDialogFragment() {
    /* inside of your Bottom Sheet Dialog Fragment */
    override fun onStart() {
        super.onStart()
        BottomSheetBehavior.from(requireView().parent as View).apply {
        state = BottomSheetBehavior.STATE_HALF_EXPANDED
       }
    }
}
Tripoli answered 12/11, 2020 at 13:40 Comment(0)
U
0

Use this block in onCreateView before returning root view

dialog!!.setOnShowListener { dialog ->
        val d = dialog as BottomSheetDialog
        BottomSheetBehavior.from(requireView().parent as View).apply {
                state = BottomSheetBehavior.STATE_EXPANDED
            }
    }
Unhook answered 20/4, 2022 at 8:2 Comment(0)
H
0

You can add:

MySheet() {
    AnimatedContent(targetState = isExpanded, label = "") { targetState ->
        when (targetState) {
            true -> Box(modifier = Modifier.fillMaxSize())
            false -> Box(modifier = Modifier.height(0.dp))
        }
    }
}

Inside the BottomSheetScaffold:

var isExpandedValue by remember { mutableStateOf(false) }

BottomSheetScaffold(
    sheetContent = {
        MySheet(isExpanded = isExpandedValue) { value -> isExpandedValue = value }
    }
) {
}

And handle the isExpandedValue as in a callback function. In this way the content of the bottom sheet will be animated and expanded fully. You can adapt this code to expand only half-way through.

Hitormiss answered 31/5 at 9:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.