Используя WindowInsetsCompat , ваше приложение может запрашивать и управлять экранной клавиатурой (также называемой IME ) аналогично тому, как оно взаимодействует с системными панелями. Ваше приложение также может использовать WindowInsetsAnimationCompat для создания плавных переходов при открытии или закрытии программной клавиатуры.
Предварительные требования
Прежде чем настраивать управление и анимацию для программной клавиатуры, сконфигурируйте приложение для отображения текста от края до края . Это позволит ему обрабатывать отступы системных окон, такие как системные панели и экранная клавиатура.
Анимация синхронизированных вставок по умолчанию
Начиная с API уровня 37.2 и выше, приложения, использующие стандартные настройки компоновки окна (например, SOFT_INPUT_ADJUST_RESIZE или SOFT_INPUT_ADJUST_PAN при отображении IME с помощью WindowInsetsController.show(WindowInsets.Type.ime()) ) без реализации пользовательского WindowInsetsAnimationCompat.Callback , могут воспользоваться преимуществами синхронизированной покадровой анимации на системном уровне. При включении эта функция автоматически применяет отступы к каждому кадру и запускает проход компоновки, создавая плавный переход при перемещении клавиатуры.
Чтобы включить это поведение, добавьте следующий тег <property> в файл AndroidManifest.xml вашего приложения или активности:
<property
android:name="android.window.PROPERTY_COMPAT_ALLOW_SYNCHRONIZED_INSETS_ANIMATION"
android:value="true" />
Выполнять анимацию вставок вне основного потока.
По умолчанию регистрация объекта WindowInsetsAnimationCompat.Callback гарантирует, что анимация вставки окна будет выполняться в основном потоке приложения. Это необходимо, если ваше приложение постоянно обновляет и синхронизирует макет представления во время анимации (например, в методе onProgress ). Однако, если вашему приложению нужно только отслеживать переходы жизненного цикла (например, когда IME начинает или заканчивает анимацию) и не требуется обновлять представления покадрово, выполнение в основном потоке может привести к ненужным накладным расходам и заиканию анимации — особенно если основной поток занят другими задачами.
Начиная с API уровня 37.2 и выше, вы можете использовать ViewTreeObserver.WindowInsetsAnimationListener для отслеживания этих переходов. Поскольку этот слушатель не получает покадровых обновлений прогресса (нет обратного вызова onProgress ), система может запускать анимацию вставки в отдельном потоке анимации, а не в основном потоке, что приводит к более плавному переходу.
Котлин
val listener = object : ViewTreeObserver.WindowInsetsAnimationListener { override fun onPrepare(animation: WindowInsetsAnimation) { // Handle preparation before animation starts } override fun onEnd(animation: WindowInsetsAnimation) { // Clean up temporary changes } } // Add the listener view.viewTreeObserver.addWindowInsetsAnimationListener(listener) // Remove the listener when no longer needed view.viewTreeObserver.removeWindowInsetsAnimationListener(listener)
Java
ViewTreeObserver.WindowInsetsAnimationListener listener = new ViewTreeObserver.WindowInsetsAnimationListener() { @Override public void onPrepare(@NonNull WindowInsetsAnimation animation) { // Handle preparation before animation starts } @Override public void onEnd(@NonNull WindowInsetsAnimation animation) { // Clean up temporary changes } }; // Add the listener view.getViewTreeObserver().addWindowInsetsAnimationListener(listener); // Remove the listener when no longer needed view.getViewTreeObserver().removeWindowInsetsAnimationListener(listener);
Проверить видимость программного обеспечения клавиатуры
Используйте WindowInsets для проверки видимости программной клавиатуры.
Котлин
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;
В качестве альтернативы можно использовать ViewCompat.setOnApplyWindowInsetsListener для отслеживания изменений видимости программной клавиатуры.
Котлин
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; });
Синхронизировать анимацию с программной клавиатурой.
При нажатии пользователем на текстовое поле ввода клавиатура автоматически выдвигается снизу экрана, как показано в следующем примере:
Пример, обозначенный как «Несинхронизировано» на рисунке 2, демонстрирует поведение по умолчанию в Android 10 (уровень API 29), при котором текстовое поле и содержимое приложения автоматически подстраиваются под анимацию клавиатуры, вместо того чтобы синхронизироваться с ней — такое поведение может выглядеть неестественно.
В Android 11 (уровень API 30) и выше можно использовать
WindowInsetsAnimationCompatдля синхронизации перехода приложения с движением клавиатуры вверх и вниз от нижнего края экрана. Это выглядит более плавно, как показано в примере с пометкой «Синхронизировано» на рисунке 2.
Настройте WindowInsetsAnimationCompat.Callback , указав представление, которое необходимо синхронизировать с анимацией клавиатуры.
Котлин
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 есть несколько методов, которые можно переопределить, а именно onPrepare() , onStart() , onProgress() и onEnd() . Начните с вызова onPrepare() перед любыми изменениями макета.
onPrepare вызывается при начале анимации отступов и перед перекомпоновкой элементов представления из-за анимации. Его можно использовать для сохранения начального состояния, которым в данном случае является нижняя координата элемента представления.

onPrepare() для записи начального состояния. Следующий фрагмент кода демонстрирует пример вызова функции onPrepare :
Котлин
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 вызывается при начале анимации отступов. Вы можете использовать его для установки всех свойств представления в конечное состояние изменений макета. Если у вас установлен коллбэк OnApplyWindowInsetsListener для какого-либо из представлений, он уже вызывается в этот момент. Это подходящий момент для сохранения конечного состояния свойств представления.

onStart() для записи конечного состояния. Следующий фрагмент кода демонстрирует пример вызова функции onStart :
Котлин
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 вызывается при изменении отступов в процессе выполнения анимации, поэтому вы можете переопределить её и получать уведомления на каждом кадре во время анимации клавиатуры. Обновите свойства представления, чтобы анимация представления синхронизировалась с клавиатурой.
На этом этапе все изменения компоновки завершены. Например, если вы используете View.translationY для смещения представления, значение постепенно уменьшается при каждом вызове этого метода и в конечном итоге достигает 0 , возвращаясь к исходному положению в компоновке.
onProgress() для синхронизации анимаций. Следующий фрагмент кода демонстрирует пример вызова функции onProgress :
Котлин
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; }
При желании вы можете переопределить onEnd . Этот метод вызывается после завершения анимации. Это подходящий момент для очистки любых временных изменений.
Дополнительные ресурсы
- WindowInsetsAnimation на GitHub.
