允许 DialogFragment 的外部触摸
Posted
技术标签:
【中文标题】允许 DialogFragment 的外部触摸【英文标题】:Allow outside touch for DialogFragment 【发布时间】:2013-03-01 05:22:42 【问题描述】:我的应用中有一个Fragment
,显示DialogFragment
。
我在片段中有一个关闭对话框的按钮。但是当我显示 dialogFragment 时,对话框外部的触摸什么也不做,我无法单击对话框片段外部的按钮。
如何允许 DialogFragment 进行外部触摸?
【问题讨论】:
【参考方案1】:为此,应打开允许外部触摸的Window
标志,并且为了美观,应清除背景暗淡标志。
由于必须在创建对话框后完成,我通过Handler
实现了它。
@Override
public void onViewCreated(final View view, Bundle savedInstanceState)
super.onViewCreated(view, savedInstanceState);
// This is done in a post() since the dialog must be drawn before locating.
getView().post(new Runnable()
@Override
public void run()
Window dialogWindow = getDialog().getWindow();
// Make the dialog possible to be outside touch
dialogWindow.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
dialogWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
getView().invalidate();
);
此时外部触摸是可能的。
如果我们想让它更好看并且没有框架,可以添加以下代码:
@Override
public void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
// Hide title of the dialog
setStyle(STYLE_NO_FRAME, 0);
【讨论】:
你知道如何为 BottomSheetDialogFragment 实现同样的效果吗? @Yanix 这在 DialogFragment 设置为全屏时不起作用 window.setLayout(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT) 这在 android 6、7、7.1、8 的情况下有效,但在 Android 9 的情况下失败。在 Android 9 上无法通过 DialogFragment 之外的任何点击。【参考方案2】:这是一个更通用的标志,允许任何操作,而不仅仅是触摸操作
window.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
【讨论】:
【参考方案3】:我找到了处理外部触摸解除的替代方法。请检查下面的代码示例,这肯定会起作用,
@Override
public void onStart()
// mDialogView is member variable
mDialogView = getView();
mDialogView.setOnTouchListener(new View.OnTouchListener()
@Override
public boolean onTouch(View v, MotionEvent event)
float eventX = event.getRawX();
float eventY = event.getRawY();
int location[] = new int[2];
mDialogView.getLocationOnScreen(location);
if (eventX < location[0] || eventX > (location[0] + mDialogView.getWidth()) || eventY < location[1]
|| eventY > location[1] + mDialogView.getHeight())
dismiss();
return true;
return false;
);
就我而言,我得到的 getDialog() 不是空值,但 getDialog().dismiss() 不起作用。此外,上面标记的解决方案在我的情况下也不起作用。所以,我采用了这种方法。
【讨论】:
【参考方案4】:根据我上面的小调查,我真的认为这应该是公认的答案:
@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
FragmentActivity act = getActivity();
AlertDialog.Builder builder = new AlertDialog.Builder(act);
// ... do your stuff here
Dialog dialog = builder.create();
dialog.setCanceledOnTouchOutside(false);
Window window = dialog.getWindow();
if( window!=null )
window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
return dialog;
即把flags的设置移到onCreateDialog()的最后,加上setCancelOnTouchOutside(false)。
接受的答案不适用于运行 Android 9 和 10(很可能是 11)的三星手机。看起来在这些系统上,三星改变了一些东西,使得设置这些标志只有在很早设置时才有效。
上述方法适用于任何地方,包括 Google 模拟器、LG Nexus 5X、HTC Desire 12、Google Pixel 6、几部华为手机,以及我测试过的所有(大约 20 部)三星手机。
【讨论】:
【参考方案5】:我已经在
上测试了接受的(Yaniv 的)答案Galaxy Tab A 9.7 Android 7.1.1: ok
Galaxy Tab A 8.0 Android 7 : ok
Galaxy Tab S4 Android 9 : no touch outside DialogFragment
Galaxy S5 Android 6.0.1 : ok
Galaxy Tab S2 Android 7 : ok
Galaxy Tab S3 Android 9 : no touch outside DialogFragment
Galaxy Tab S4 Android 9 : no touch outside DialogFragment
Galaxy Note Edge Android 6.0.1: ok
Galaxy Note4 Android 6.0.1 : ok
而且它看起来确实不适用于 Android 9。当我说“不起作用”时,我的意思是当我尝试单击 DialogFragment 外部的按钮时,它的 onClick() 方法根本不会被调用。
编辑:根据我的发现(以 cmets 为单位),我认为以下应该是公认的答案。以下适用于我测试过的所有地方,包括模拟器、Google Pixel 6、LG Nexus 5X、HTC Desire 12、大约 5 部华为手机和大约 20 部运行 Android 5 到 10 的三星手机。
public Dialog onCreateDialog(Bundle savedInstanceState)
FragmentActivity act = getActivity();
AlertDialog.Builder builder = new AlertDialog.Builder(act);
// ... do your stuff here....
Dialog dialog = builder.create();
dialog.setCanceledOnTouchOutside(false);
Window window = dialog.getWindow();
if( window!=null )
window.setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
return dialog;
【讨论】:
它不适用于 Android 10 和即将推出的 Android 11。总结一下:仅适用于 Android 6、7、7.1、8.0、8.1,不适用于 Android 9、10 和 11。 以上结果是在真实的三星手机上观察到的。奇怪的是,当在 Android 9、10 或 11 的模拟器上运行时 - 接受的答案确实有效。 甚至更好 - 现在我已经在运行 Android 9 的华为 Mate Pro 上进行了测试,并且在那里它可以正常工作。所以看起来这不仅仅适用于运行 Android 9、10 或 11 的三星手机。 好吧,我终于找到了一种方法,让接受的答案也适用于三星手机:将设置“FLAG_NOT_TOUCH_MODAL”标志的部分移到 onCreateDialog() 的末尾,并且不要 post()它 - 只需从那里调用它。看起来在三星手机上,这个标志需要尽早设置才能工作。【参考方案6】:您需要根据需要设置合适的样式。您可以在official docs 中阅读有关 FragmentDialogs 样式的更多信息。
我相信你可以使用以下样式:
STYLE_NO_FRAME【讨论】:
我已经在使用 STYLE_NO_FRAME,它是唯一适合我的(自 UI 以来)。【参考方案7】:好吧,让我们具体一点。 如果你想使用外部视图,那么你不应该使用对话框片段,而是像这样使用片段。
FragmentTransaction transaction = fragmentManager.beginTransaction();
// For a little polish, specify a transition animation
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
// To make it fullscreen, use the 'content' root view as the container
// for the fragment, which is always the root view for the activity
transaction.add(android.R.id.content, newFragment)
.addToBackStack(null).commit();
并制作 wrap_content 布局,这显然看起来像 Dialog,默认情况下它位于屏幕顶部。
更多详情请访问here
【讨论】:
【参考方案8】:@yaniv 提供了答案。但如果你愿意,这里有一个 sn-p,你可以得到相同的结果,但不需要将 Runnable
发布到根 View
:
@NonNull
@Override
public Dialog onCreateDialog(@Nullable final Bundle savedInstanceState)
final Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
return dialog;
【讨论】:
以上是关于允许 DialogFragment 的外部触摸的主要内容,如果未能解决你的问题,请参考以下文章
DialogFragment findFragmentByTag 返回 null
重叠DialogFragment,在方向更改时以错误的顺序重新创建
ViewPage + DialogFragment + Bitmap项目实战