控制螢幕鍵盤並製作動畫

使用 WindowInsetsCompat, 您的應用程式可以查詢及控制螢幕小鍵盤 (又稱為 IME) 類似 與系統資訊列互動的方式。您的應用程式也能使用 WindowInsetsAnimationCompat敬上 開啟或關閉螢幕鍵盤時,可創造流暢的轉場效果。

圖 1.兩個螢幕鍵盤範例 開放式轉場效果

必要條件

設定螢幕鍵盤的控制項和動畫前,請先完成 在您的應用程式中顯示無邊框。這樣一來, 該元件會處理系統視窗插邊,例如 系統資訊列和螢幕小鍵盤。

檢查鍵盤軟體顯示設定

使用 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.同步鍵盤動畫。
  • 示例標示為「未同步處理」圖 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 導致這些元素再次出現動畫您可以用它來儲存起始狀態 在本例中是檢視畫面的底部座標。

顯示根檢視畫面起始狀態底部座標的圖片。
圖 3. 使用 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 回呼,表示已 稱為這個元件建議您趁此機會儲存檢視畫面的結束狀態 資源。

顯示檢視畫面結束狀態底部座標的圖片
圖 4. 使用 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 的原始版面配置位置。

圖 5.使用 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。系統會在動畫結束後呼叫此方法 已結束。建議您趁此機會清除任何暫時變更。

其他資源