Используя WindowInsetsCompat
, ваше приложение может опрашивать и управлять экранной клавиатурой (также называемой IME ) аналогично тому, как оно взаимодействует с системными панелями. Ваше приложение также может использовать WindowInsetsAnimationCompat
для создания плавных переходов при открытии и закрытии экранной клавиатуры.
Предпосылки
Прежде чем настраивать управление и анимацию для виртуальной клавиатуры, настройте приложение на отображение «от края до края» . Это позволит ему обрабатывать вставки системных окон , такие как системные панели и экранная клавиатура.
Проверьте видимость программного обеспечения клавиатуры
Используйте WindowInsets
для проверки видимости программной клавиатуры.
Котлин
val insets = ViewCompat.getRootWindowInsets(view) ?: return val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime()) val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
Ява
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 }
Ява
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. } )
Ява
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() }
Ява
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 }
Ява
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 }
Ява
@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.