사용자가 텍스트 입력란을 탭하면 다음 예와 같이 키보드가 화면 하단에서 슬라이드되어 제자리에 배치됩니다.
그림 2. 동기화된 키보드 애니메이션
그림 2의 '동기화되지 않음'이라는 라벨이 지정된 예에서는 Android 10 (API 수준 29)의 기본 동작을 보여줍니다. 여기서는 텍스트 필드와 앱 콘텐츠가 키보드의 애니메이션과 동기화되지 않고 제자리에 스냅됩니다. 이 동작은 시각적으로 거슬릴 수 있습니다.
Android 11 (API 수준 30) 이상에서는 WindowInsetsAnimationCompat를 사용하여 화면 하단에서 키보드가 위아래로 슬라이드하는 것과 앱의 전환을 동기화할 수 있습니다. 그림 2의 '동기화됨' 라벨이 지정된 예와 같이 더 부드럽게 표시됩니다.
인셋 애니메이션이 시작되면 onStart가 호출됩니다. 이를 사용하여 모든 뷰 속성을 레이아웃 변경의 최종 상태로 설정할 수 있습니다. OnApplyWindowInsetsListener 콜백이 뷰 중 하나로 설정되어 있으면 이 시점에서 이미 호출됩니다. 이때 뷰 속성의 최종 상태를 저장하는 것이 좋습니다.
그림 4.onStart()을 사용하여 종료 상태를 기록합니다.
다음 스니펫은 onStart 호출의 예를 보여줍니다.
Kotlin
varendBottom=0foverridefunonStart(animation:WindowInsetsAnimationCompat,bounds:WindowInsetsAnimationCompat.BoundsCompat):WindowInsetsAnimationCompat.BoundsCompat{// Record the position of the view after the IME transition.endBottom=view.bottom.toFloat()returnbounds}
onProgress는 애니메이션을 실행하는 과정에서 인셋이 변경될 때 호출되므로 이를 재정의하여 키보드 애니메이션 중에 모든 프레임에서 알림을 받을 수 있습니다. 키보드와 동기화되어 뷰가 애니메이션으로 표시되도록 뷰 속성을 업데이트합니다.
이 시점에서 모든 레이아웃 변경이 완료됩니다. 예를 들어 View.translationY를 사용하여 뷰를 이동하면 이 메서드를 호출할 때마다 값이 점차 감소하여 결국 원래 레이아웃 위치인 0에 도달합니다.
그림 5.onProgress()를 사용하여 애니메이션을 동기화합니다.
다음 스니펫은 onProgress 호출의 예를 보여줍니다.
Kotlin
overridefunonProgress(insets:WindowInsetsCompat,runningAnimations:MutableList<WindowInsetsAnimationCompat>):WindowInsetsCompat{// Find an IME animation.valimeAnimation=runningAnimations.find{it.typeMaskandWindowInsetsCompat.Type.ime()!=0}?:returninsets// Offset the view based on the interpolated fraction of the IME animation.view.translationY=(startBottom-endBottom)*(1-imeAnimation.interpolatedFraction)returninsets}
자바
@NonNull@OverridepublicWindowInsetsCompatonProgress(@NonNullWindowInsetsCompatinsets,@NonNullList<WindowInsetsAnimationCompat>runningAnimations){// Find an IME animation.WindowInsetsAnimationCompatimeAnimation=null;for(WindowInsetsAnimationCompatanimation: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()));}returninsets;}
선택적으로 onEnd를 재정의할 수 있습니다. 이 메서드는 애니메이션이 끝난 후 호출됩니다. 이때 임시 변경사항을 정리하는 것이 좋습니다.
이 페이지에 나와 있는 콘텐츠와 코드 샘플에는 콘텐츠 라이선스에서 설명하는 라이선스가 적용됩니다. 자바 및 OpenJDK는 Oracle 및 Oracle 계열사의 상표 또는 등록 상표입니다.
최종 업데이트: 2025-08-27(UTC)
[[["이해하기 쉬움","easyToUnderstand","thumb-up"],["문제가 해결됨","solvedMyProblem","thumb-up"],["기타","otherUp","thumb-up"]],[["필요한 정보가 없음","missingTheInformationINeed","thumb-down"],["너무 복잡함/단계 수가 너무 많음","tooComplicatedTooManySteps","thumb-down"],["오래됨","outOfDate","thumb-down"],["번역 문제","translationIssue","thumb-down"],["샘플/코드 문제","samplesCodeIssue","thumb-down"],["기타","otherDown","thumb-down"]],["최종 업데이트: 2025-08-27(UTC)"],[],[],null,["# Control and animate the software keyboard\n\nTry the Compose way \nJetpack Compose is the recommended UI toolkit for Android. Learn how to work with the keyboard in Compose. \n[Software keyboard in Compose →](/develop/ui/compose/system/keyboard-animations) \n\n\u003cbr /\u003e\n\nUsing [`WindowInsetsCompat`](/reference/androidx/core/view/WindowInsetsCompat),\nyour app can query and control the on-screen keyboard (also called the\n[IME](https://en.wikipedia.org/wiki/Input_method)) similar to the\nway it interacts with the system bars. Your app can also use\n[`WindowInsetsAnimationCompat`](/reference/androidx/core/view/WindowInsetsAnimationCompat)\nto create seamless transitions when the software keyboard is opened or closed.\n**Figure 1.** Two examples of the software keyboard open-closed transition.\n\nPrerequisites\n-------------\n\nBefore setting up control and animation for the software keyboard, configure\nyour app to [display edge-to-edge](/training/gestures/edge-to-edge). This lets\nit handle [system window insets](/develop/ui/views/layout/insets) such as the\nsystem bars and the on-screen keyboard.\n\nCheck keyboard software visibility\n----------------------------------\n\nUse [`WindowInsets`](/reference/android/view/WindowInsets) to check the software\nkeyboard visibility. \n\n### Kotlin\n\n```kotlin\nval insets = ViewCompat.getRootWindowInsets(view) ?: return\nval imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())\nval imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom\n```\n\n### Java\n\n```java\nWindowInsetsCompat insets = ViewCompat.getRootWindowInsets(view);\nboolean imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime());\nint imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;\n```\n\nAlternatively, you can use\n[`ViewCompat.setOnApplyWindowInsetsListener`](/reference/androidx/core/view/ViewCompat#setOnApplyWindowInsetsListener(android.view.View,%20androidx.core.view.OnApplyWindowInsetsListener))\nto observe changes to software keyboard visibility. \n\n### Kotlin\n\n```kotlin\nViewCompat.setOnApplyWindowInsetsListener(view) { _, insets -\u003e\n val imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime())\n val imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom\n insets\n}\n```\n\n### Java\n\n```java\nViewCompat.setOnApplyWindowInsetsListener(view, (v, insets) -\u003e {\n boolean imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime());\n int imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;\n return insets;\n});\n```\n| **Note:** To achieve the best backward compatibility with this AndroidX implementation, set `android:windowSoftInputMode=\"adjustResize\"` to the activity in your `AndroidManifest.xml` file.\n\nSynchronize animation with the software keyboard\n------------------------------------------------\n\nA user tapping a text input field causes the keyboard to slide into place from\nthe bottom of the screen, as shown in the following example:\n**Figure 2.** Synchronized keyboard animation.\n\n- The example labeled \"Unsynchronized\" in figure 2 shows the default behavior\n in Android 10 (API level 29), in which the text field and content of the app\n snap into place instead of synchronizing with the keyboard's\n animation---behavior that can be visually jarring.\n\n- In Android 11 (API level 30) and higher, you can use\n `WindowInsetsAnimationCompat` to synchronize the transition of the app with\n the keyboard sliding up and down from the bottom of the screen. This looks\n smoother, as shown in the example labeled \"Synchronized\" in figure 2.\n\n| **Note:** Don't consume `WindowInsets` in `setWindowInsetsApplyListener` for any parent [`ViewGroup`](/reference/android/view/ViewGroup) objects. Instead, let `WindowInsetsAnimatorCompat` handle them on Android 10 and lower.\n\nConfigure\n[`WindowInsetsAnimationCompat.Callback`](/reference/androidx/core/view/WindowInsetsAnimationCompat.Callback)\nwith the view to be synchronized with the keyboard animation. \n\n### Kotlin\n\n```kotlin\nViewCompat.setWindowInsetsAnimationCallback(\n view,\n object : WindowInsetsAnimationCompat.Callback(DISPATCH_MODE_STOP) {\n // Override methods.\n }\n)\n```\n\n### Java\n\n```java\nViewCompat.setWindowInsetsAnimationCallback(\n view,\n new WindowInsetsAnimationCompat.Callback(\n WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_STOP\n ) {\n // Override methods.\n });\n```\n\nThere are several methods to override in `WindowInsetsAnimationCompat.Callback`,\nnamely\n[`onPrepare()`](/reference/androidx/core/view/WindowInsetsAnimationCompat.Callback#onPrepare(androidx.core.view.WindowInsetsAnimationCompat)),\n[`onStart()`](/reference/androidx/core/view/WindowInsetsAnimationCompat.Callback#onStart(androidx.core.view.WindowInsetsAnimationCompat,%20androidx.core.view.WindowInsetsAnimationCompat.BoundsCompat)),\n[`onProgress()`](/reference/androidx/core/view/WindowInsetsAnimationCompat.Callback#onProgress(androidx.core.view.WindowInsetsCompat,%20java.util.List%3Candroidx.core.view.WindowInsetsAnimationCompat%3E)),\nand\n[`onEnd()`](/reference/androidx/core/view/WindowInsetsAnimationCompat.Callback#onEnd(androidx.core.view.WindowInsetsAnimationCompat)).\nStart with calling `onPrepare()` before any of the layout changes.\n\n`onPrepare` is called when an insets animation is starting and before the views\nare re-laid out due to an animation. You can use it to save the start state,\nwhich in this case is the bottom coordinate of the view.\n**Figure 3.** Using `onPrepare()` to record the start state.\n\nThe following snippet shows a sample call to `onPrepare`: \n\n### Kotlin\n\n```kotlin\nvar startBottom = 0f\n\noverride fun onPrepare(\n animation: WindowInsetsAnimationCompat\n) {\n startBottom = view.bottom.toFloat()\n}\n```\n\n### Java\n\n```java\nfloat startBottom;\n\n@Override\npublic void onPrepare(\n @NonNull WindowInsetsAnimationCompat animation\n) {\n startBottom = view.getBottom();\n}\n```\n\n`onStart` is called when an insets animation starts. You can use it to set all\nthe view properties to the end state of the layout changes. If you have an\n`OnApplyWindowInsetsListener` callback set to any of the views, it is already\ncalled at this point. This is a good time to save the end state of the view\nproperties.\n**Figure 4.** Using `onStart()` to record the end state.\n\nThe following snippet shows a sample call to `onStart`: \n\n### Kotlin\n\n```kotlin\nvar endBottom = 0f\n\noverride fun onStart(\n animation: WindowInsetsAnimationCompat,\n bounds: WindowInsetsAnimationCompat.BoundsCompat\n): WindowInsetsAnimationCompat.BoundsCompat {\n // Record the position of the view after the IME transition.\n endBottom = view.bottom.toFloat()\n\n return bounds\n}\n```\n\n### Java\n\n```java\nfloat endBottom;\n\n@NonNull\n@Override\npublic WindowInsetsAnimationCompat.BoundsCompat onStart(\n @NonNull WindowInsetsAnimationCompat animation,\n @NonNull WindowInsetsAnimationCompat.BoundsCompat bounds\n) {\n endBottom = view.getBottom();\n return bounds;\n}\n```\n\n`onProgress` is called when the insets change as part of running an animation,\nso you can override it and be notified on every frame during the keyboard\nanimation. Update the view properties so that the view animates in\nsynchronization with the keyboard.\n\nAll the layout changes are complete at this point. For example, if you use\n`View.translationY` to shift the view, the value gradually decreases for every\ncall of this method and eventually reaches `0` to the original layout position.\n**Figure 5.** Using `onProgress()` to synchronize the animations.\n\nThe following snippet shows a sample call to `onProgress`: \n\n### Kotlin\n\n```kotlin\noverride fun onProgress(\n insets: WindowInsetsCompat,\n runningAnimations: MutableList\u003cWindowInsetsAnimationCompat\u003e\n): WindowInsetsCompat {\n // Find an IME animation.\n val imeAnimation = runningAnimations.find {\n it.typeMask and WindowInsetsCompat.Type.ime() != 0\n } ?: return insets\n\n // Offset the view based on the interpolated fraction of the IME animation.\n view.translationY =\n (startBottom - endBottom) * (1 - imeAnimation.interpolatedFraction)\n\n return insets\n}\n```\n\n### Java\n\n```java\n@NonNull\n@Override\npublic WindowInsetsCompat onProgress(\n @NonNull WindowInsetsCompat insets,\n @NonNull List\u003cWindowInsetsAnimationCompat\u003e runningAnimations\n) {\n // Find an IME animation.\n WindowInsetsAnimationCompat imeAnimation = null;\n for (WindowInsetsAnimationCompat animation : runningAnimations) {\n if ((animation.getTypeMask() & WindowInsetsCompat.Type.ime()) != 0) {\n imeAnimation = animation;\n break;\n }\n }\n if (imeAnimation != null) {\n // Offset the view based on the interpolated fraction of the IME animation.\n view.setTranslationY((startBottom - endBottom)\n\n * (1 - imeAnimation.getInterpolatedFraction()));\n }\n return insets;\n}\n```\n\nOptionally, you can override `onEnd`. This method is called after the animation\nis over. This is a good time to clean up any temporary changes.\n\nAdditional resources\n--------------------\n\n- [WindowInsetsAnimation](https://github.com/android/user-interface-samples/tree/main/WindowInsetsAnimation) on GitHub."]]