Gérer la visibilité du mode de saisie

Lorsque la sélection de la saisie se déplace à l'intérieur ou en dehors d'un champ de texte modifiable, Android affiche ou masque l'entrée, comme le clavier à l'écran, selon les besoins. Le système décide également de la manière dont votre interface utilisateur et le champ de texte apparaissent au-dessus du mode de saisie. Par exemple, lorsque l'espace vertical à l'écran est limité, le champ de texte peut remplir tout l'espace au-dessus de la méthode de saisie.

Pour la plupart des applications, ces comportements par défaut suffisent. Toutefois, dans certains cas, vous souhaiterez peut-être mieux contrôler la visibilité de la méthode de saisie et son impact sur la mise en page. Cette leçon explique comment contrôler la visibilité du mode de saisie et y répondre.

Afficher le clavier virtuel au début de l'activité

Bien qu'Android place le curseur sur le premier champ de texte de votre mise en page au début de l'activité, il n'affiche pas le clavier virtuel. Ce comportement est approprié, car la saisie de texte peut ne pas être la tâche principale de l'activité. Toutefois, si la saisie de texte est effectivement la tâche principale, comme dans un écran de connexion, vous souhaiterez probablement que le clavier virtuel s'affiche par défaut.

Pour afficher le mode de saisie au début de l'activité, ajoutez l'attribut android:windowSoftInputMode à l'élément <activity> avec la valeur "stateVisible". Par exemple :

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

Spécifiez la manière dont votre UI doit répondre

Lorsque le clavier virtuel apparaît à l'écran, il réduit l'espace disponible pour l'interface utilisateur de votre application. Le système décide de la manière d'ajuster la partie visible de votre interface utilisateur, mais il peut ne pas fonctionner correctement. Pour garantir un comportement optimal de votre application, spécifiez la manière dont vous souhaitez que le système affiche votre UI dans l'espace restant.

Pour déclarer votre traitement préféré dans une activité, utilisez l'attribut android:windowSoftInputMode dans l'élément <activity> de votre fichier manifeste avec l'une des valeurs "adjust".

Par exemple, pour vous assurer que le système redimensionne votre mise en page en fonction de l'espace disponible, ce qui permet d'accéder à l'ensemble de son contenu, même s'il nécessite un défilement, utilisez "adjustResize":

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

Vous pouvez combiner la spécification d'ajustement avec la spécification de visibilité initiale du clavier virtuel de la section précédente:

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

La spécification de "adjustResize" est importante si votre interface utilisateur inclut des commandes auxquelles l'utilisateur peut avoir besoin d'accéder immédiatement après ou pendant la saisie de texte. Par exemple, si vous utilisez une mise en page relative pour placer une barre de boutons en bas de l'écran, "adjustResize" redimensionne la mise en page afin que la barre de boutons apparaisse au-dessus du clavier virtuel.

Afficher le clavier virtuel à la demande

Si le cycle de vie de votre activité comporte une méthode pour laquelle vous souhaitez vous assurer que le mode d'entrée est visible, vous pouvez l'afficher à l'aide de InputMethodManager.

Par exemple, la méthode suivante utilise un objet View dans lequel l'utilisateur est censé saisir quelque chose, appelle requestFocus() pour le cibler, puis showSoftInput() pour ouvrir le mode de saisie:

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

Afficher le clavier virtuel de manière fiable

Dans certains cas, par exemple au démarrage d'une activité, l'utilisation de InputMethodManager.showSoftInput() pour afficher le clavier virtuel peut empêcher l'utilisateur de voir le clavier virtuel.

La visibilité du clavier virtuel lorsque vous utilisez showSoftInput() dépend des conditions suivantes:

Dans certains cas d'utilisation, par exemple au démarrage d'une activité, certaines de ces conditions requises ne sont pas remplies. Le système ne considère pas la vue comme connectée au clavier virtuel, ignore l'appel showSoftInput() et l'utilisateur ne peut pas voir le clavier virtuel.

Pour vous assurer que le clavier virtuel s'affiche de manière fiable, vous pouvez utiliser les alternatives suivantes:

  • (Recommandé) Utilisez WindowInsetsControllerCompat. Cet objet affiche le clavier virtuel pendant Activity.onCreate(), comme indiqué dans l'extrait de code suivant. La planification de l'appel est garantie une fois que la fenêtre est sélectionnée.

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

Gérer les indicateurs de visibilité de l'environnement d'exécution avec soin

Lorsque vous activez/désactivez la visibilité du clavier virtuel au moment de l'exécution, veillez à ne pas transmettre certaines valeurs d'indicateurs à ces méthodes. Par exemple, si l'application s'attend à ce que le clavier virtuel s'affiche lors de l'appel de View.getWindowInsetsController().show(ime()) dans Activity.onCreate() pendant le démarrage de l'activité, les développeurs de l'application doivent veiller à ne pas définir d'indicateurs SOFT_INPUT_STATE_HIDDEN ou SOFT_INPUT_STATE_ALWAYS_HIDDEN lors du lancement initial au cas où le clavier virtuel serait masqué de manière inattendue.

Le système masque automatiquement le clavier virtuel

Dans la plupart des cas, le système dispose de poignées pour masquer le clavier virtuel. Voici les différents cas de figure possibles:

  • L'utilisateur termine la tâche dans le champ de texte.
  • L'utilisateur appuie sur la touche Retour ou effectue des gestes de balayage avec la navigation vers l'arrière.
  • L'utilisateur accède à une autre application qui a défini des indicateurs SOFT_INPUT_STATE_HIDDEN ou SOFT_INPUT_STATE_ALWAYS_HIDDEN lorsque la vue est sélectionnée.

Masquer manuellement le clavier virtuel en fonction du comportement précédent du système

Votre application doit masquer le clavier virtuel manuellement dans certaines situations, par exemple lorsque le champ de texte perd son focus dans View.OnFocusChangeListener.onFocusChange. Utilisez cette technique judicieusement. La fermeture du clavier virtuel nuit à l'expérience utilisateur de manière inattendue.

Si votre application masque manuellement le clavier virtuel, vous devez savoir si celui-ci a été affiché explicitement ou implicitement:

  • Le clavier virtuel est considéré comme ayant été explicitement affiché après un appel à showSoftInput().

  • À l'inverse, le clavier virtuel est considéré comme ayant été affiché implicitement dans l'une des conditions suivantes:

    • Le système a affiché le clavier virtuel lors de l'application de android:windowSoftInputMode.
    • Votre application a transmis SHOW_IMPLICIT à showSoftInput().

Normalement, hideSoftInputFromWindow() masque le clavier virtuel, quelle que soit la façon dont il a été demandé, mais avec HIDE_IMPLICIT_ONLY, il peut se limiter à ignorer uniquement un clavier virtuel demandé implicitement.

Afficher une boîte de dialogue ou une vue en superposition sur le clavier virtuel

Dans certains cas, l'activité de l'éditeur peut avoir besoin de créer une boîte de dialogue ou une fenêtre de superposition non modifiable au-dessus du clavier virtuel.

Votre application comporte plusieurs options, décrites dans les sections suivantes.

En résumé, assurez-vous de gérer correctement les indicateurs de fenêtre du clavier virtuel ciblant la fenêtre afin qu'elle réponde aux attentes suivantes en matière d'ordre vertical (couche z) :

  • Aucun indicateur (cas normal): derrière la couche du clavier virtuel et peut recevoir du texte.
  • FLAG_NOT_FOCUSABLE : en haut de la couche du clavier virtuel, mais ne peut pas recevoir de texte.
  • FLAG_ALT_FOCUSABLE_IM : en haut de la couche du clavier virtuel, peut être sélectionné, mais n'est pas connecté au clavier virtuel. Empêche également toutes les vues situées en dessous de se connecter au clavier virtuel. Cela est utile pour afficher une boîte de dialogue d'application qui n'utilise pas la saisie de texte au-dessus de la couche du clavier virtuel.
  • FLAG_NOT_FOCUSABLE et FLAG_ALT_FOCUSABLE_IM : situé derrière la couche du clavier virtuel, mais ne peuvent pas recevoir de texte.
  • FLAG_NOT_FOCUSABLE et FLAG_NOT_TOUCH_MODAL : en haut du clavier virtuel, cette option permet aux événements tactiles de passer "à travers" la fenêtre jusqu'au clavier virtuel.

Créer une boîte de dialogue

Utilisez l'indicateur de fenêtre de dialogue FLAG_ALT_FOCUSABLE_IM pour que la boîte de dialogue reste au-dessus du clavier virtuel et que celui-ci ne soit pas sélectionné:

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

Créer une vue en superposition

Créez une vue en superposition spécifiant le type de fenêtre TYPE_APPLICATION_OVERLAY et l'indicateur de fenêtre FLAG_ALT_FOCUSABLE_IM par l'activité ciblée sur le clavier virtuel.

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

Afficher une boîte de dialogue ou une vue sous le clavier virtuel

Votre application devra peut-être créer une boîte de dialogue ou une fenêtre comportant les propriétés suivantes:

  • Apparaît sous le clavier virtuel demandé par une activité d'éditeur, de sorte qu'elle n'est pas affectée par la saisie de texte.
  • Reste informé des modifications apportées à la taille des encarts du clavier virtuel afin d'ajuster la mise en page de la boîte de dialogue ou de la fenêtre.

Dans ce cas, votre application dispose de plusieurs options. Les sections suivantes décrivent ces options.

Créer une boîte de dialogue

Créez une boîte de dialogue en définissant à la fois l'option de fenêtre FLAG_NOT_FOCUSABLE et l'option de fenêtre 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();

Créer une vue en superposition

Créez une vue en superposition en définissant à la fois l'option de fenêtre FLAG_NOT_FOCUSABLE et l'option de fenêtre 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);