Pregunta Interceptar el botón Atrás desde el teclado suave


Tengo la actividad con varios campos de entrada. Cuando comenzó la actividad, se muestra el teclado suave. Cuando el botón Atrás se presiona el teclado suave 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 función personalizada? 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.


74
2017-10-15 06:59


origen


Respuestas:


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 ha mencionado que no hay una forma directa de hacer esto 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 al 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, el initInputField() función configura el campo de entrada. También habilita la tecla enter 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 el onBackPressed() se llama dentro de nuestro diseño, entonces 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 del 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);
    }
}

Desafortunadamente no puedo tomar todo el crédito. Si revisas el Android fuente para el cuadro de búsqueda rápida de diálogos Verás de dónde vino la idea.


69
2018-04-27 22:57



onKeyDown () y onBackPressed () no funciona para este caso Tienes que usar onKeyPreIme.

Inicialmente, debe crear 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 lo suficiente para resolver su problema. Esta solución funciona para mí perfectamente.

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

58
2018-02-25 12:46



Descubrí, que anulando el dispatchKeyEventPreIme método de la clase de diseño también funciona bien. Simplemente configure su actividad principal como un atributo e inicie un método predefinido.

public class LinearLayoutGradient extends LinearLayout {
    MainActivity a;

    public void setMainActivity(MainActivity a) {
        this.a = a;
    }

    @Override
    public boolean dispatchKeyEventPreIme(KeyEvent event) {
        if (a != null) {
            InputMethodManager imm = (InputMethodManager) a
                .getSystemService(Context.INPUT_METHOD_SERVICE);

            if (imm.isActive() && event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
                a.launchMethod;
            }
        }

        return super.dispatchKeyEventPreIme(event);
    }
}

9
2018-06-05 18:14



Tuve éxito anulando dispatchKeyEvent:

@Override
public boolean dispatchKeyEvent(KeyEvent event) {
    if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
        finish();
        return true;
    }
    return super.dispatchKeyEvent(event);
}

Oculta el teclado y finaliza la actividad.


7
2018-02-24 00:29



¿Cómo muestra el teclado suave?

Si estás usando InputMethodManager.showSoftInput(), puedes intentar pasar en un ResultReceiver y la implementación onReceiveResult() manejar RESULT_HIDDEN

http://developer.android.com/reference/android/view/inputmethod/InputMethodManager.html


4
2017-10-15 07:16



Tuve el mismo problema, pero lo esquivé interceptando la tecla de retroceso. En mi caso (HTC Desire, Android 2.2, Application API Level 4) cierra el teclado e inmediatamente finaliza la actividad. No sé por qué esto no debería funcionar para usted también:

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        return true;
    }
    return super.onKeyDown(keyCode, event);
}

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        onBackPressed();
        return true;
    }
    return super.onKeyUp(keyCode, event);
}

/**
 * Called when the activity has detected the user's press of the back key
 */
private void onBackPressed() {
    Log.e(TAG, "back pressed");
    finish();
}

2
2018-03-03 11:01



Utilizar onKeyPreIme(int keyCode, KeyEvent event) método y verificación de KeyEvent.KEYCODE_BACK evento. Es muy simple sin hacer ningún código de fantasía.


1
2017-11-21 16:09



Pruebe este código en su implementación BackPressed (Block Back Button en android)

InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(myEditText.getWindowToken(), 0);

Te sugiero que eches un vistazo @ Cerrar / ocultar el teclado suave de Android


0
2017-10-22 06:40