Android - How to disable STATE_HALF_EXPANDED state of a bottom sheet
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
}
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:
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>