Android - 如何禁用底部 STATE_HALF_EXPANDED 状态 sheet

Android - How to disable STATE_HALF_EXPANDED state of a bottom sheet

我有一个底部 sheet 应该在两个状态之间,STATE_COLLAPSEDSTATE_EXPANDED 折叠时高度应为 200dp,展开时为全屏。

所以我将 BottomSheetBehavior 设置为

isFitToContents = false
peekHeight = 200dp

我不得不在 halfExpandedRatio 中设置一个值,否则在 STATE_HALF_EXPANDED 时底部 sheet 将占据屏幕的一半。

我正在与 com.google.android.material:material:1.1.0-rc01

一起工作

有没有办法禁用 STATE_HALF_EXPANDED 状态?

或者我实际上应该设置 skipCollapsed=true,根据比率计算出 200dp 的含义并使用 STATE_HALF_EXPANDEDSTATE_EXPANDED 而不是 STATE_COLLAPSEDSTATE_EXPANDED

尝试在您的 BottomSheetBehavior 上设置 addBottomSheetCallback,当您检测到 STATE_HALF_EXPANDED 状态时,调用 setState(STATE_HIDDEN) 因此每当底部 sheet 尝试到达中途状态,它就会关闭。

半膨胀率的值必须设置为0到1之间的某个值不包含,因此将此值设置为一定小于的某个非常低的数字你的窥视高度,说“0.0001f”。使用此值,您甚至不应该看到 STATE_HALF_EXPANDED 状态。状态将在 STATE_EXPANDEDSTATE_COLLAPSED 之间波动。


备选方案

上面的解决方案有效并有效地禁用了 STATE_HALF_EXPANDED 状态,但它是 hackish (IMO) 并且将来可能会崩溃。例如,如果强制执行介于 peek 高度和全高之间的半扩展比率的合理值怎么办?那就麻烦了。

OP 规定的要求是底​​部 sheet 应该在窥视高度和全高之间过渡。 peek 高度没有问题,但 OP 指定 isFitToContents = false 以达到全高。 (我假设他的底部 sheet 可能比可用的 space 短。)

不幸的是,当 isFitToContents == false 引入了一个额外的 "half-height" 行为时,OP 想要避免,因此这个问题。

除了 "half-height" 行为之外,还引入了另一种行为,即 "expanded offset." 扩展偏移量指定底部 sheet 将停止在全屏下方多远。例如,100f 的值将在完全展开时在底部 sheet 的顶部留下 100px 边框。扩展偏移量的默认值为零。

我不知道 isFitToContents == false 引入了除上述行为之外的任何行为。

那么,鉴于这些要求,我们能否在指定 isFitToContents == true 的同时设计一个在窥视高度和全高之间移动的底部 sheet,从而避免 "half height" 问题?不需要非零扩展偏移量,因此我们不必担心。

这是一个简短的演示应用程序,演示了我们可以使用右下 sheet 结构满足这些要求:

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>

如果我们有一个长底部 sheet 那么下面的结构可以滚动它:

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>

我有一个类似的用例,其中布局必须是高度的三分之一。我尝试了以下方法,效果很好。

<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>

我不得不动态更改这些,所以我在底部设置了以下内容 sheet 但你也可以在 xml 中这样做:

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

为了解散,我使用以下函数向我的片段添加了动画:

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

希望对您有所帮助

尝试设置 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.

如果你想尝试上面的图片,你可以按照下面的代码,可能会对你有所帮助!!!

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>

我尝试了不同的方法,但没有一种技术是完美的。我尝试拦截 BottomSheetBehavior.BottomSheetCallback {} 中的事件并根据自定义逻辑调用 dismiss() 但它导致了一个问题。

所以,最后,在我的 BottomSheetDialogFragment 中,我添加了 bottomSheetBehavior.isDraggable = false,这导致通过触摸拖动底部 sheet 而且,我我自己处理了对话的解雇。在空白区域对话框中无论如何都会被解雇。

请注意,底部 sheet 仍会随着动画展开。太棒了!

覆盖乐趣 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
}

我正在使用 material 库版本 1.1.0 并且 BottomSheetBehavior class 有这个 属性 skipCollapsed,如果你将它设置为 true 底部 sheet 将跳过 STATE_HALF_EXPANDED.

这是我的代码:

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
}

根据其他答案,您可以通过在底部 sheet 上设置 app:behavior_skipCollapsed="true" 在 XML 中禁用此功能。例如,在我的用例中:

<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">

在 Kotlin 中,要禁用 STATE_HALF_EXPANDED,您可以使用类似的解决方案,方法是访问 BottomSheetDialogFragmentBottomSheetBehavior 并设置 skipCollapsed = true.

为此,您可以覆盖 onViewCreated `BottomSheetDialogFragment 实现的方法并访问对话框的行为对象。

示例(代码在您的 BottomSheetDialogFragment 实现中):

    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
        }
    }