我已经实现了一个 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 规范 可以通过触摸底部工作表外部来关闭底部工作表,因此我可以选择哪些选项来覆盖此行为并防止其被忽略?
创建它的实例时应该使用
#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_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;
}
最简单的方法是在 BottomSheetDialogFragment 的对话框上将
setCanceledOnTouchOutside
设置为 false。与 Kotlin 一起,
class XFragment : BottomSheetDialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
dialog?.setCanceledOnTouchOutside(false)
}
}
我可以看到很多答案,但无法让它们工作,直到找到这篇不错的博尔格帖子这里
除了上述用户@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)
这对我来说创造了奇迹。由于我的底页用户体验要求它不可取消和不可拖动,因此我能够通过这些更改正确地实现它。
对我来说,我正在寻找相反的方法,当我点击外部时关闭
BottomSheetDialogFragment
:
class MyBottomSheetDialogFragment : BottomSheetDialogFragment() {
...
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog =
super.onCreateDialog(savedInstanceState).apply {
setCanceledOnTouchOutside(true)
}
}
要禁用外部点击取消,只需将值设置为
false
M 的回答。 Erfan Mowlaei 很有用,但我一直在寻找一种方法来在每次创建类的实例时强制执行此行为,而不必记住调用 setCancelable(false)
。见下文。
class MyBottomSheet : BottomSheetDialogFragment() {
companion object {
fun newInstance() = MyBottomSheet().apply {
isCancelable = false
}
}
}
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
}
}
class MyBottomSheetDialog : BottomSheetDialogFragment() {
/*********/
override fun onCreateDialog(savedInstanceState: Bundle?) =
super.onCreateDialog(savedInstanceState).apply {
setCanceledOnTouchOutside(false)
setOnShowListener { expand() } /**to Expand your bottomSheet according to the content**/
}
/*******/
}
唯一对我有用的东西
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
}
}
解决方案
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);
}
覆盖您的customDialogFragment中BottomSheetDialogFragment的onActivityCreated()
方法和
setCanceledOnTouchOutside(false)
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") }
现在用户无法通过单击外部来关闭它,但可以向下滑动并关闭对话框
您需要添加
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
}
}