【问题标题】:Detect back button but don't dismiss dialogfragment检测后退按钮但不关闭对话框片段
【发布时间】:2014-02-13 23:34:44
【问题描述】:

我有一个浮动对话框的对话框片段,其中包括一个特殊键盘,当用户在 EditText 字段内按下时会弹出一个特殊键盘(正常的 IME 停止显示)。

我希望在用户按下后退按钮(就像使用普通 IME 服务一样)时关闭键盘(可见性 = GONE),但对话框保持可见。但是,从我对 SO 和其他地方的相当广泛的阅读中可以看出,似乎没有办法做到这一点。

如果我将对话框设置为不可取消,则 onCancel() 或 onDismiss() 不会通知我,因为对话框不可取消。

如果我将对话框设置为可取消,我会收到通知,但对话框会被关闭。

我无法将 onKeyListener 附加到片段中的对话框,因为它已被系统替换,以便片段可以处理对话框的生命周期。

有没有办法做到这一点?或者是否为了 Fragment 系统的目的而完全隔离了对关键事件的检测?

【问题讨论】:

    标签: android dialog dialogfragment


    【解决方案1】:

    作为胡安·佩德罗·马丁内斯(Juan Pedro Martinez)回答的附录,我认为在查看此线程时澄清一个特定问题(我有一个)会有所帮助。

    如果您希望创建一个新的 DialogFragment 并拥有它,以便用户只能使用后退按钮取消它,从而消除随机屏幕触摸过早取消片段,那么这就是您将使用的代码。

    在您调用 DialogFragment 的任何代码中,您需要将可取消设置设置为 false,以便 NOTHING 关闭片段、不触摸杂散屏幕等。

    DialogFragment mDialog= new MyDialogFragment();
    mDialog.setCancelable(false);
    mDialog.show(getFragmentManager(), "dialog");
    

    然后,在您的 DialogFragment 中,在本例中为 MyDaialogFragment.java,您添加 onResume 覆盖代码以使对话框侦听后退按钮。当它被按下时,它会执行dismiss()来关闭片段。

    @Override
     public void onResume() 
     {
         super.onResume();
    
         getDialog().setOnKeyListener(new OnKeyListener()
         {
             @Override
             public boolean onKey(android.content.DialogInterface dialog, 
                                  int keyCode,android.view.KeyEvent event) 
             {
                  if ((keyCode ==  android.view.KeyEvent.KEYCODE_BACK))
                  {
                       // To dismiss the fragment when the back-button is pressed.
                       dismiss();
                       return true;
                  }
                  // Otherwise, do nothing else
                  else return false;
             }
       });
    }
    

    现在您的对话框将在“setCancelable”为 false 的情况下被调用,这意味着没有任何东西(没有外部点击)可以取消它并关闭它,并且只允许(从对话框本身)返回按钮来关闭它。

    甘巴特!

    【讨论】:

    • 完美,谢谢,我想在保持返回按钮功能的同时防止意外的外部点击,对我来说,我所要做的就是在 DialogFragment 内的 onCreateView 中的对话框实例上设置取消(false)。
    • 谢谢,这个答案最适合我的情况。
    【解决方案2】:

    试试这个然后回来支持我的评论:D

    /**
     * Callback when Back button is pressed.
     * By default it gonna call back press of host activity
     */
    protected open fun onBackPressed() {
        requireActivity().onBackPressed()
    }
    
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return object : BottomSheetDialog(context!!, theme) {
            override fun onBackPressed() {
                this@BaseBottomSheetFragment.onBackPressed()
            }
    
            override fun setOnKeyListener(onKeyListener: DialogInterface.OnKeyListener?) {
                //Do not call super function
                //This function do nothing but DON'T REMOVE this.
                //Try to set null for onKeyListener is not working.
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      扩展上面的Juan Pedro Martinez's 答案。我在DialogFragment 上写了一个扩展,可以从onCreate() 设置它会自动设置关键侦听器并根据生命周期将其删除。

      fun DialogFragment.setOnBackPressListener(onBackPress: () -> Boolean) {
          val listener = DialogInterface.OnKeyListener { _, keyCode, event ->
              if (keyCode == KeyEvent.KEYCODE_BACK && event?.action != KeyEvent.ACTION_DOWN) {
                  onBackPress()
              } else {
                  false
              }
          }
          val observer = object : LifecycleObserver {
              @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
              fun onResume() {
                   dialog?.setOnKeyListener(listener)
              }
      
              @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
              fun onPause() {
                   dialog?.setOnKeyListener(null)
              }
          }
          lifecycle.addObserver(observer)
      }
      

      DialogFragment#onCreate中的用法

      setOnBackPressListener {
          // handle back press here
      
          true // return true to indicate back press was handled, false if not
      }
      

      【讨论】:

        【解决方案4】:

        AndroidX OnBackPressedDispatcher 也可以是某人的选项

        【讨论】:

        【解决方案5】:

        防止取消DialogFragment:

        dialog.setCanceledOnTouchOutside(false)
        dialog.setCancelable(false)
        dialog.setOnKeyListener { dialog, keyCode, event ->
            keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP
        }
        

        【讨论】:

          【解决方案6】:

          使用 Fragment onCancel 覆盖方法。当您按回时调用它。 这是一个示例:

          @Override
          public void onCancel(DialogInterface dialog) {
              super.onCancel(dialog);
          
              // Add you codition
          }
          

          【讨论】:

          • 请注意,使用这种方法您无法处理 DialogFragment 是否会被解除。你可以得到通知,它将被解雇
          • 确实@Leo Droidcoder 知道如何在无法关闭对话框片段之前拦截按下的后退按钮吗?
          【解决方案7】:

          使用带有 closeActivity 标志的 DialogFragment 的 onDismiss() 回调

          private var closeActivity: Boolean = true    
          
          override fun onDismiss(dialog: DialogInterface?) {
                  super.onDismiss(dialog)
          
                  if (closeActivity) {
                      activity!!.finish()
                  }
              }
          

          【讨论】:

            【解决方案8】:

            怎么没有人提出这个建议?

            public Dialog onCreateDialog(Bundle savedInstanceState) {
              Dialog dialog = super.onCreateDialog(savedInstanceState);
            
              // Add back button listener
              dialog.setOnKeyListener(new Dialog.OnKeyListener() {
                @Override
                public boolean onKey(DialogInterface dialogInterface, int keyCode, KeyEvent keyEvent) {
                  // getAction to make sure this doesn't double fire
                  if (keyCode == KeyEvent.KEYCODE_BACK && keyEvent.getAction() == KeyEvent.ACTION_UP) {
                    // Your code here
                    return true; // Capture onKey
                  }
                  return false; // Don't capture
                }
              });
            
              return dialog;
            }
            

            【讨论】:

              【解决方案9】:

              创建对话框时,同时覆盖 onBackPressed 和 onTouchEvent :

                      final Dialog dialog = new Dialog(activity) {
                          @Override
                          public boolean onTouchEvent(final MotionEvent event) {
                              //note: all touch events that occur here are outside the dialog, yet their type is just touching-down
                              boolean shouldAvoidDismissing = ... ;
                              if (shouldAvoidDismissing) 
                                  return true;
                              return super.onTouchEvent(event);
                          }
              
                          @Override
                          public void onBackPressed() {
                              boolean shouldAvoidDismissing = ... ;
                              if (!shouldSwitchToInviteMode)
                                  dismiss();
                              else
                                  ...
                          }
                      };
              

              【讨论】:

                【解决方案10】:

                我和你有同样的问题,我已经修复了它,将 onKeyListener 附加到对话框片段。

                在DialogFragment的扩展类的方法onResume()中放了这段代码:

                    getDialog().setOnKeyListener(new OnKeyListener()
                    {
                        @Override
                        public boolean onKey(android.content.DialogInterface dialog, int keyCode,android.view.KeyEvent event) {
                
                            if ((keyCode ==  android.view.KeyEvent.KEYCODE_BACK))
                                {
                                     //Hide your keyboard here!!!
                                     return true; // pretend we've processed it
                                }
                            else 
                                return false; // pass on to be processed as normal
                        }
                    });
                

                您可以发现的问题之一是这段代码将被执行两次:一次是当用户按下返回按钮时,另一次是当他离开按下它时。在这种情况下,您必须按事件过滤:

                @Override
                public void onResume() {
                    super.onResume();
                
                    getDialog().setOnKeyListener(new OnKeyListener()
                    {
                        @Override
                        public boolean onKey(android.content.DialogInterface dialog, int keyCode,
                                android.view.KeyEvent event) {
                
                            if ((keyCode ==  android.view.KeyEvent.KEYCODE_BACK))
                            {
                                //This is the filter
                                if (event.getAction()!=KeyEvent.ACTION_DOWN)
                                        return true;
                                else
                                {
                                    //Hide your keyboard here!!!!!!
                                    return true; // pretend we've processed it
                                }
                            } 
                            else 
                                return false; // pass on to be processed as normal
                        }
                    });
                }
                

                【讨论】:

                • 我没有使用过滤器,而是添加了 getDialog().setOnKeyListener(null) 来阻止第二次调用。
                【解决方案11】:

                最好和最干净的方法是在您在 onCreateDialog() 中创建的对话框中覆盖 onBackPressed()。

                @Override
                public Dialog onCreateDialog(Bundle savedInstanceState) {
                    return new Dialog(getActivity(), getTheme()){
                        @Override
                        public void onBackPressed() {
                            //do your stuff
                        }
                    };
                }
                

                【讨论】:

                • 我发现的唯一实际工作正常的解决方案。
                • 这在 DialogFragments 中不起作用,因为 DialogFragment 类中没有 onBackPressed()。
                • DialogFragments 包装一个对话框 - onCreateDialog 创建这个对话框。它适用于 DialogFragments。
                • 我只能确认这是最好和最简单的解决方案。感谢分享这个@Ian Wong
                • 绝对应该是一个公认的答案。迄今为止最原生的解决方案!
                猜你喜欢
                • 2012-08-22
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2017-06-13
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多