Detecta el botón Atrás pero no descartas el fragmento de diálogo

Tengo un cuadro de diálogo para un diálogo flotante que incluye un teclado especial que aparece cuando un usuario presiona dentro de un campo EditText (el IME normal se detiene para que no se muestre).

Me gustaría que el teclado se descarte (visibility = GONE) cuando el usuario presiona el botón Atrás (al igual que con un servicio IME normal) pero el cuadro de diálogo permanece visible. Sin embargo, no parece haber una manera de hacer esto en la medida en que puedo ver en mi extensa lectura sobre SO y en otros lugares.

Si configuro el diálogo para que no sea cancelable, no me notificarán mediante onCancel () o onDismiss () porque el diálogo no es cancelable.

Si configuro el diálogo para que sea cancelable, recibo una notificación, pero el diálogo se descarta.

No puedo adjuntar un onKeyListener al diálogo en el fragmento porque el sistema lo reemplaza para que el fragmento pueda manejar el ciclo de vida del diálogo.

¿Hay alguna manera de hacer esto? ¿O el acceso a la detección de eventos clave ha sido completamente vallado a los efectos del sistema Fragment?

La mejor manera y la manera más limpia es anular onBackPressed () en el cuadro de diálogo que creó enCreateDialog ().

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

Tuve el mismo problema que tú y lo arreglé adjuntando el onKeyListener al fragmento de diálogo.

En el método onResume() de la clase que se extiende de DialogFragment ponga estos trozos de código:

  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 } }); 

Aquí uno de los problemas que puede encontrar es que este código se ejecutará dos veces: uno cuando el usuario presione el botón Atrás y otro cuando salga para presionarlo. En ese caso, debe filtrar por evento:

 @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 } }); } 

Como una adición a la respuesta de Juan Pedro Martínez, pensé que sería útil aclarar una pregunta específica (una que tenía) al mirar este hilo.

Si desea crear un nuevo DialogFragment y tenerlo de modo que el usuario solo pueda cancelarlo con el botón Atrás, lo que elimina los toques de pantalla aleatorios al cancelar el fragmento prematuramente, entonces este es el código que usaría.

En cualquier código que llame DialogFragment, debe establecer la configuración cancelable en false para que NADA descarte el fragmento, no toque la pantalla, etc.

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

Luego, dentro de su DialogFragment, en este caso MyDaialogFragment.java, agrega el código onresume override para que el diálogo escuche el botón Atrás. Cuando se presiona ejecutará el cierre () para cerrar el fragmento.

 @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; } }); 

Ahora se llamará a su diálogo con “setCancelable” a falso, lo que significa que nada (sin clics externos) puede cancelarlo, apagarlo y permitir (desde el propio diálogo) solo el botón Atrás para cerrarlo.

Ganbatte!

¿Cómo nadie sugirió esto?

 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; } 

Al crear el cuadro de diálogo, anula onBackPressed y 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 ... } }; 
 @Override public Dialog onCreateDialog(Bundle savedInstanceState) { Dialog dialog = new Dialog(getActivity(), getTheme()) { @Override public void onBackPressed() { //your code } }; return dialog; }