防止在外部触摸时关闭 BottomSheetDialogFragment

问题描述 投票:0回答:14

我已经实现了一个 BottomSheet 对话框,我想防止当用户在窥视时触摸底页外部(未完全展开状态)时底页消失。

我在代码中设置了

dialog.setCanceledOnTouchOutside(false);
,但似乎没有任何影响。

这是我的 BottomSheetDialogFragment 类:

public class ShoppingCartBottomSheetFragment extends BottomSheetDialogFragment  {

    private BottomSheetBehavior.BottomSheetCallback mBottomSheetBehaviorCallback = new BottomSheetBehavior.BottomSheetCallback() {

        @Override
        public void onStateChanged(@NonNull View bottomSheet, int newState) {
            if (newState == BottomSheetBehavior.STATE_HIDDEN) {
                dismiss();
            }

        }

        @Override
        public void onSlide(@NonNull View bottomSheet, float slideOffset) {
        }
    };

    @Override
    public void setupDialog(Dialog dialog, int style) {
        super.setupDialog(dialog, style);

        View contentView = View.inflate(getContext(), R.layout.fragment_shopping_cart_bottom_sheet, null);

        dialog.setCanceledOnTouchOutside(false);

        dialog.setContentView(contentView);

        CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) ((View) contentView.getParent()).getLayoutParams();
        CoordinatorLayout.Behavior behavior = params.getBehavior();

        if( behavior != null && behavior instanceof BottomSheetBehavior ) {
            ((BottomSheetBehavior) behavior).setBottomSheetCallback(mBottomSheetBehaviorCallback);
            ((BottomSheetBehavior) behavior).setPeekHeight(97);
            ((BottomSheetBehavior) behavior).setHideable(false);
        }
    }


    @Override
    public void onStart() {
        super.onStart();
        Window window = getDialog().getWindow();
        WindowManager.LayoutParams windowParams = window.getAttributes();
        windowParams.dimAmount = 0;
        windowParams.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
        window.setAttributes(windowParams);
    }
}

根据 BottomSheet 规范 可以通过触摸底部工作表外部来关闭底部工作表,因此我可以选择哪些选项来覆盖此行为并防止其被忽略?

android bottom-sheet
14个回答
64
投票

创建它的实例时应该使用

#setCancelable(false)

    BottomSheetDialogFragment bottomSheetDialogFragment = new SequenceControlFragmentBottomSheet();
    bottomSheetDialogFragment.setCancelable(false);
    bottomSheetDialogFragment.show(getChildFragmentManager(), bottomSheetDialogFragment.getTag());

32
投票

在简单的底部表单对话框的情况下,上述所有答案都有点复杂,只需使用可取消的,因为它可以防止在对话框外部滚动和单击。

mBottomSheetDialog.setCancelable(false)
mBottomSheetDialog.setCanceledOnTouchOutside(false)

只需将其用于简单的底部表单对话框。 它对我有用。


26
投票

setCancelable(false)
也将防止底部纸张在背面按下时消失。如果我们查看 android 设计支持库中底部工作表的布局资源,有一个 ID 为 touch_outside
View
组件,并且在
OnClickListener
的方法
wrapInBottomSheet
中设置了一个
BottomSheetDialog
,用于使用用于检测外部点击并关闭对话框。因此,为了防止在底部表单之外触摸时取消,我们需要删除
OnClickListener

将这些行添加到

onActivityCreated
方法(或
onCreateView
之后的任何其他生命周期方法)。

@Override public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    View touchOutsideView = getDialog().getWindow()
        .getDecorView()
        .findViewById(android.support.design.R.id.touch_outside);
    touchOutsideView.setOnClickListener(null);
}

此外,如果您想防止通过向下滑动来关闭底部工作表,请更改底部工作表对话框行为 Hideable false。要

setHideable(false)
将以下代码添加到
onCreateDialog
方法中。

@Override public Dialog onCreateDialog(Bundle savedInstanceState) {
    final BottomSheetDialog bottomSheetDialog =
        (BottomSheetDialog) super.onCreateDialog(savedInstanceState);

    bottomSheetDialog.setOnShowListener(new DialogInterface.OnShowListener() {
      @Override public void onShow(DialogInterface dialog) {
        FrameLayout bottomSheet =
        bottomSheetDialog.findViewById(android.support.design.R.id.design_bottom_sheet);

        if (null != bottomSheet) {
          BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);
        behavior.setHideable(false);
        }
      }
    });
    return bottomSheetDialog;
  }

19
投票

最简单的方法是在 BottomSheetDialogFragment 的对话框上将

setCanceledOnTouchOutside
设置为 false。与 Kotlin 一起,

class XFragment : BottomSheetDialogFragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        dialog?.setCanceledOnTouchOutside(false)
    }

}

4
投票

我可以看到很多答案,但无法让它们工作,直到找到这篇不错的博尔格帖子这里

除了上述用户@shijo 提出的解决方案之外,还有另一种解决方案。我为

BottomSheetDialogFragment
所做的只是遵循
onActivityCreated
中的代码片段,并且能够实现这两种行为。

  • 禁用可拖动行为。

  • 禁用外部触摸取消。

      override fun onActivityCreated(savedInstanceState: Bundle?) {
      super.onActivityCreated(savedInstanceState)
      val dialog = dialog as BottomSheetDialog?
    
      val bottomSheet =
          dialog?.findViewById<FrameLayout>(com.google.android.material.R.id.design_bottom_sheet)
      val behavior: BottomSheetBehavior<View> = BottomSheetBehavior.from(bottomSheet as View)
      behavior.state = BottomSheetBehavior.STATE_EXPANDED
      behavior.peekHeight = 0
    
      // Disable Draggable behavior
      behavior.isDraggable = false
    
      // Disable cancel on touch outside
      val touchOutsideView =
          getDialog()?.window?.decorView?.findViewById<View>(com.google.android.material.R.id.touch_outside)
      touchOutsideView?.setOnClickListener(null)
    

这对我来说创造了奇迹。由于我的底页用户体验要求它不可取消和不可拖动,因此我能够通过这些更改正确地实现它。


4
投票

对我来说,我正在寻找相反的方法,当我点击外部时关闭

BottomSheetDialogFragment

class MyBottomSheetDialogFragment : BottomSheetDialogFragment() {
    ...
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =
        super.onCreateDialog(savedInstanceState).apply {
            setCanceledOnTouchOutside(true)
        }
}

要禁用外部点击取消,只需将值设置为

false


3
投票

M 的回答。 Erfan Mowlaei 很有用,但我一直在寻找一种方法来在每次创建类的实例时强制执行此行为,而不必记住调用 setCancelable(false)

。见下文。

class MyBottomSheet : BottomSheetDialogFragment() { companion object { fun newInstance() = MyBottomSheet().apply { isCancelable = false } } }
    

3
投票
尝试 Kotlin 方式

override fun onViewCreated(view: View, savedInstanceState: Bundle?) { dialog?.setCancelable(false) dialog?.setCanceledOnTouchOutside(false) view.viewTreeObserver.addOnGlobalLayoutListener { val dialog = dialog as BottomSheetDialog? val bottomSheet = dialog!!.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet) as FrameLayout val behavior = BottomSheetBehavior.from(bottomSheet) behavior.state = BottomSheetBehavior.STATE_EXPANDED behavior.peekHeight = 0 behavior.isDraggable = false } }
    

3
投票
在 kotlin 中就这么简单,你可以这样做;

class MyBottomSheetDialog : BottomSheetDialogFragment() { /*********/ override fun onCreateDialog(savedInstanceState: Bundle?) = super.onCreateDialog(savedInstanceState).apply { setCanceledOnTouchOutside(false) setOnShowListener { expand() } /**to Expand your bottomSheet according to the content**/ } /*******/ }
    

2
投票
我尝试了所有答案,但这是最好的解决方案

唯一对我有用的东西

Style.xml

<style name="BottomSheetDialogTheme" parent="@style/Theme.Design.Light.BottomSheetDialog"> <item name="android:statusBarColor">@android:color/transparent</item> <item name="android:windowIsFloating">false</item> <item name="android:windowSoftInputMode">adjustResize|stateAlwaysVisible</item> <item name="android:navigationBarColor">@color/white</item> <item name="bottomSheetStyle">@style/BottomSheet</item> </style> <!-- set the rounded drawable as background to your bottom sheet --> <style name="BottomSheet" parent="@style/Widget.Design.BottomSheet.Modal"> <item name="android:background">@drawable/bg_bottom_sheet_dialog_fragment</item> </style>

RoundedBottomSheetDialogFragment

open class RoundedBottomSheetDialogFragment : BottomSheetDialogFragment() { override fun getTheme(): Int = R.style.BottomSheetDialogTheme override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { return BottomSheetDialog(requireContext(), theme) } } class UserDetailsSheet : RoundedBottomSheetDialogFragment() { init { isCancelable = false } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.user_info_fragment, container, false) } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val dialog = super.onCreateDialog(savedInstanceState) dialog.setCancelable(false) dialog.setCanceledOnTouchOutside(false) return dialog } }
    

1
投票
我觉得上面的答案都有点不完整,我会解释原因。

    setCancelable 将停止外部点击行为,但它也会阻止您的 BottomsheetDialogoagFragment 后按。
  1. 很少有答案会重写 setCancelable() 方法来管理外部点击,这有点复杂,因为您需要处理可取消和可隐藏。

解决方案

override fun onStart() { super.onStart() stopOutsideClick() } private fun stopOutsideClick() { val touchOutsideView = dialog?.window?.decorView?.findViewById<View>(com.google.android.material.R.id.touch_outside) touchOutsideView?.setOnClickListener(null) }

您必须在 onStart() 中调用此方法,其中对话框的实例将始终存在。


1
投票
简单而简短的工作解决方案

@Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); getDialog().setCanceledOnTouchOutside(false); }
覆盖您的customDialogFragment中BottomSheetDialogFragment的

onActivityCreated()

方法和
setCanceledOnTouchOutside(false)

    


0
投票
我遇到了同样的问题。但我需要防止底部工作表片段通过单击外部关闭,但留出通过向下滑动关闭的机会。只需将变量

isHideable: Boolean

 添加到您的 BSFragment 类并添加下一个片段

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val dialog = super.onCreateDialog(savedInstanceState) dialog.setOnShowListener { val d = dialog as BottomSheetDialog val bottomSheet = d.findViewById<FrameLayout>(com.google.android.material.R.id.design_bottom_sheet) BottomSheetBehavior.from(bottomSheet!!).state = initialState() BottomSheetBehavior.from(bottomSheet).setHideable(isHideable) } return dialog }
当您调用片段时 - 将 isHideable 设置为 true

private fun showBSFragmentDialog() { val bSFDialog = BSFragment.newInstance() bSFDialog.isCancelable = false bSFDialog.isHideable = true bSFDialog.show(supportFragmentManager, "bSFDialog") }
现在用户无法通过单击外部来关闭它,但可以向下滑动并关闭对话框


0
投票
与上面的答案非常相似。

您需要添加

setCanceledOnTouchOutside(false)

 & 
isCancellable = false

这是我的代码。

class SomeFragment : BottomSheetDialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val view = LayoutInflater.from(activity) .inflate(R.layout.fragment_some, null, false) val bottomSheetDialog = super.onCreateDialog(savedInstanceState).apply { setContentView(view) setOnShowListener { dialog -> val d = dialog as BottomSheetDialog val bottomSheet: View = d.findViewById(com.google.android.material.R.id.design_bottom_sheet) ?: return@setOnShowListener BottomSheetBehavior.from(bottomSheet).apply { skipCollapsed = true state = BottomSheetBehavior.STATE_EXPANDED } } setCanceledOnTouchOutside(false) <--- The important part!!! isCancelable = false <--- The important part!!! } binding = FragmentSomeBinding.bind(view).apply { fragment = this@SomeFragment } return bottomSheetDialog } }
    
© www.soinside.com 2019 - 2024. All rights reserved.