【问题标题】:Allow outside touch for DialogFragment允许 DialogFragment 的外部触摸
【发布时间】:2013-03-01 05:22:42
【问题描述】:

我的应用中有一个Fragment,显示DialogFragment
我在片段中有一个关闭对话框的按钮。但是当我显示 dialogFragment 时,对话框外部的触摸什么也不做,我无法单击对话框片段外部的按钮。

如何允许 DialogFragment 进行外部触摸?

【问题讨论】:

    标签: android android-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;
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2020-05-06
              • 1970-01-01
              • 2020-04-11
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多