Cómo controlar la visibilidad del método de entrada

Cuando el foco de entrada se mueve hacia adentro o hacia afuera de un campo de texto editable, Android muestra u oculta la entrada (como el teclado en pantalla) según corresponda. El sistema también decide cómo aparecen tu IU y el campo de texto por encima del método de entrada. Por ejemplo, cuando el espacio vertical de la pantalla está restringido, el campo de texto puede llenar todo el espacio por encima del método de entrada.

Para la mayoría de las apps, estos comportamientos predeterminados son todo lo que se necesita. Sin embargo, en algunos casos, es posible que desees tener más control sobre la visibilidad del método de entrada y cómo afecta al diseño. En esta lección, se explica cómo controlar la visibilidad del método de entrada y responder a ella.

Mostrar el teclado en pantalla cuando comienza la actividad

Aunque Android destaca el primer campo de texto de tu diseño cuando comienza la actividad, no muestra el teclado en pantalla. Este comportamiento es apropiado porque ingresar texto podría no ser la tarea principal de la actividad. Sin embargo, si ingresar texto es la tarea principal, como en una pantalla de acceso, es probable que quieras que el teclado en pantalla aparezca de forma predeterminada.

Para mostrar el método de entrada cuando comienza tu actividad, agrega el atributo android:windowSoftInputMode al elemento <activity> con el valor "stateVisible". Por ejemplo:

<application ... >
    <activity
        android:windowSoftInputMode="stateVisible" ... >
        ...
    </activity>
   ...
</application>

Cómo especificar cómo debe responder tu IU

Cuando el teclado en pantalla aparece en la pantalla, se reduce la cantidad de espacio disponible para la IU de tu app. El sistema decide cómo ajustar la parte visible de la IU, pero es posible que no lo haga bien. Para garantizar el mejor comportamiento para tu app, especifica cómo quieres que el sistema muestre tu IU en el espacio restante.

Para declarar tu tratamiento preferido en una actividad, usa el atributo android:windowSoftInputMode en el elemento <activity> de tu manifiesto con uno de los valores de "ajuste".

Por ejemplo, para asegurarte de que el sistema cambie el tamaño de tu diseño al espacio disponible, lo que permite que se pueda acceder a todo el contenido de tu diseño, incluso si requiere desplazamiento, usa "adjustResize":

<application ... >
   <activity
       android:windowSoftInputMode="adjustResize" ... >
       ...
   </activity>
   ...
</application>

Puedes combinar la especificación de ajuste con la especificación de visibilidad inicial del teclado en pantalla de la sección anterior:

<activity
    android:windowSoftInputMode="stateVisible|adjustResize" ... >
    ...
</activity>

Especificar "adjustResize" es importante si la IU incluye controles a los que el usuario podría necesitar acceder inmediatamente después o mientras realiza una entrada de texto. Por ejemplo, si usas un diseño relativo para colocar una barra de botones en la parte inferior de la pantalla, con "adjustResize" cambia el tamaño del diseño para que la barra de botones aparezca sobre el teclado en pantalla.

Cómo mostrar el teclado en pantalla a pedido

Si hay un método en el ciclo de vida de tu actividad en el que deseas asegurarte de que el método de entrada sea visible, puedes usar InputMethodManager para mostrarlo.

Por ejemplo, el siguiente método toma un objeto View, en el que se espera que el usuario escriba algo, llama a requestFocus() para enfocarlo y, luego, llama a showSoftInput() para abrir el método de entrada:

Kotlin

fun showSoftKeyboard(view: View) {
   if (view.requestFocus()) {
       val imm = getSystemService(InputMethodManager::class.java)
       imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT)
   }
}

Java

public void showSoftKeyboard(View view) {
   if (view.requestFocus()) {
       InputMethodManager imm = getSystemService(InputMethodManager.class);
       imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
   }
}

Cómo mostrar el teclado en pantalla de manera confiable

Hay ciertas situaciones, como cuando se inicia una actividad, en las que el uso de InputMethodManager.showSoftInput() para mostrar el teclado en pantalla puede hacer que el teclado en pantalla no sea visible para el usuario.

La visibilidad del teclado en pantalla cuando se usa showSoftInput() depende de las siguientes condiciones:

  • La vista ya debe estar conectada al teclado en pantalla. (Esto, a su vez, requiere que se enfoque la ventana y la vista de editor solicite el enfoque de vista con View.requestFocus()).

  • La visibilidad también puede verse afectada por el atributo android:windowSoftInputMode y las marcas que usa showSoftInput().

En ciertos casos de uso, como cuando se inicia una actividad, no se cumplen algunas de estas condiciones obligatorias. El sistema no considera que la vista esté conectada al teclado en pantalla, ignora la llamada a showSoftInput(), y el usuario no ve el teclado en pantalla.

Para asegurarte de que el teclado en pantalla se muestre de manera confiable, puedes usar las siguientes alternativas:

  • Usa WindowInsetsControllerCompat(recomendado). Este objeto muestra el teclado en pantalla durante Activity.onCreate(), como se indica en el siguiente fragmento de código. Se garantiza que la llamada se programará una vez que la ventana se enfoque.

Kotlin

editText.requestFocus()
WindowCompat.getInsetsController(window, editText)!!.show(WindowInsetsCompat.Type.ime())

Java

editText.requestFocus();
WindowCompat.getInsetsController(getWindow(), editText).show(WindowInsetsCompat.Type.ime());

Kotlin

class MyEditText : EditText() {
  ...
  override fun onWindowFocusChanged(hasWindowFocus: Boolean) {
    if (hasWindowFocus) {
      requestFocus()
      post {
        val imm: InputMethodManager = getSystemService(InputMethodManager::class.java)
        imm.showSoftInput(this, 0)
      }
    }
  }
}

Java

public class MyEditText extends EditText {
  ...
  @Override
  public void onWindowFocusChanged(boolean hasWindowFocus) {
    if (hasWindowFocus) {
      requestFocus();
      post(() -> {
        InputMethodManager imm = getSystemService(InputMethodManager.class);
        imm.showSoftInput(this, 0);
      });
    }
  }
}

Maneja con cuidado las marcas de visibilidad del entorno de ejecución

Cuando actives la visibilidad del teclado en pantalla durante el tiempo de ejecución, ten cuidado de no pasar ciertos valores de marcas a estos métodos. Por ejemplo, si la aplicación espera que aparezca el teclado en pantalla cuando se llama a View.getWindowInsetsController().show(ime()) en Activity.onCreate() durante el inicio de la actividad, los desarrolladores de aplicaciones deben tener cuidado de no configurar las marcas SOFT_INPUT_STATE_HIDDEN o SOFT_INPUT_STATE_ALWAYS_HIDDEN durante el inicio inicial en caso de que el teclado en pantalla se oculte de forma inesperada.

Por lo general, el sistema oculta el teclado en pantalla automáticamente

En la mayoría de las situaciones, el sistema oculta el teclado en pantalla. Puede ser cualquiera de los siguientes casos:

  • El usuario finaliza la tarea en el campo de texto.
  • El usuario presiona la tecla Atrás o los gestos de deslizamiento con la navegación hacia atrás.
  • El usuario navega a otra app, y esa otra app estableció las marcas SOFT_INPUT_STATE_HIDDEN o SOFT_INPUT_STATE_ALWAYS_HIDDEN cuando la vista obtiene el foco.

Ocultar manualmente el teclado en pantalla según el comportamiento anterior del sistema

Tu app debe ocultar el teclado en pantalla de forma manual en algunas situaciones, por ejemplo, cuando el campo de texto pierde el foco en View.OnFocusChangeListener.onFocusChange. Usa esta técnica con criterio, ya que cerrar el teclado en pantalla perjudica de forma inesperada la experiencia del usuario.

Si tu app oculta el teclado en pantalla de forma manual, debes saber si este se mostró de manera explícita o implícita:

  • Se considera que el teclado en pantalla se mostró explícitamente después de una llamada a showSoftInput().

  • Por el contrario, se considera que el teclado en pantalla se mostró implícitamente en cualquiera de las siguientes condiciones:

Por lo general, hideSoftInputFromWindow() oculta el teclado en pantalla independientemente de cómo se solicitó, pero con HIDE_IMPLICIT_ONLY puede limitarse a descartar únicamente un teclado en pantalla solicitado de manera implícita.

Mostrar un diálogo o una vista superpuesta sobre el teclado en pantalla

En algunas situaciones, es posible que la actividad del editor deba crear un diálogo no editable o una ventana superpuesta sobre el teclado en pantalla.

Tu app tiene algunas opciones que se describen en las siguientes secciones.

En resumen, asegúrate de controlar correctamente las marcas de ventana del teclado en pantalla que se orientan a la ventana, de modo que cumpla con las siguientes expectativas con respecto al orden vertical (capa Z):

  • Sin marcas (mayúsculas y minúsculas): Detrás de la capa de teclado en pantalla, y puede recibir texto.
  • FLAG_NOT_FOCUSABLE: Se superpone a la capa de teclado en pantalla, pero no puede recibir texto.
  • FLAG_ALT_FOCUSABLE_IM: Puede enfocarse en la capa de teclado en pantalla, pero no está conectado al teclado. También bloquea la conexión con el teclado en pantalla para todas las vistas que estén por debajo. Esto es útil para mostrar un diálogo de la app que no usa entrada de texto sobre la capa del teclado en pantalla.
  • FLAG_NOT_FOCUSABLE y FLAG_ALT_FOCUSABLE_IM: Detrás de la capa de teclado en pantalla, pero no puede recibir texto.
  • FLAG_NOT_FOCUSABLE y FLAG_NOT_TOUCH_MODAL: En la parte superior del teclado en pantalla, permite que los eventos táctiles pasen "por" la ventana al teclado en pantalla.

Crea un diálogo

Usa la marca FLAG_ALT_FOCUSABLE_IM de la ventana de diálogo para mantener el diálogo sobre el teclado en pantalla y evitar que este se enfoque:

Kotlin

val content = TextView(this)
content.text = "Non-editable dialog on top of soft keyboard"
content.gravity = Gravity.CENTER
val builder = AlertDialog.Builder(this)
  .setTitle("Soft keyboard layering demo")
  .setView(content)
mDialog = builder.create()
mDialog!!.window!!
  .addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)
mDialog!!.show()

Java

TextView content = new TextView(this);
content.setText("Non-editable dialog on top of soft keyboard");
content.setGravity(Gravity.CENTER);
final AlertDialog.Builder builder = new AlertDialog.Builder(this)
    .setTitle("Soft keyboard layering demo")
    .setView(content);
mDialog = builder.create();
mDialog.getWindow().addFlags(FLAG_ALT_FOCUSABLE_IM);
mDialog.show();

Cómo crear una vista superpuesta

Crea una vista superpuesta que especifique el tipo de ventana TYPE_APPLICATION_OVERLAY y la marca de ventana FLAG_ALT_FOCUSABLE_IM por la actividad objetivo del teclado en pantalla.

Kotlin

val params = WindowManager.LayoutParams(
  width,  /* Overlay window width */
  height,  /* Overlay window height */
  WindowManager.LayoutParams.TYPE_APPLICATION, /* Overlay window type */
  WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM /* No need to allow for text input on top of the soft keyboard */
    or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,  /* Allow touch event send to soft keyboard behind the overlay */
  PixelFormat.TRANSLUCENT
)
params.title = "Overlay window"
mOverlayView!!.layoutParams = params
windowManager.addView(mOverlayView, params)

Java

WindowManager.LayoutParams params = new WindowManager.LayoutParams(
    width, /* Overlay window width */
    height, /* Overlay window height */
    TYPE_APPLICATION, /* Overlay window type */
    FLAG_ALT_FOCUSABLE_IM /* No need to allow for text input on top of the soft keyboard */
        | FLAG_NOT_TOUCH_MODAL, /* Allow touch event send to soft keyboard behind the overlay */
    PixelFormat.TRANSLUCENT);
params.setTitle("Overlay window");
mOverlayView.setLayoutParams(params);
getWindowManager().addView(mOverlayView, params);

Mostrar un diálogo o una vista debajo del teclado en pantalla

Es posible que tu app necesite crear un diálogo o una ventana con las siguientes propiedades:

  • Aparece debajo del teclado en pantalla solicitado por una actividad del editor de modo que no se vea afectado por la entrada de texto.
  • Mantiene al tanto de los cambios en el tamaño de la inserción del teclado en pantalla para ajustar el diseño del diálogo o la ventana.

En este caso, tu app tiene varias opciones. En las siguientes secciones, se describen estas opciones.

Crea un diálogo

Para crear un diálogo, configura la marca FLAG_NOT_FOCUSABLE y la marca de ventana FLAG_ALT_FOCUSABLE_IM:

Kotlin

val content = TextView(this)
content.text = "Non-editable dialog behind soft keyboard"
content.gravity = Gravity.CENTER
val builder = AlertDialog.Builder(this)
  .setTitle("Soft keyboard layering demo")
  .setView(content)
mDialog = builder.create()
mDialog!!.window!!
  .addFlags(FLAG_NOT_FOCUSABLE or FLAG_ALT_FOCUSABLE_IM)
mDialog!!.show()

Java

TextView content = new TextView(this);
content.setText("Non-editable dialog behind soft keyboard");
content.setGravity(Gravity.CENTER);
final AlertDialog.Builder builder = new AlertDialog.Builder(this)
    .setTitle("Soft keyboard layering demo")
    .setView(content);

mDialog = builder.create();
mDialog.getWindow()
    .addFlags(FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
mDialog.show();

Cómo crear una vista superpuesta

Para crear una vista de superposición, configura la marca de ventana FLAG_NOT_FOCUSABLE y la marca de ventana FLAG_ALT_FOCUSABLE_IM:

Kotlin

val params = WindowManager.LayoutParams(
  width,  /* Overlay window width */
  height,  /* Overlay window height */
  WindowManager.LayoutParams.TYPE_APPLICATION,  /* Overlay window type */
  WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
      or WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
  PixelFormat.TRANSLUCENT
)
params.title = "Overlay window"
mOverlayView!!.layoutParams = params
windowManager.addView(mOverlayView, params)

Java

WindowManager.LayoutParams params = new WindowManager.LayoutParams(
    width, /* Overlay window width */
    height, /* Overlay window height */
    TYPE_APPLICATION, /* Overlay window type */
    FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM,
    PixelFormat.TRANSLUCENT);
params.setTitle("Overlay window");
mOverlayView.setLayoutParams(params);
getWindowManager().addView(mOverlayView, params);