控制软件键盘并为其添加动画效果

使用 WindowInsetsCompat, 您的应用可以查询和控制屏幕键盘(也称为 IME)类似于 与系统栏交互的方式。您的应用还可以使用 WindowInsetsAnimationCompat 在软件键盘打开或关闭时创建无缝转换。

<ph type="x-smartling-placeholder">
</ph> <ph type="x-smartling-placeholder">
图 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;
});
AndroidManifest.xml

使用软件键盘同步动画

用户点按文本输入字段会导致键盘从 屏幕底部,如以下示例所示:

<ph type="x-smartling-placeholder">
</ph> <ph type="x-smartling-placeholder">
图 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 到原始布局位置。

<ph type="x-smartling-placeholder">
</ph> <ph type="x-smartling-placeholder">
图 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。系统会在动画播放完毕后调用此方法 结束了。这时有必要清理任何临时更改。

其他资源