Steruj klawiaturą programową i korzystaj z animacji

Wypróbuj Compose
Jetpack Compose to zalecany zestaw narzędzi interfejsu na Androida. Dowiedz się, jak korzystać z klawiatury w trybie pisania.

Za pomocą WindowInsetsCompat aplikacja może wysyłać zapytania do klawiatury ekranowej (zwanej też IME) i sterować nią w podobny sposób jak w przypadku interakcji z paskami systemowymi. Aplikacja może też używać WindowInsetsAnimationCompat do tworzenia płynnych przejść podczas otwierania lub zamykania klawiatury ekranowej.

Rysunek 1. Dwa przykłady przejścia otwarcia i zamknięcia klawiatury ekranowej.

Wymagania wstępne

Zanim skonfigurujesz sterowanie i animację klawiatury ekranowej, skonfiguruj aplikację tak, aby wyświetlała się na całej szerokości ekranu. Dzięki temu może obsługiwać wstawki okien systemowych, takie jak paski systemowe i klawiatura ekranowa.

Sprawdzanie widoczności oprogramowania klawiatury

Sprawdź widoczność klawiatury ekranowej za pomocą WindowInsets.

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 do obserwowania zmian widoczności klawiatury ekranowej.

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

Synchronizowanie animacji z klawiaturą ekranową

Gdy użytkownik kliknie pole do wpisywania tekstu, klawiatura wysunie się z dołu ekranu, jak pokazano w tym przykładzie:

Rysunek 2. Zsynchronizowana animacja klawiatury.
  • Przykład oznaczony jako „Niesynchronizowany” na rysunku 2 pokazuje domyślne działanie w Androidzie 10 (poziom API 29), w którym pole tekstowe i treść aplikacji zatrzaskują się zamiast synchronizować z animacją klawiatury, co może być wizualnie nieprzyjemne.

  • W Androidzie 11 (poziom interfejsu API 30) i nowszym możesz użyć WindowInsetsAnimationCompat, aby zsynchronizować przejście aplikacji z wysuwaniem i chowanie klawiatury u dołu ekranu. Wygląda to płynniej, jak widać na przykładzie oznaczonym jako „Zsynchronizowane” na rysunku 2.

.

Skonfiguruj WindowInsetsAnimationCompat.Callback widok, który ma być synchronizowany 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.
    });

WindowInsetsAnimationCompat.Callback można zastąpić kilka metod, a mianowicie onPrepare(), onStart(), onProgress()onEnd(). Zacznij od wywołania onPrepare() przed wprowadzeniem jakichkolwiek zmian w układzie.

onPrepare jest wywoływana, gdy rozpoczyna się animacja wstawień i zanim widoki zostaną ponownie rozmieszczone z powodu animacji. Możesz go użyć do zapisania stanu początkowego, którym w tym przypadku jest dolny współrzędny widoku.

Ilustracja pokazująca współrzędną dolną stanu początkowego widoku głównego.
Rysunek 3. Użyj onPrepare(), aby zarejestrować stan początkowy.

Poniższy fragment kodu pokazuje 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();
}

onStart jest wywoływana, gdy rozpoczyna się animacja wstawek. Możesz go użyć, aby ustawić wszystkie właściwości widoku na stan końcowy zmian układu. Jeśli masz wywołanie zwrotne OnApplyWindowInsetsListener ustawione w dowolnym widoku, jest ono już wywoływane w tym momencie. To dobry moment, aby zapisać stan końcowy właściwości widoku.

Ilustracja pokazująca współrzędną dolną widoku w stanie końcowym
Rysunek 4. Użyj onStart(), aby nagrać stan końcowy.

Poniższy fragment kodu pokazuje 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;
}

onProgress jest wywoływana, gdy wstawki zmieniają się w ramach animacji, więc możesz ją zastąpić i otrzymywać powiadomienia w każdej klatce podczas animacji klawiatury. Zaktualizuj właściwości widoku, aby animacja widoku była zsynchronizowana z klawiaturą.

Na tym etapie wszystkie zmiany układu są już gotowe. Jeśli na przykład użyjesz View.translationY, aby przesunąć widok, wartość będzie stopniowo zmniejszać się przy każdym wywołaniu tej metody i ostatecznie osiągnie 0, czyli pierwotną pozycję układu.

Rysunek 5. Użyj onProgress(), aby zsynchronizować animacje.

Poniższy fragment kodu pokazuje 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;
}

Opcjonalnie możesz zastąpić wartość onEnd. Ta metoda jest wywoływana po zakończeniu animacji. To dobry moment, aby usunąć wszelkie tymczasowe zmiany.

Dodatkowe materiały