Softwaretastatur steuern und animieren

Mit WindowInsetsCompat kann Ihre App die Bildschirmtastatur (auch IME genannt) abfragen und steuern, ähnlich wie bei der Interaktion mit den Systemleisten. Deine App kann auch WindowInsetsAnimationCompat verwenden, um nahtlose Übergänge beim Öffnen oder Schließen der Softwaretastatur zu schaffen.

Abbildung 1. Zwei Beispiele für den Übergang der Softwaretastatur (offen/geschlossen).

Voraussetzungen

Bevor Sie die Steuerung und Animation für die Softwaretastatur einrichten, müssen Sie Ihre App so konfigurieren, dass sie randlos angezeigt wird. Auf diese Weise können Einfügungen des Systemfensters wie die Systemleisten und die Bildschirmtastatur verarbeitet werden.

Sichtbarkeit der Tastatursoftware prüfen

Mit WindowInsets kannst du die Sichtbarkeit der Softwaretastatur prüfen.

Kotlin

val insets = ViewCompat.getRootWindowInsets(view) ?: return
val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom

Java

WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(view);
boolean imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime());
int imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;

Alternativ können Sie ViewCompat.setOnApplyWindowInsetsListener verwenden, um Änderungen an der Sichtbarkeit der Softwaretastatur zu beobachten.

Kotlin

ViewCompat.setOnApplyWindowInsetsListener(view) { _, insets ->
  val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
  val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
  insets
}

Java

ViewCompat.setOnApplyWindowInsetsListener(view, (v, insets) -> {
  boolean imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime());
  int imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;
  return insets;
});

Animation mit der Bildschirmtastatur synchronisieren

Wenn ein Nutzer auf ein Texteingabefeld tippt, schiebt sich die Tastatur vom unteren Bildschirmrand ein, wie im folgenden Beispiel gezeigt:

Abbildung 2: Synchronisierte Tastaturanimation.
  • Das Beispiel mit der Bezeichnung „Nicht synchronisiert“ in Abbildung 2 zeigt das Standardverhalten in Android 10 (API-Ebene 29), bei dem das Textfeld und der Inhalt der App an der richtigen Stelle einrasten, anstatt mit der Animation der Tastatur zu synchronisieren. Dies kann visuell verwirrend sein.

  • Ab Android 11 (API-Level 30) kannst du WindowInsetsAnimationCompat verwenden, um den Übergang der App zu synchronisieren, wobei die Tastatur vom unteren Bildschirmrand nach oben und unten gleitet. Dies sieht gleichmäßiger aus, wie in dem Beispiel mit der Bezeichnung „Synchronisiert“ in Abbildung 2 gezeigt.

Konfigurieren Sie WindowInsetsAnimationCompat.Callback mit der Ansicht, die mit der Tastaturanimation synchronisiert werden soll.

Kotlin

ViewCompat.setWindowInsetsAnimationCallback(
  view,
  object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_STOP) {
    // Override methods.
  }
)

Java

ViewCompat.setWindowInsetsAnimationCallback(
    view,
    new WindowInsetsAnimationCompat.Callback(
        WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_STOP
    ) {
      // Override methods.
    });

In WindowInsetsAnimationCompat.Callback gibt es mehrere Methoden zum Überschreiben: onPrepare(), onStart(), onProgress() und onEnd(). Rufe zuerst onPrepare() auf, bevor du das Layout änderst.

onPrepare wird aufgerufen, wenn eine Insets-Animation gestartet wird und bevor die Ansichten aufgrund einer Animation wieder eingeblendet werden. Sie können damit den Startstatus speichern, in diesem Fall die untere Koordinate der Ansicht.

Ein Bild, auf dem die untere Koordinate des Startzustands der Stammansicht zu sehen ist.
Abbildung 3: Verwenden Sie onPrepare(), um den Startstatus aufzuzeichnen.

Das folgende Snippet zeigt einen Beispielaufruf für onPrepare:

Kotlin

var startBottom = 0f

override fun onPrepare(
  animation: WindowInsetsAnimationCompat
) {
  startBottom = view.bottom.toFloat()
}

Java

float startBottom;

@Override
public void onPrepare(
    @NonNull WindowInsetsAnimationCompat animation
) {
  startBottom = view.getBottom();
}

onStart wird aufgerufen, wenn eine Einfügungsanimation gestartet wird. Sie können damit alle Ansichtseigenschaften auf den Endstatus der Layoutänderungen setzen. Wenn ein OnApplyWindowInsetsListener-Callback auf eine der Ansichten festgelegt ist, wird er an diesem Punkt bereits aufgerufen. An dieser Stelle empfiehlt es sich, den Endstatus der Ansichtseigenschaften zu speichern.

Ein Bild, das die untere Koordinate des Endzustands der Ansicht zeigt
Abbildung 4: Verwenden Sie onStart(), um den Endzustand aufzuzeichnen.

Das folgende Snippet zeigt einen Beispielaufruf für onStart:

Kotlin

var endBottom = 0f

override fun onStart(
  animation: WindowInsetsAnimationCompat,
  bounds: WindowInsetsAnimationCompat.BoundsCompat
): WindowInsetsAnimationCompat.BoundsCompat {
  // Record the position of the view after the IME transition.
  endBottom = view.bottom.toFloat()

  return bounds
}

Java

float endBottom;

@NonNull
@Override
public WindowInsetsAnimationCompat.BoundsCompat onStart(
    @NonNull WindowInsetsAnimationCompat animation,
    @NonNull WindowInsetsAnimationCompat.BoundsCompat bounds
) {
  endBottom = view.getBottom();
  return bounds;
}

onProgress wird aufgerufen, wenn sich die Einfügungen im Rahmen der Ausführung einer Animation ändern. Sie können sie also überschreiben und während der Tastaturanimation bei jedem Frame eine Benachrichtigung erhalten. Aktualisieren Sie die Ansichtseigenschaften, damit die Ansicht synchron mit der Tastatur animiert wird.

Alle Layoutänderungen sind jetzt abgeschlossen. Wenn Sie beispielsweise View.translationY verwenden, um die Ansicht zu verschieben, nimmt der Wert für jeden Aufruf dieser Methode allmählich ab und erreicht schließlich 0 an die ursprüngliche Layoutposition.

Abbildung 5. Verwenden Sie onProgress(), um die Animationen zu synchronisieren.

Das folgende Snippet zeigt einen Beispielaufruf für onProgress:

Kotlin

override fun onProgress(
  insets: WindowInsetsCompat,
  runningAnimations: MutableList<WindowInsetsAnimationCompat>
): WindowInsetsCompat {
  // Find an IME animation.
  val imeAnimation = runningAnimations.find {
    it.typeMask and WindowInsetsCompat.Type.ime() != 0
  } ?: return insets

  // Offset the view based on the interpolated fraction of the IME animation.
  view.translationY =
    (startBottom - endBottom) * (1 - imeAnimation.interpolatedFraction)

  return insets
}

Java

@NonNull
@Override
public WindowInsetsCompat onProgress(
    @NonNull WindowInsetsCompat insets,
    @NonNull List<WindowInsetsAnimationCompat> runningAnimations
) {
  // Find an IME animation.
  WindowInsetsAnimationCompat imeAnimation = null;
  for (WindowInsetsAnimationCompat animation : runningAnimations) {
    if ((animation.getTypeMask() & WindowInsetsCompat.Type.ime()) != 0) {
      imeAnimation = animation;
      break;
    }
  }
  if (imeAnimation != null) {
    // Offset the view based on the interpolated fraction of the IME animation.
    view.setTranslationY((startBottom - endBottom)

        *   (1 - imeAnimation.getInterpolatedFraction()));
  }
  return insets;
}

Optional können Sie onEnd überschreiben. Diese Methode wird nach der Animation aufgerufen. Dies ist ein guter Zeitpunkt, um alle temporären Änderungen zu entfernen.

Zusätzliche Ressourcen