透過使用 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 中標示為「Un syncd」的範例顯示 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
。您可以使用這個範圍儲存起始狀態,在本例中為檢視區塊的底部座標。
下列程式碼片段為 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。