Android - How to disable STATE_HALF_EXPANDED state of a bottom sheet
Asked Answered
P

10

41

I have a bottom sheet that should go between 2 states, STATE_COLLAPSED and STATE_EXPANDED when it's collapsed the hight should be 200dp and when expanded it will be full screen.

So I'm setting the BottomSheetBehavior with

isFitToContents = false
peekHeight = 200dp

and I'm forced to set a value in halfExpandedRatio otherwise when at STATE_HALF_EXPANDED the bottom sheet will take half of the screen.

I'm working w/ com.google.android.material:material:1.1.0-rc01

Is there a way to disable the STATE_HALF_EXPANDED state?

Or I should actually set skipCollapsed=true, figure out in terms of ratio what 200dp means and work with STATE_HALF_EXPANDED and STATE_EXPANDED instead of STATE_COLLAPSED and STATE_EXPANDED

Pelvic answered 26/12, 2019 at 11:43 Comment(7)
please provide more details such as, how the bottom sheet look like.Stylograph
@UD..I don't think that the bottom sheet content is relevant in this case. This is a more general question, is it possible to disable one of the bottom sheet statesPelvic
For my use case, it's seems that setting halfExpandedRatio=0.25f and peekHeight = 200dp and then treating STATE_COLLAPSED and STATE_HALF_EXPANDED as if they are the same state solves the issue. Keeping the question open in case there are other ideas.Pelvic
you can follow this link , it will help androidhive.info/2017/12/android-working-with-bottom-sheetStylograph
Be sure to accept one of these answers, if one meets the objectives set out in your question!Cruelty
@Cruelty - I accepted an answer, thanks for your helpPelvic
The problem lies in the isFitToContents flag, you must set it to true to skip the STATE_HALF_EXPANDED state.Bastardy
S
22

Update: As mentioned in another answer to this post, Material version 1.1.0 and, I presume, subsequent versions of the library have a property skipCollapsed that will work as the OP requested. If you are using any of these libraries, that would be the preferred solution.



The value of the half expanded ratio must be set to some value between 0 and 1 exclusive, so set this value to some very low number that is certain to be less than your peek height, say "0.0001f". With this value you should not even see the STATE_HALF_EXPANDED state. The states will fluctuate between STATE_EXPANDED and STATE_COLLAPSED.


Alternate solution

The solution above works and effectively disables the STATE_HALF_EXPANDED state, but it is hackish (IMO) and may break in the future. For instance, what if a reasonable value for the half expanded ratio which is somewhere between the peek height and the full height is enforced? That would be trouble.

The requirements as stated by the OP is that the bottom sheet should transition between the peek height and the full height. There is no problem with the peek height, but the OP specifies isFitToContents = false to get to the full height. (I assume that his bottom sheet may be shorter then the available space.)

Unfortunately, when isFitToContents == false an additional "half-height" behavior is introduced that the OP wants to avoid and therefore the question.

In addition to the "half-height" behavior another behavior is introduced which is the "expanded offset." The expanded offset specifies how far down from full-screen the bottom sheet will stop. A value of 100f, for instance, will leave a 100px border at the top of the bottom sheet when fully expanded. The default for the expanded offset is zero.

I am not aware of any behaviors that isFitToContents == false introduces other than those mentioned above.

So, given these requirements, can we fashion a bottom sheet that moves between the peek height and the full height while specifying isFitToContents == true thus avoiding the "half height" problem? There is no requirement for a non-zero expanded offset, so we don't have to worry about that.

Here is a short demo app demonstrating that we can meet these requirements with the right bottom sheet structure:

enter image description here

MainActivity5.kt

class MainActivity5 : BaseActivity() {  
    override fun onCreate(savedInstanceState: Bundle?) {  
        super.onCreate(savedInstanceState)  
        setContentView(R.layout.activity_main5)  
  
        val bottomSheet = findViewById<LinearLayout>(R.id.bottom_sheet)  
        val sheetBehavior: BottomSheetBehavior<LinearLayout> = BottomSheetBehavior.from(bottomSheet)  
        sheetBehavior.isFitToContents = true // the default  
  sheetBehavior.peekHeight = 200  
  
  // Log the states the bottom sheet passes through.  
  sheetBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {  
            override fun onStateChanged(bottomSheet: View, newState: Int) {  
                Log.d("MainActivity", "<<<< $newState = ${translateSheetState(newState)}")  
            }  
  
            override fun onSlide(bottomSheet: View, slideOffset: Float) {}  
        })  
    }  
}

BaseActivity.kt

open class BaseActivity : AppCompatActivity() {  
  
    protected fun translateSheetState(state: Int): String {  
        return when (state) {  
            BottomSheetBehavior.STATE_COLLAPSED -> "STATE_COLLAPSED"  
  BottomSheetBehavior.STATE_DRAGGING -> "STATE_DRAGGING"  
  BottomSheetBehavior.STATE_EXPANDED -> "STATE_EXPANDED"  
  BottomSheetBehavior.STATE_HALF_EXPANDED -> "STATE_HALF_EXPANDED"  
  BottomSheetBehavior.STATE_HIDDEN -> "STATE_HIDDEN"  
  BottomSheetBehavior.STATE_SETTLING -> "STATE_SETTLING"  
  else -> "Unknown state: $state"  
  }  
    }  
}

activity_main5.xml

<androidx.coordinatorlayout.widget.CoordinatorLayout 
    android:id="@+id/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/bottom_sheet"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/holo_orange_light"
        android:orientation="vertical"
        android:scrollbars="none"
        app:layout_behavior="@string/bottom_sheet_behavior">

        <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="16dp"
            android:text="@string/short_text"
            android:textSize="16sp" />

    </LinearLayout>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

If we have a long bottom sheet then the following structure works to scroll it:

activity_main6.xml

<androidx.coordinatorlayout.widget.CoordinatorLayout 
    android:id="@+id/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/bottom_sheet"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/holo_orange_light"
        android:orientation="vertical"
        android:scrollbars="none"
        app:layout_behavior="@string/bottom_sheet_behavior">

        <androidx.core.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:id="@+id/tv"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="16dp"
                android:text="@string/long_text"
                android:textSize="16sp" />
        </androidx.core.widget.NestedScrollView>
    </LinearLayout>

</androidx.coordinatorlayout.widget.CoordinatorLayout>
Superstition answered 2/1, 2020 at 14:35 Comment(3)
regarding your initial response - I saw that even if I set half expanded ratio, a very slim hint of the bottom sheet is still visible. And anyway, this isn't the behavior that I'm looking for. as you mentioned in "Alternate solution" - "the bottom sheet should transition between the peek height and the full height"Pelvic
your "Alternate solution" seems to be working and it's the solution I needed, my initial testing showed that i need to use isFitToContents = false, but testing now w/ isFitToContents = true works okPelvic
@NoaDrach If the bottom sheet is hideable then there would be at least 1px showing at the bottom due to how the bottom sheet offset is calculated. I wasn't thinking that the sheet would be hidden but, with 1px showing, the sheet could just be forced to be hidden sheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN when the half expanded state is reached, but that is getting a little complicated. The alternate solution is better.Superstition
J
34

I am using material library version 1.1.0 and the BottomSheetBehavior class has this property skipCollapsed, if you set it to true the bottom sheet will skip the STATE_HALF_EXPANDED.

Here is my code:

class FilterBottomSheet : BottomSheetDialogFragment() {
    private lateinit var behavior: BottomSheetBehavior<View>

    override fun onStart() {
        super.onStart()

        behavior.state = BottomSheetBehavior.STATE_EXPANDED
    }

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

        val view = View.inflate(requireContext(), R.layout.filter_bottom_sheet, null)

        val params = view.root.layoutParams as LinearLayout.LayoutParams?
        params?.height = getScreenHeight()
        view.root.layoutParams = params

        dialog.setContentView(view)

        behavior = BottomSheetBehavior.from(view.parent as View)
        behavior.skipCollapsed = true

        return dialog
    }

    private fun getScreenHeight(): Int = Resources.getSystem().displayMetrics.heightPixels
}
Joachima answered 3/6, 2020 at 17:41 Comment(2)
The only answer that has any sense to it, is not a weird trick, and the best part, it works.Finite
The behavior.state = BottomSheetBehavior.STATE_EXPANDED in onStart worked for me.Mulvaney
S
22

Update: As mentioned in another answer to this post, Material version 1.1.0 and, I presume, subsequent versions of the library have a property skipCollapsed that will work as the OP requested. If you are using any of these libraries, that would be the preferred solution.



The value of the half expanded ratio must be set to some value between 0 and 1 exclusive, so set this value to some very low number that is certain to be less than your peek height, say "0.0001f". With this value you should not even see the STATE_HALF_EXPANDED state. The states will fluctuate between STATE_EXPANDED and STATE_COLLAPSED.


Alternate solution

The solution above works and effectively disables the STATE_HALF_EXPANDED state, but it is hackish (IMO) and may break in the future. For instance, what if a reasonable value for the half expanded ratio which is somewhere between the peek height and the full height is enforced? That would be trouble.

The requirements as stated by the OP is that the bottom sheet should transition between the peek height and the full height. There is no problem with the peek height, but the OP specifies isFitToContents = false to get to the full height. (I assume that his bottom sheet may be shorter then the available space.)

Unfortunately, when isFitToContents == false an additional "half-height" behavior is introduced that the OP wants to avoid and therefore the question.

In addition to the "half-height" behavior another behavior is introduced which is the "expanded offset." The expanded offset specifies how far down from full-screen the bottom sheet will stop. A value of 100f, for instance, will leave a 100px border at the top of the bottom sheet when fully expanded. The default for the expanded offset is zero.

I am not aware of any behaviors that isFitToContents == false introduces other than those mentioned above.

So, given these requirements, can we fashion a bottom sheet that moves between the peek height and the full height while specifying isFitToContents == true thus avoiding the "half height" problem? There is no requirement for a non-zero expanded offset, so we don't have to worry about that.

Here is a short demo app demonstrating that we can meet these requirements with the right bottom sheet structure:

enter image description here

MainActivity5.kt

class MainActivity5 : BaseActivity() {  
    override fun onCreate(savedInstanceState: Bundle?) {  
        super.onCreate(savedInstanceState)  
        setContentView(R.layout.activity_main5)  
  
        val bottomSheet = findViewById<LinearLayout>(R.id.bottom_sheet)  
        val sheetBehavior: BottomSheetBehavior<LinearLayout> = BottomSheetBehavior.from(bottomSheet)  
        sheetBehavior.isFitToContents = true // the default  
  sheetBehavior.peekHeight = 200  
  
  // Log the states the bottom sheet passes through.  
  sheetBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {  
            override fun onStateChanged(bottomSheet: View, newState: Int) {  
                Log.d("MainActivity", "<<<< $newState = ${translateSheetState(newState)}")  
            }  
  
            override fun onSlide(bottomSheet: View, slideOffset: Float) {}  
        })  
    }  
}

BaseActivity.kt

open class BaseActivity : AppCompatActivity() {  
  
    protected fun translateSheetState(state: Int): String {  
        return when (state) {  
            BottomSheetBehavior.STATE_COLLAPSED -> "STATE_COLLAPSED"  
  BottomSheetBehavior.STATE_DRAGGING -> "STATE_DRAGGING"  
  BottomSheetBehavior.STATE_EXPANDED -> "STATE_EXPANDED"  
  BottomSheetBehavior.STATE_HALF_EXPANDED -> "STATE_HALF_EXPANDED"  
  BottomSheetBehavior.STATE_HIDDEN -> "STATE_HIDDEN"  
  BottomSheetBehavior.STATE_SETTLING -> "STATE_SETTLING"  
  else -> "Unknown state: $state"  
  }  
    }  
}

activity_main5.xml

<androidx.coordinatorlayout.widget.CoordinatorLayout 
    android:id="@+id/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/bottom_sheet"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/holo_orange_light"
        android:orientation="vertical"
        android:scrollbars="none"
        app:layout_behavior="@string/bottom_sheet_behavior">

        <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="16dp"
            android:text="@string/short_text"
            android:textSize="16sp" />

    </LinearLayout>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

If we have a long bottom sheet then the following structure works to scroll it:

activity_main6.xml

<androidx.coordinatorlayout.widget.CoordinatorLayout 
    android:id="@+id/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/bottom_sheet"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/holo_orange_light"
        android:orientation="vertical"
        android:scrollbars="none"
        app:layout_behavior="@string/bottom_sheet_behavior">

        <androidx.core.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:id="@+id/tv"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:padding="16dp"
                android:text="@string/long_text"
                android:textSize="16sp" />
        </androidx.core.widget.NestedScrollView>
    </LinearLayout>

</androidx.coordinatorlayout.widget.CoordinatorLayout>
Superstition answered 2/1, 2020 at 14:35 Comment(3)
regarding your initial response - I saw that even if I set half expanded ratio, a very slim hint of the bottom sheet is still visible. And anyway, this isn't the behavior that I'm looking for. as you mentioned in "Alternate solution" - "the bottom sheet should transition between the peek height and the full height"Pelvic
your "Alternate solution" seems to be working and it's the solution I needed, my initial testing showed that i need to use isFitToContents = false, but testing now w/ isFitToContents = true works okPelvic
@NoaDrach If the bottom sheet is hideable then there would be at least 1px showing at the bottom due to how the bottom sheet offset is calculated. I wasn't thinking that the sheet would be hidden but, with 1px showing, the sheet could just be forced to be hidden sheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN when the half expanded state is reached, but that is getting a little complicated. The alternate solution is better.Superstition
T
4

In Kotlin, to disable the STATE_HALF_EXPANDED you can use a similar solution by accessing the BottomSheetDialogFragment's BottomSheetBehavior and setting skipCollapsed = true.

To achieve that, you can override onViewCreated method of your implementation of `BottomSheetDialogFragment and access the behavior object of the dialog.

Example (code goes inside your BottomSheetDialogFragment implementation):

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        dialog?.let {
            val sheet = it as BottomSheetDialog
            sheet.behavior.state = BottomSheetBehavior.STATE_EXPANDED
            sheet.behavior.skipCollapsed = true
        }
    }
Tendon answered 6/12, 2021 at 11:9 Comment(1)
Your solution does not allow the bottom sheet go to the collapsed state after we switch to the expanded state.Bastardy
E
2

try setting a addBottomSheetCallback on your BottomSheetBehavior, and when you detect a STATE_HALF_EXPANDED state, call setState(STATE_HIDDEN) so whenever the bottom sheet tries to reach the halfway state, it'll just close.

Ehman answered 30/12, 2019 at 12:51 Comment(3)
nice idea, in my case I would set the state to STATE_COLLAPSED and not STATE_HIDDEN. But I tried to implement it and the transition from STATE_HALF_EXPANDED to STATE_COLLAPSED feels clunky. The transition between states is animated, so you see the bottom sheet stopping at STATE_HALF_EXPANDED and then it moves to STATE_COLLAPSEDPelvic
Could you combine this with a halfExpandedRatio of 0?Shay
@Shay - 2 issues here - 1. halfExpandedRatio must be above 0 2. setting it to a very low value will cause it to me minimized almost completely (half expanded state), before being snapped to the collapsed state. I thought of combining this suggestion w/ my working solution of halfExpandedState=0.25f, b/c then the transition between states won't be this obvious. But, I'm not sure that will be a big change compared to what I already havePelvic
F
1

Try setting BottomSheetBehavior.setHalfExpandedRatio(0f). There is not much else which would affect STATE_HALF_EXPANDED, unless explicitly setting the state with .setState(). It should also be possible to create a custom Behavior, which extends CoordinatorLayout.Behavior<View> and does not have STATE_HALF_EXPANDED. eg. Intercepting everything with CoordinatorLayout Behaviors.

Fragrant answered 4/1, 2020 at 10:28 Comment(1)
That won't work - halfExpandedRatio needs to be between 0 and 1 exclusively.Coincide
S
1

enter image description here

if you want to try above like in image , you can follow below code, may it will help you!!!

public class CollectionsBottomSheet extends BottomSheetDialogFragment {
    private BottomSheetBehavior mBehavior;


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

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        BottomSheetDialog dialog = (BottomSheetDialog) super.onCreateDialog(savedInstanceState);
        View view = View.inflate(getContext(), R.layout.collections_layout, null);
        LinearLayout linearLayout = view.findViewById(R.id.root);
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) linearLayout.getLayoutParams();
        params.height = getScreenHeight();
        linearLayout.setLayoutParams(params);
        dialog.setContentView(view);
        mBehavior = BottomSheetBehavior.from((View) view.getParent());
        return dialog;

    }

    @Override
    public void onStart() {
        super.onStart();
        mBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
    }

    public static int getScreenHeight() {
        return Resources.getSystem().getDisplayMetrics().heightPixels;
    }
}



xml 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/bottom_sheet"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    android:fitsSystemWindows="true">


    <LinearLayout
        android:id="@+id/root"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/filter_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:drawableStart="@drawable/ic_cancel"
                android:drawableLeft="@drawable/ic_cancel"
                android:drawablePadding="30dp"
                android:gravity="center_vertical"
                android:padding="12dp"
                android:text="Filters"
                android:textColor="@color/black"
                android:textSize="18sp" />

            <View
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_weight="1" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginEnd="10dp"
                android:layout_marginRight="10dp"
                android:padding="5dp"
                android:text="Reset ALL"
                android:textColor="#6f6f6f"
                android:textSize="12sp" />

        </LinearLayout>

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="#d8dbdb" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/sort_background"
            android:drawableStart="@drawable/ic_star"
            android:drawableLeft="@drawable/ic_star"
            android:drawablePadding="15dp"
            android:padding="15dp"
            android:text="GUEST RATINGS"
            android:textColor="#6f6f6f"
            android:textSize="16sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/sort_background"
            android:drawableStart="@drawable/ic_money"
            android:drawableLeft="@drawable/ic_money"
            android:drawablePadding="15dp"
            android:padding="15dp"
            android:text="PRICE RANGE"
            android:textColor="#6f6f6f"
            android:textSize="16sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/sort_background"
            android:drawableStart="@drawable/ic_loan"
            android:drawableLeft="@drawable/ic_star"
            android:drawablePadding="15dp"
            android:padding="15dp"
            android:text="PAY AT HOTEL"
            android:textColor="#6f6f6f"
            android:textSize="16sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/sort_background"
            android:drawableStart="@drawable/ic_folder"
            android:drawableLeft="@drawable/ic_folder"
            android:drawablePadding="15dp"
            android:padding="15dp"
            android:text="COLLECTIONS"
            android:textColor="#6f6f6f"
            android:textSize="16sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/sort_background"
            android:drawableStart="@drawable/ic_perm_identity_black_24dp"
            android:drawableLeft="@drawable/ic_perm_identity_black_24dp"
            android:drawablePadding="15dp"
            android:padding="15dp"
            android:text="FACILITIES"
            android:textColor="#6f6f6f"
            android:textSize="16sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/sort_background"
            android:drawableStart="@drawable/ic_apartment"
            android:drawableLeft="@drawable/ic_apartment"
            android:drawablePadding="15dp"
            android:padding="15dp"
            android:text="CATEGORIES"
            android:textColor="#6f6f6f"
            android:textSize="16sp" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/sort_background"
            android:drawableStart="@drawable/ic_hotel_building"
            android:drawableLeft="@drawable/ic_hotel_building"
            android:drawablePadding="15dp"
            android:padding="15dp"
            android:text="ACCOMMODATION TYPE"
            android:textColor="#6f6f6f"
            android:textSize="16sp" />

    </LinearLayout>


</LinearLayout>
Stylograph answered 4/1, 2020 at 15:0 Comment(0)
D
1

I tried different ways, but no technique worked perfect. I tried intercepting events in BottomSheetBehavior.BottomSheetCallback {} and called dismiss() based on custom logic but it caused a jerk.

So, at last, In my BottomSheetDialogFragment I added bottomSheetBehavior.isDraggable = false and this caused Dragging of bottom sheet by touch And, I handled the dismissal of dialog on my own. on empty area dialog anyways get dismissed.

Note that, The bottom sheet still expands with animation. That' really great!

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val dialog = super.onCreateDialog(savedInstanceState)

    dialog.setOnShowListener {
        val bottomSheetDialog = it as BottomSheetDialog
        val bottomSheet =
            bottomSheetDialog.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet)
                ?: return@setOnShowListener

        //Making background to transparent to avoid white background to given space margin.
        bottomSheet.setBackgroundColor(ContextCompat.getColor(context!!, R.color.transparent))

        val inflatedView = fragmentProfileDialogBinding.root
        val parent = inflatedView.parent as View

        val bottomSheetBehavior = BottomSheetBehavior.from(parent)
        bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
        bottomSheetBehavior.isDraggable = false
    }

    return dialog
}
Dewayne answered 11/2, 2020 at 7:18 Comment(0)
C
1

In line with other answers, you can disable this in XML by setting app:behavior_skipCollapsed="true" on the bottom sheet. For example, in my use case:

<androidx.constraintlayout.widget.ConstraintLayout
                    android:id="@+id/bottom_sheet"
                    xmlns:android="http://schemas.android.com/apk/res/android"
                    xmlns:app="http://schemas.android.com/apk/res-auto"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    app:behavior_skipCollapsed="true"
                    app:behavior_hideable="true"
                    app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
                    android:background="@color/colorTransparent">
Concertante answered 13/10, 2021 at 13:53 Comment(0)
P
0

I had a similar use case where the layout had to be one-third of the height. I tried the following and it worked great.

<androidx.coordinatorlayout.widget.CoordinatorLayout 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_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/grey"
    android:clickable="true">

    <LinearLayout
        android:id="@+id/bottom_sheet"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/rounded_bottom_sheet_background"
        android:orientation="vertical"
        app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycler"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

I had to change these dynamically so I set the following on bottom sheet but you can do this in xml just as well:

bottomSheet.setPeekHeight(200);// 200px
bottomSheet.setHideable(false);

For dismissing, I added animation to my fragment using the following function:

fragmentTransaction.setCustomAnimations(
                    R.anim.fade_in,
                    R.anim.fade_out,
                    R.anim.fade_in,
                    R.anim.fade_out)

Hope this helps

Pernas answered 3/1, 2020 at 6:6 Comment(0)
V
0

As of more recent versions of MDC, you could alternatively avoid using the aforementioned workarounds by creating a custom BottomSheetBehavior that overrides the following methods:

class SkipHalfExpandedBottomSheetBehavior<V: View>(context: Context, attributeSet: AttributeSet?) :
    BottomSheetBehavior<V>(context, attributeSet) {
    override fun shouldSkipHalfExpandedStateWhenDragging() = true
    override fun shouldExpandOnUpwardDrag(
        dragDurationMillis: Long,
        yPositionPercentage: Float
    ) = true
}

This seems to work everywhere except for nested scrolling. For that, you would need to patch onStopNestedScroll in BottomSheetBehavior like this:

@Override
public void onStopNestedScroll(
    @NonNull CoordinatorLayout coordinatorLayout,
    @NonNull V child,
    @NonNull View target,
    int type) {
  if (child.getTop() == getExpandedOffset()) {
    setStateInternal(STATE_EXPANDED);
    return;
  }
  if (isNestedScrollingCheckEnabled()
      && (nestedScrollingChildRef == null
          || target != nestedScrollingChildRef.get()
          || !nestedScrolled)) {
    return;
  }
  @StableState int targetState;
  if (lastNestedScrollDy > 0) {
    if (fitToContents) {
      targetState = STATE_EXPANDED;
    } else {
      int currentTop = child.getTop();
      if (currentTop < halfExpandedOffset) {
        targetState = STATE_EXPANDED;
      } else {
        if (shouldSkipHalfExpandedStateWhenDragging()) {
          targetState = STATE_COLLAPSED;
        } else {
          targetState = STATE_HALF_EXPANDED;
        }
      }
    }
  } else if (hideable && shouldHide(child, getYVelocity())) {
    targetState = STATE_HIDDEN;
  } else if (lastNestedScrollDy == 0) {
    int currentTop = child.getTop();
    if (fitToContents) {
      if (Math.abs(currentTop - fitToContentsOffset) < Math.abs(currentTop - collapsedOffset)) {
        targetState = STATE_EXPANDED;
      } else {
        targetState = STATE_COLLAPSED;
      }
    } else {
      if (currentTop < halfExpandedOffset) {
        if (currentTop < Math.abs(currentTop - collapsedOffset)) {
          targetState = STATE_EXPANDED;
        } else {
          if (shouldSkipHalfExpandedStateWhenDragging()) {
            targetState = STATE_COLLAPSED;
          } else {
            targetState = STATE_HALF_EXPANDED;
          }
        }
      } else {
        if (shouldSkipHalfExpandedStateWhenDragging()) {
          targetState = STATE_COLLAPSED;
        } else {
          if (Math.abs(currentTop - halfExpandedOffset) < Math.abs(currentTop - collapsedOffset)) {
            targetState = STATE_HALF_EXPANDED;
          } else {
            targetState = STATE_COLLAPSED;
          }
        }
      }
    }
  } else {
    if (fitToContents) {
      targetState = STATE_COLLAPSED;
    } else {
      // Settle to nearest height.
      int currentTop = child.getTop();
      if (shouldSkipHalfExpandedStateWhenDragging()) {
        targetState = STATE_COLLAPSED;
      } else {
        if (Math.abs(currentTop - halfExpandedOffset) < Math.abs(currentTop - collapsedOffset)) {
          targetState = STATE_HALF_EXPANDED;
        } else {
          targetState = STATE_COLLAPSED;
        }
      }
    }
  }
  startSettling(child, targetState, false);
  nestedScrolled = false;
}
Vinia answered 29/7, 2022 at 16:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.