Softwaretastatur steuern und animieren

Mit WindowInsetsCompat kann Ihre App die Bildschirmtastatur (auch IME genannt) ähnlich wie die Systemleisten abfragen und steuern. In Ihrer App kann auch das Symbol WindowInsetsAnimationCompat verwendet werden, um nahtlose Übergänge beim Öffnen oder Schließen der Softwaretastatur zu ermöglichen.

Abbildung 1. Zwei Beispiele für den Übergang von geöffneter zu geschlossener Softwaretastatur.

Voraussetzungen

Bevor Sie die Steuerung und Animation für die Softwaretastatur einrichten, konfigurieren Sie Ihre App so, dass sie randlos angezeigt wird. So können Systemfenster-Einsätze wie die Systemleisten und die Bildschirmtastatur berücksichtigt werden.

Sichtbarkeit der Tastatursoftware prüfen

Mit WindowInsets können Sie 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 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;
});

Animation mit der Softwaretastatur synchronisieren

Wenn ein Nutzer auf ein Textfeld tippt, wird die Tastatur von unten auf dem Bildschirm eingeblendet, wie im folgenden Beispiel gezeigt:

Abbildung 2. Synchronisierte Tastaturanimation.
  • Das Beispiel mit der Beschriftung „Nicht synchronisiert“ in Abbildung 2 zeigt das Standardverhalten in Android 10 (API-Ebene 29). Dabei werden das Textfeld und der Inhalt der App an ihre Position gebracht, anstatt sich mit der Tastaturanimation zu synchronisieren. Das kann optisch irritierend wirken.

  • Unter Android 11 (API-Level 30) und höher können Sie mit WindowInsetsAnimationCompat den Übergang der App mit der Tastatur synchronisieren, die vom unteren Displayrand nach oben und unten bewegt wird. Das sieht glatter aus, wie in Abbildung 2 im Beispiel mit der Beschriftung „Synchronisiert“ zu sehen ist.

Konfigurieren Sie WindowInsetsAnimationCompat.Callback so, dass die Ansicht mit der Tastaturanimation synchronisiert wird.

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

Es gibt mehrere Methoden, die in WindowInsetsAnimationCompat.Callback überschrieben werden können: onPrepare(), onStart(), onProgress() und onEnd(). Rufen Sie zuerst onPrepare() auf, bevor sich das Layout ändert.

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

Ein Bild, das die untere Koordinate des Startstatus der Stammansicht zeigt
Abbildung 3. Verwenden Sie onPrepare(), um den Startstatus aufzuzeichnen.

Das folgende Snippet zeigt einen Beispielaufruf von 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 Einblendungsanimation beginnt. Damit können Sie alle Ansichtseigenschaften auf den Endstatus der Layoutänderungen festlegen. Wenn für eine der Ansichten ein OnApplyWindowInsetsListener-Callback festgelegt ist, wird er an dieser Stelle bereits aufgerufen. Jetzt ist ein guter Zeitpunkt, den Endstatus der Ansichtseigenschaften zu speichern.

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

Das folgende Snippet zeigt einen Beispielaufruf von 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 Einzüge während der Ausführung einer Animation ändern. Sie können sie also überschreiben und bei jedem Frame während der Tastaturanimation benachrichtigt werden. Aktualisieren Sie die Ansichtseigenschaften so, dass 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 bei jedem Aufruf dieser Methode allmählich ab und erreicht schließlich 0, die ursprüngliche Layoutposition.

Abbildung 5 Mit onProgress() die Animationen synchronisieren

Das folgende Snippet zeigt einen Beispielaufruf von 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. Jetzt ist ein guter Zeitpunkt, um alle temporären Änderungen rückgängig zu machen.

Weitere Informationen