Interceptar el botón Atrás desde el teclado virtual
Tengo la actividad con varios campos de entrada. Cuando comenzó la actividad se muestra el teclado virtual. Cuando se presiona el botón Atrás, el teclado virtual se cierra y para cerrar la actividad necesito presionar el botón Atrás una vez más.
Entonces la pregunta: ¿es posible interceptar el botón Atrás para cerrar el teclado virtual y finalizar la actividad con solo presionar el botón Atrás sin crear una personalización InputMethodService
?
PD: Sé cómo interceptar el botón Atrás en otros casos: onKeyDown()
o onBackPressed()
pero no funciona en este caso: solo se intercepta la segunda pulsación del botón Atrás.
onKeyDown() y onBackPressed() no funcionan en este caso. Tienes que usar onKeyPreIme .
Inicialmente, debe crear un texto de edición personalizado que extienda EditText. Y luego tienes que implementar el método onKeyPreIme que controla KeyEvent.KEYCODE_BACK . Después de esto, una presión hacia atrás es suficiente para resolver su problema. Esta solución me funciona perfectamente.
TextoEditarPersonalizado.java
public class CustomEditText extends EditText {
public CustomEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
// User has pressed Back key. So hide the keyboard
InputMethodManager mgr = (InputMethodManager)
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
mgr.hideSoftInputFromWindow(this.getWindowToken(), 0);
// TODO: Hide your view as you do it in your activity
}
return false;
}
En tu XML
<com.YOURAPP.CustomEditText
android:id="@+id/CEditText"
android:layout_height="wrap_content"
android:layout_width="match_parent"/>
En tu actividad
public class MainActivity extends Activity {
private CustomEditText editText;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText = (CustomEditText) findViewById(R.id.CEditText);
}
}
Sí, es completamente posible mostrar y ocultar el teclado e interceptar las llamadas al botón Atrás. Es un pequeño esfuerzo adicional, ya que se mencionó que no existe una forma directa de hacerlo en la API. La clave es anular boolean dispatchKeyEventPreIme(KeyEvent)
dentro de un diseño. Lo que hacemos es crear nuestro diseño. Elegí RelativeLayout ya que era la base de mi Actividad.
<?xml version="1.0" encoding="utf-8"?>
<com.michaelhradek.superapp.utilities.SearchLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.michaelhradek.superapp"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/white">
Dentro de nuestra Actividad configuramos nuestros campos de entrada y llamamos a la setActivity(...)
función.
private void initInputField() {
mInputField = (EditText) findViewById(R.id.searchInput);
InputMethodManager imm =
(InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED,
InputMethodManager.HIDE_IMPLICIT_ONLY);
mInputField.setOnEditorActionListener(new OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId,
KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
performSearch();
return true;
}
return false;
}
});
// Let the layout know we are going to be overriding the back button
SearchLayout.setSearchActivity(this);
}
Obviamente, la initInputField()
función configura el campo de entrada. También habilita la tecla Intro para ejecutar la funcionalidad (en mi caso una búsqueda).
@Override
public void onBackPressed() {
// It's expensive, if running turn it off.
DataHelper.cancelSearch();
hideKeyboard();
super.onBackPressed();
}
Entonces, cuando se onBackPressed()
llama dentro de nuestro diseño, podemos hacer lo que queramos, como ocultar el teclado:
private void hideKeyboard() {
InputMethodManager imm = (InputMethodManager)
getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mInputField.getWindowToken(), 0);
}
De todos modos, aquí está mi anulación de RelativeLayout.
package com.michaelhradek.superapp.utilities;
import android.app.Activity;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.RelativeLayout;
/**
* The root element in the search bar layout. This is a custom view just to
* override the handling of the back button.
*
*/
public class SearchLayout extends RelativeLayout {
private static final String TAG = "SearchLayout";
private static Activity mSearchActivity;;
public SearchLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SearchLayout(Context context) {
super(context);
}
public static void setSearchActivity(Activity searchActivity) {
mSearchActivity = searchActivity;
}
/**
* Overrides the handling of the back key to move back to the
* previous sources or dismiss the search dialog, instead of
* dismissing the input method.
*/
@Override
public boolean dispatchKeyEventPreIme(KeyEvent event) {
Log.d(TAG, "dispatchKeyEventPreIme(" + event + ")");
if (mSearchActivity != null &&
event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
KeyEvent.DispatcherState state = getKeyDispatcherState();
if (state != null) {
if (event.getAction() == KeyEvent.ACTION_DOWN
&& event.getRepeatCount() == 0) {
state.startTracking(event, this);
return true;
} else if (event.getAction() == KeyEvent.ACTION_UP
&& !event.isCanceled() && state.isTracking(event)) {
mSearchActivity.onBackPressed();
return true;
}
}
}
return super.dispatchKeyEventPreIme(event);
}
}
Lamentablemente no puedo llevarme todo el crédito. Si marca la fuente de Android para el cuadro de diálogo de búsqueda rápida, verá de dónde surgió la idea.