【问题标题】:Android - How to disable STATE_HALF_EXPANDED state of a bottom sheetAndroid - 如何禁用底部工作表的 STATE_HALF_EXPANDED 状态
【发布时间】:2020-04-16 16:05:16
【问题描述】:

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

所以我将BottomSheetBehavior 设置为

isFitToContents = false
peekHeight = 200dp

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

我正在和com.google.android.material:material:1.1.0-rc01一起工作

有没有办法禁用STATE_HALF_EXPANDED 状态?

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

【问题讨论】:

  • 请提供更多详细信息,例如底部表格的外观。
  • @UD..我认为在这种情况下底部的工作表内容不相关。这是一个更普遍的问题,是否可以禁用底部工作表状态之一
  • 对于我的用例,似乎设置 halfExpandedRatio=0.25fpeekHeight = 200dp 然后将 STATE_COLLAPSEDSTATE_HALF_EXPANDED 视为相同的状态可以解决问题。如果有其他想法,请保持问题开放。
  • 你可以点击这个链接,它会帮助androidhive.info/2017/12/android-working-with-bottom-sheet
  • 如果某个答案符合您问题中设定的目标,请务必接受其中一个答案!

标签: android material-components-android


【解决方案1】:

我正在使用材料库版本 1.1.0,BottomSheetBehavior 类具有此属性 skipCollapsed,如果将其设置为 true,底部表格将跳过 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
}

【讨论】:

  • 我不知道为什么,但这就像一个魅力:)
  • 唯一有意义的答案,不是一个奇怪的把戏,最好的部分,它有效。
【解决方案2】:

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


替代解决方案

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

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

不幸的是,当isFitToContents == false 引入了额外的“半高”行为时,OP 希望避免并因此出现问题。

除了“半高”行为之外,还引入了另一种行为,即“扩展偏移”。扩展的偏移量指定底部工作表从全屏向下停止的距离。例如,100f 的值将在完全展开时在底部工作表的顶部留下一个 100px 边框。扩展偏移的默认值为零。

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

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

这是一个简短的演示应用程序,展示了我们可以通过右下方的表格结构满足这些要求:

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>

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

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>

【讨论】:

  • 关于您最初的反应 - 我看到即使我设置了一半的展开比例,仍然可以看到非常纤细的底片提示。无论如何,这不是我正在寻找的行为。正如您在“替代解决方案”中提到的 - “底部工作表应在窥视高度和全高之间过渡”
  • 您的“替代解决方案”似乎正在运行,这是我需要的解决方案,我的初始测试表明我需要使用 isFitToContents = false,但现在使用 isFitToContents = true 进行测试可以正常工作
  • @NoaDrach 如果底部工作表是可隐藏的,那么由于底部工作表偏移的计算方式,底部至少会显示 1px。我没想到工作表会被隐藏,但是显示 1px 时,工作表可能会在达到半展开状态时被强制隐藏sheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN,但这有点复杂。替代解决方案更好。
【解决方案3】:

尝试在您的BottomSheetBehavior 上设置addBottomSheetCallback,当您检测到STATE_HALF_EXPANDED 状态时,调用setState(STATE_HIDDEN),这样每当底部工作表尝试达到中间状态时,它就会关闭。

【讨论】:

  • 好主意,在我的情况下,我会将状态设置为 STATE_COLLAPSED 而不是 STATE_HIDDEN。但我尝试实现它,从STATE_HALF_EXPANDEDSTATE_COLLAPSED 的过渡感觉很笨拙。状态之间的转换是动画的,因此您会看到底部表格在 STATE_HALF_EXPANDED 处停止,然后移动到 STATE_COLLAPSED
  • 你能把它和 0 的 halfExpandedRatio 结合起来吗?
  • @Ridcully - 这里有 2 个问题 - 1. halfExpandedRatio 必须高于 0 2. 将其设置为非常低的值将导致它几乎完全最小化(半展开状态),然后再被捕捉到崩溃的状态。我想把这个建议与我的halfExpandedState=0.25f, b/c 的工作解决方案结合起来,那么状态之间的转换就不会那么明显了。但是,与我已经拥有的相比,我不确定这会是一个很大的变化
【解决方案4】:

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

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

我必须动态更改这些,所以我在底页上设置了以下内容,但您也可以在 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)

希望对你有帮助

【讨论】:

    【解决方案5】:

    尝试设置BottomSheetBehavior.setHalfExpandedRatio(0f)。除非使用.setState() 显式设置状态,否则不会影响STATE_HALF_EXPANDED。还应该可以创建自定义Behavior,它扩展CoordinatorLayout.Behavior&lt;View&gt;,但没有STATE_HALF_EXPANDED。例如。 Intercepting everything with CoordinatorLayout Behaviors.

    【讨论】:

    • 那行不通 - halfExpandedRatio 必须在 0 和 1 之间。
    【解决方案6】:

    如果你想试试上面的图片,你可以按照下面的代码,希望对你有帮助!!!

    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>
    

    【讨论】:

      【解决方案7】:

      我尝试了不同的方法,但没有一种方法是完美的。我尝试拦截BottomSheetBehavior.BottomSheetCallback {} 中的事件并根据自定义逻辑调用dismiss(),但它引起了混蛋。

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

      请注意,底部工作表仍会随着动画展开。真是太棒了!

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

      【讨论】:

        【解决方案8】:

        根据其他答案,您可以通过在底部工作表上设置 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">
        

        【讨论】:

          【解决方案9】:

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

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

          示例(代码在您的 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
                  }
              }
          

          【讨论】:

          • 您的解决方案不允许底部工作表在我们切换到展开状态后进入折叠状态。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-12-10
          • 1970-01-01
          • 2016-10-05
          • 2021-12-25
          • 2016-12-27
          相关资源
          最近更新 更多