Steruj klawiaturą programową i korzystaj z animacji

Korzystając z WindowInsetsCompat, aplikacja może używać klawiatury ekranowej (zwanej też IME) podobny do sposób jego interakcji z paskami systemowymi. Aplikacja może też używać tych funkcji: WindowInsetsAnimationCompat w celu utworzenia płynnych przejść po otwarciu lub zamknięciu klawiatury programowej.

. Rysunek 1. Dwa przykłady klawiatury programowej i otwartym lub zamkniętym przejściem.

Wymagania wstępne

Przed skonfigurowaniem elementów sterujących i animacji dla klawiatury programowej skonfiguruj wyświetlanie w całości od krawędzi do krawędzi. Dzięki temu obsługuje wstawki okien systemowych, takie jak paski systemowe i klawiaturę ekranową.

Sprawdź widoczność oprogramowania klawiatury

Użyj WindowInsets, aby sprawdzić oprogramowanie widoczność klawiatury.

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;

Możesz też użyć ViewCompat.setOnApplyWindowInsetsListener by obserwować zmiany w widoczności klawiatury programowej.

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

Synchronizuj animację z klawiaturą programową

Gdy użytkownik klika pole do wprowadzania tekstu, klawiatura przesuwa się z jak widać w tym przykładzie:

. Rysunek 2. Animacja zsynchronizowanej klawiatury.
  • Przykład oznaczony jako „Niezsynchronizowana” na ilustracji 2 pokazuje domyślne zachowanie w Androidzie 10 (poziom interfejsu API 29), w którym pole tekstowe i zawartość aplikacji wskoczy na miejsce, zamiast synchronizować się z klawiaturą animacja – zachowanie, które może być atrakcyjne wizualnie.

  • W Androidzie 11 (poziom API 30) i nowszym możesz używać WindowInsetsAnimationCompat, aby zsynchronizować przenoszenie aplikacji z: klawiaturę przesuwa się w górę i w dół ekranu. Wygląda tak jak w przykładzie z etykietą „Zsynchronizowane”. na ilustracji 2.

.

Skonfiguruj WindowInsetsAnimationCompat.Callback w widoku, który ma być zsynchronizowany z animacją klawiatury.

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

Istnieje kilka metod zastępowania ustawień w WindowInsetsAnimationCompat.Callback, mianowicie onPrepare(), onStart(), onProgress() oraz onEnd(). Zacznij od wywołania onPrepare() przed zmianą układu.

Funkcja onPrepare jest wywoływana, gdy rozpoczyna się animacja wcięcia i przed wyświetleniami. są rozłożone z powodu animacji. Można w nim zapisać stan początkowy, czyli w tym przypadku dolna współrzędna widoku.

Obraz przedstawiający dolną współrzędną stanu początkowego widoku głównego.
Rysunek 3. Używasz onPrepare() do: rejestrować stan początkowy.

Ten fragment kodu zawiera przykładowe wywołanie funkcji 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();
}

Funkcja onStart jest wywoływana, gdy rozpoczyna się animacja wcięcia. Możesz użyć tej opcji, aby ustawić wszystkie właściwości widoku, aby zmienić stan końcowy. Jeśli posiadasz Wywołanie zwrotne OnApplyWindowInsetsListener zostało ustawione na dowolny z widoków, jest już w tym miejscu. To dobry moment na zapisanie stanu końcowego widoku usług.

Obraz przedstawiający dolną współrzędną stanu końcowego widoku
Rysunek 4. Nagrywanie: onStart() o stanie końcowym.

Ten fragment kodu zawiera przykładowe wywołanie funkcji 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;
}

Funkcja onProgress jest wywoływana, gdy wstawki zmieniają się w trakcie animacji. Można je więc zastąpić i otrzymywać powiadomienia przy każdej ramce podczas korzystania z klawiatury. animację. Zaktualizuj właściwości widoku, tak aby był animowany synchronizację z klawiaturą.

Na tym etapie wszystkie zmiany układu są zakończone. Na przykład, jeśli używasz View.translationY, aby przesunąć widok; wartość stopniowo się zmniejsza przy każdym i ostatecznie dochodzi do 0 do pierwotnej pozycji układu.

. Rysunek 5. Używasz onProgress() do: synchronizują animacje.

Ten fragment kodu zawiera przykładowe wywołanie funkcji 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;
}

Możesz też zastąpić zasadę onEnd. Ta metoda jest wywoływana po animacji koniec. To dobry moment na usunięcie wszelkich tymczasowych zmian.

Dodatkowe materiały