Softwaretastatur steuern und animieren

Mit WindowInsetsCompat kann Ihre App die Bildschirmtastatur (auch IME genannt) ähnlich wie die Interaktion mit den Systemleisten abfragen und steuern. Ihre App kann auch WindowInsetsAnimationCompat verwenden, um nahtlose Übergänge zu schaffen, wenn die Softwaretastatur geöffnet oder geschlossen wird.

Abbildung 1. Zwei Beispiele für den Übergang der Softwaretastatur zum Öffnen und Schließen.

Voraussetzungen

Bevor Sie Steuerung und Animation für die Softwaretastatur einrichten, konfigurieren Sie Ihre App für die randlose Anzeige. Dadurch können Einsätze in Systemfenstern wie die Systemleisten und die Bildschirmtastatur verarbeitet werden.

Sichtbarkeit der Tastatursoftware prüfen

Verwenden Sie WindowInsets, um die Sichtbarkeit der Softwaretastatur zu 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 mit ViewCompat.setOnApplyWindowInsetsListener Änderungen an der Sichtbarkeit der Softwaretastatur 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;
});

Animationen mit der Softwaretastatur synchronisieren

Wenn ein Nutzer auf ein Texteingabefeld tippt, gleitet die Tastatur vom unteren Bildschirmrand an ihre Position, 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 die richtige Stelle gesetzt werden, anstatt mit der Animation der Tastatur zu synchronisieren. Dies kann visuell verwirrend sein.

  • Unter Android 11 (API-Level 30) und höher kannst du WindowInsetsAnimationCompat verwenden, um den Übergang der App zu synchronisieren. Dabei schiebt die Tastatur vom unteren Bildschirmrand nach oben und unten. Dies sieht reibungsloser aus, wie im Beispiel mit dem Label „Synchronisiert“ in Abbildung 2 dargestellt.

Konfigurieren Sie WindowInsetsAnimationCompat.Callback für die 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, nämlich onPrepare(), onStart(), onProgress() und onEnd(). Rufen Sie zuerst onPrepare() auf, bevor sich das Layout ändert.

onPrepare wird aufgerufen, wenn eine Animation beginnt und bevor die Ansichten aufgrund einer Animation neu angeordnet werden. Sie können damit den Startstatus speichern. In diesem Fall ist dies die untere Koordinate der Ansicht.

Ein Bild, auf dem die untere Koordinate des Startstatus 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 eingefügte Animation beginnt. Damit können Sie für alle Ansichtseigenschaften den Endzustand der Layoutänderungen festlegen. Wenn ein OnApplyWindowInsetsListener-Callback auf eine der Ansichten festgelegt ist, wird er an dieser Stelle bereits aufgerufen. An dieser Stelle empfiehlt es sich, den Endstatus der Ansichtsattribute 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 während der Ausführung einer Animation ändern. Sie können den Wert also überschreiben und während der Tastaturanimation bei jedem Frame benachrichtigt werden. Aktualisieren Sie die Ansichtsattribute, damit die Ansicht mit der Tastatur synchronisiert wird.

Alle Layoutänderungen sind jetzt abgeschlossen. Wenn Sie beispielsweise View.translationY zum Verschieben der Ansicht verwenden, wird der Wert bei jedem Aufruf dieser Methode allmählich verringert und erreicht schließlich 0 an der ursprünglichen 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 Abschluss der Animation aufgerufen. Dies ist ein guter Zeitpunkt, um vorübergehende Änderungen zu bereinigen.

Weitere Informationen