앱은 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
호출의 샘플을 보여줍니다.
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
호출의 샘플을 보여줍니다.
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
호출의 샘플을 보여줍니다.
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