WindowInsetsCompat를 사용하면 앱이 시스템 표시줄과 상호작용하는 방식과 유사하게 터치 키보드 (IME라고도 함)를 쿼리하고 제어할 수 있습니다. 앱은 WindowInsetsAnimationCompat를 사용하여 소프트웨어 키보드가 열리거나 닫힐 때 원활한 전환을 만들 수도 있습니다.
기본 요건
소프트웨어 키보드의 제어 및 애니메이션을 설정하기 전에 앱이 더 넓은 화면을 표시하도록 구성하세요. 이렇게 하면 시스템 바 및 온스크린 키보드와 같은 시스템 창 인셋을 처리할 수 있습니다.
키보드 소프트웨어 공개 상태 확인
WindowInsets을 사용하여 소프트웨어 키보드 공개 상태를 확인합니다.
Kotlin
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를 사용하여 소프트 키보드 표시 상태의 변경사항을 관찰할 수 있습니다.
Kotlin
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를 구성합니다.
Kotlin
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 호출의 예를 보여줍니다.
Kotlin
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 호출의 예를 보여줍니다.
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 }
자바
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 호출의 예를 보여줍니다.
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 }
자바
@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를 재정의할 수 있습니다. 이 메서드는 애니메이션이 끝난 후 호출됩니다. 이때 임시 변경사항을 정리하는 것이 좋습니다.
추가 리소스
- GitHub의 WindowInsetsAnimation
 
  