防止在触摸外部时关闭底部工作表对话框片段



我已经实现了一个底部工作表对话框,我想防止当用户在偷看时触摸底部工作表外部时底部工作表关闭(未完全展开状态(。

我已经在代码中设置了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 规范,可以通过触摸底板外部来关闭底板,因此我有什么选项可以覆盖此行为并防止它被关闭?

创建实例时应使用#setCancelable(false)

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

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

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

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

setCancelable(false)也会防止底部在按背时关闭。如果我们查看 android 设计支持库中底部工作表的布局资源,有一个 ID 为 touch_outsideView 组件,并且在 BottomSheetDialog 的方法 wrapInBottomSheet 中设置了一个OnClickListener,用于检测外部点击并关闭对话框。因此,为了防止在底板外触摸时取消,我们需要 移除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);
}

此外,如果要通过向下滑动来防止底部工作表关闭,请更改底部工作表对话框行为可隐藏 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;
  }

最简单的方法是在 BottomSheetDialogFragment 的对话框中将setCanceledOnTouchOutside设置为 false。 与科特林,

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

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

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

我尝试了所有答案,但这是最好的工作解决方案

唯一对我有用的东西

风格.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>

圆角底部工作表对话框片段

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

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

这是上面用户@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)
    

这为我创造了奇迹。由于我的底表UX要求它是不可取消和不可拖动的,因此我能够通过这些更改正确实现它。

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

我觉得以上所有答案都有点不完整,我会解释原因。

  1. setCancelable 将停止外部点击行为,但它也会阻止您的底表DialoagFragment从后按。
  2. 很少有答案会覆盖 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(( 中调用此方法,其中对话框的实例将始终存在。

简单而简短的工作解决方案

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        getDialog().setCanceledOnTouchOutside(false);
    }

覆盖自定义对话框片段中的onActivityCreated()底部工作表对话片段的方法,并setCanceledOnTouchOutside(false)

在 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**/
        }
/*******/
}

我遇到了同样的问题。但是我需要防止底部工作表碎片通过单击外部关闭,但要留出向下滑动关闭的机会。只需将变量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")  }

现在用户无法通过单击外部关闭它,但能够向下滑动并关闭对话框

对我来说

,我正在寻找相反的方法,当我单击外部时关闭BottomSheetDialogFragment

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

要禁用外部单击时的取消,只需将值设置为 false

最新更新