88

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

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

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

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

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

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

4

11 回答 11

199

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

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    return new Dialog(getActivity(), getTheme()){
        @Override
        public void onBackPressed() {
            //do your stuff
        }
    };
}
于 2014-08-29T18:30:46.210 回答
86

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

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

    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
        }
    });
}
于 2014-04-10T12:00:13.040 回答
28

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

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;
}
于 2018-02-10T14:51:54.137 回答
25

作为胡安·佩德罗·马丁内斯(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 的情况下被调用,这意味着没有任何东西(没有外部点击)可以取消它并关闭它,并且只允许(从对话框本身)返回按钮来关闭它。

甘巴特!

于 2014-08-11T19:43:20.150 回答
21

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

@Override
public void onCancel(DialogInterface dialog) {
    super.onCancel(dialog);

    // Add you codition
}
于 2018-12-18T07:46:06.273 回答
5

创建对话框时,覆盖 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
                    ...
            }
        };
于 2016-07-07T09:38:02.420 回答
3

防止取消DialogFragment:

dialog.setCanceledOnTouchOutside(false)
dialog.setCancelable(false)
dialog.setOnKeyListener { dialog, keyCode, event ->
    keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP
}
于 2020-06-12T11:51:11.067 回答
2

延伸胡安佩德罗马丁内斯上面的答案。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
}
于 2021-08-31T23:59:22.923 回答
1

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

private var closeActivity: Boolean = true    

override fun onDismiss(dialog: DialogInterface?) {
        super.onDismiss(dialog)

        if (closeActivity) {
            activity!!.finish()
        }
    }
于 2018-09-18T07:15:05.950 回答
1

试试这个,然后回来支持我的评论: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.
        }
    }
}

在此处输入图像描述

于 2021-11-19T03:47:54.410 回答
-2

AndroidX OnBackPressedDispatcher也可以是某人的选择

于 2020-08-24T13:29:48.853 回答