使用 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 中標示為「Synchronized」的範例所示。
設定 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
,並在視圖因動畫而重新排版前呼叫。您可以使用它來儲存開始狀態,在本例中,這就是檢視畫面的底部座標。
以下程式碼片段顯示對 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
回呼設為任何檢視畫面,此時系統就會呼叫該回呼。此時可儲存檢視畫面屬性結束狀態。
以下程式碼片段顯示對 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
的呼叫範例:
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
。系統會在動畫結束後呼叫此方法。這時可以清除任何臨時變更。
其他資源
- GitHub 上的 WindowInsetsAnimation。