アプリは 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
Java
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 }
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
を構成して、キーボード アニメーションと同期させるビューを指定します。
Kotlin
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
は、インセット アニメーションの開始時と、アニメーションによってビューが再レイアウトされる前に呼び出されます。これを使用して開始状態(この場合はビューの下部座標)を保存できます。
![ルートビューの開始状態の下座標を示す画像。](https://developer.android.com/static/images/guide/navigation/software-keyboard-3.png?authuser=9&hl=ja)
onPrepare()
を使用して開始状態を記録する
次のスニペットは、onPrepare
の呼び出し例を示しています。
Kotlin
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
コールバックがいずれかのビューに設定されている場合、そのコールバックはすでに呼び出されています。ここで、ビュー プロパティの最終状態を保存することをおすすめします。
![ビューの終了状態の下座標を示す画像](https://developer.android.com/static/images/guide/navigation/software-keyboard-4.png?authuser=9&hl=ja)
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 }
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
の呼び出し例を示しています。
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 }
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)