我有一个浮动对话框的对话框片段,其中包括一个特殊的键盘,当用户在 EditText 字段内按下时会弹出该键盘(正常的 IME 停止显示)。
我希望当用户按下后退按钮(就像普通的 IME 服务一样)时键盘被关闭(可见性 = GONE),但对话框保持可见。然而,从我对 SO 和其他地方的相当广泛的阅读中可以看出,似乎没有办法做到这一点。
如果我将对话框设置为不可取消,那么我不会收到 onCancel() 或 onDismiss() 通知,因为该对话框不可取消。
如果我将对话框设置为可取消,则会收到通知,但对话框会被关闭。
我无法将 onKeyListener 附加到片段中的对话框,因为它已被系统替换,以便片段可以处理对话框的生命周期。
有什么办法可以做到这一点吗?或者出于 Fragment 系统的目的,对关键事件的检测的访问权限已被完全隔离?
最好的方法和最干净的方法是在 onCreateDialog() 中创建的对话框中重写 onBackPressed() 。
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new Dialog(getActivity(), getTheme()){
@Override
public void onBackPressed() {
//do your stuff
}
};
}
我遇到了和你一样的问题,我已经修复了它,将 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
}
});
}
作为胡安·佩德罗·马丁内斯(Juan Pedro Martinez)答案的附录,我认为在查看此帖子时澄清一个具体问题(我的一个问题)会很有帮助。
如果您希望创建一个新的 DialogFragment 并拥有它,以便用户只能使用后退按钮取消它,从而消除随机屏幕触摸过早取消片段的情况,那么这就是您将使用的代码。
在调用 DialogFragment 的任何代码中,您需要将可取消设置设置为 false,以便没有任何东西会消除片段,不会出现杂散的屏幕触摸等。
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,这意味着没有任何东西(没有外部点击)可以取消它并关闭它,并且只允许(从对话框本身内部)后退按钮来关闭它。
甘巴特!
怎么没人建议这个?
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;
}
使用 Fragment onCancel 重写方法。当您按回键时会调用它。 这是一个示例:
@Override
public void onCancel(DialogInterface dialog) {
super.onCancel(dialog);
// Add you codition
}
自 AppCompat 1.5.0-alpha01
AppCompatDialog
现在正在扩展 ComponentDialog
(源)。这意味着在您的 DialogFragment
中,您现在可以轻松获得有效的 OnBackPressedDispatcher
(从片段的对话框中),它允许您自己处理后退按键。
科特林:
class MyFragment: androidx.fragment.app.DialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
(dialog as androidx.activity.ComponentDialog)
.onBackPressedDispatcher
.addCallback(viewLifecycleOwner) {
// handle back press
}
}
}
注意:当您添加回调时,它可以防止通过按“返回”自动关闭/取消对话框。
创建对话框时,重写 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
...
}
};
防止取消DialogFragment:
dialog.setCanceledOnTouchOutside(false)
dialog.setCancelable(false)
dialog.setOnKeyListener { dialog, keyCode, event ->
keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP
}
延伸上面Juan Pedro Martinez的答案。我在
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
}
尝试一下,然后回来投票支持我的评论: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() {
[email protected]()
}
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.
}
}
}
使用带有 closeActivity 标志的 DialogFragment 的 onDismiss() 回调
private var closeActivity: Boolean = true
override fun onDismiss(dialog: DialogInterface?) {
super.onDismiss(dialog)
if (closeActivity) {
activity!!.finish()
}
}
这里有点棘手,因为我们在对话框片段上没有得到任何监听器或回调。做到这一点的方法是覆盖
onDismiss()
,它将作为系统回调,告诉对话框已关闭,并基于此我们可以设置自己的自定义回调,如下
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
debugLog("Dialog is dismissed")
dismiss()
listener?.onRemoteBackPressed()
}
您可以重写 onDismiss 方法来检测 backpressed 事件
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
// ... Do whatever you want
}
借助最新的 androidx 支持库,我们可以使用 BackPressedDispatcher 来关闭对话框片段。在对话框片段类中添加以下代码以关闭。
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requireActivity().getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
dismiss();
}
});
}
AndroidX OnBackPressedDispatcher 也可以是某人的选项