使用 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; });
AndroidManifest.xml
使用软件键盘同步动画
用户点按文本输入字段会导致键盘从 屏幕底部,如以下示例所示:
<ph type="x-smartling-placeholder">标有“未同步”的示例图 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.google.cn/static/images/guide/navigation/software-keyboard-3.png?authuser=0000&hl=hr)
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.google.cn/static/images/guide/navigation/software-keyboard-4.png?authuser=0000&hl=hr)
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
。系统会在动画播放完毕后调用此方法
结束了。这时有必要清理任何临时更改。