防止在外面触摸时解雇BottomSheetDialogFragment

时间:2017-02-10 08:01:39

标签: android bottom-sheet

我已经实现了一个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 specification底部工作表可以通过触摸底部工作表的内部来解散,因此我可以选择覆盖此行为并防止其被解除?

11 个答案:

答案 0 :(得分:18)

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

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

答案 1 :(得分:13)

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

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

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

答案 2 :(得分:9)

setCancelable(false)也可以防止底部页面在后退时被解除。如果我们在android设计支持库中查看底部工作表的布局资源,则会有一个ID为touch_outside View 组件,并且方法{{中设置了OnClickListener 1 {}} wrapInBottomSheet,用于检测外部点击并关闭对话框。因此,为防止在底部工作区外触摸取消,我们需要删除BottomSheetDialog

将这些行添加到OnClickListener方法(或onActivityCreated之后的任何其他生命周期方法)。

onCreateView

此外,如果您想通过向下滑动来阻止底部工作表消除,请更改底部工作表对话框行为可隐藏 false。要@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); } 将以下代码添加到setHideable(false)方法。

onCreateDialog

答案 3 :(得分:4)

最简单的方法是在BottomSheetDialogFragment 的对话框中将setCanceledOnTouchOutside 设置为false。使用 Kotlin,

class XFragment : BottomSheetDialogFragment() {

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

}

答案 4 :(得分:1)

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

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

在您的 customDialogFragment 和 onActivityCreated()

中覆盖 BottomSheetDialogFragment 的 setCanceledOnTouchOutside(false) 方法

答案 5 :(得分:0)

M的答案。 Erfan Mowlaei 很有用,但我一直在寻找一种方法,以便在每次创建类实例时都必须执行此行为,而不必记住调用for (int i=0;i<P;i++) { ... if (i == 0) printf("\nGuess:"); ... } 。见下文。

setCancelable(false)

答案 6 :(得分:0)

我认为以上所有答案都有些不完整,我将解释原因。

  1. setCancelable将停止外部点击行为,但也将阻止您的bottomsheetDialoagFragment再次按下。
  2. 很少有方法可以覆盖setCancelable()方法来管理外部点击,这有点复杂,因为您需要处理可取消和可隐藏的操作。

解决方案

@objc func delete(sender: Any) {
    print("delete was pressed in the edit menu")
}

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

答案 7 :(得分:0)

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

唯一对我有用的东西

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

答案 8 :(得分:0)

我可以看到很多答案,但是直到他们发现这篇不错的帖子here

时,他们还是无法正常工作。

这是上述@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要求它不可取消且不可拖动,因此我可以通过这些更改正确实现它。

答案 9 :(得分: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")  }

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

答案 10 :(得分:0)

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