如何获取Android软键盘WindowInsetsAnimation的准确动画时长
如今我们终于可以便捷地感知Android软键盘弹出、收起的动画过程,实现代码如下:
yourView.setWindowInsetsAnimationCallback(new WindowInsetsAnimation.Callback(WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE ) { @NonNull @Override public WindowInsets onProgress(@NonNull WindowInsets windowInsets, @NonNull List<WindowInsetsAnimation> list) { return windowInsets; } @Override public void onEnd(@NonNull WindowInsetsAnimation animation) { super.onEnd(animation); } @NonNull @Override public WindowInsetsAnimation.Bounds onStart(@NonNull WindowInsetsAnimation animation, @NonNull WindowInsetsAnimation.Bounds bounds) { boolean showingKeyboard = frame.getRootWindowInsets().isVisible(WindowInsets.Type.ime()); if (showingKeyboard) { yourLayoutWhenSoftKeyboardShown(); } else { yourLayoutWhenSoftKeyboardHidden(); } return super.onStart(animation, bounds); } });
借助该回调,我们可以在系统执行窗口尺寸调整动画的过程中,同步执行任意所需的自定义动画,示例代码如下:
Integer animDuration = 120; private void layoutWhenSoftKeyboardShown() { ValueAnimator m1 = ValueAnimator.ofFloat(15f, 1f); m1.setDuration(animDuration); m1.setInterpolator(new LinearInterpolator()); m1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { ((LinearLayout.LayoutParams) spacerOrWhatever.getLayoutParams()).weight = (float) animation.getAnimatedValue(); spacerOrWhatever.requestLayout(); } }); m1.start(); }
上述方案看似完善,但仍存在一个核心疑问:
软键盘系统动画的时长到底是多少?
在iOS平台中,告知开发者“系统正在执行键盘展示动画”的对应回调,会直接将动画总时长作为参数返回,开发者只需将自定义动画的时长设置为该值,即可实现与系统动画的完全同步。
在Android平台中,要如何在上述onStart回调内,获取即将执行的软键盘动画的准确时长,从而让自定义动画与系统软键盘动画时长保持一致?
解答
直接在onStart回调传入的WindowInsetsAnimation实例上调用getDuration()方法,就能获取当前软键盘动画的准确时长,返回值单位为毫秒。
需要注意特殊场景处理:如果方法返回WindowInsetsAnimation.DURATION_UNKNOWN(常量值为-1),代表当前系统没有明确指定动画时长,这种情况可以兜底使用通用默认值250ms,绝大多数厂商ROM的默认软键盘动画时长都和该值接近,不会出现明显的不同步问题。
修正后的代码写法如下:
@NonNull @Override public WindowInsetsAnimation.Bounds onStart(@NonNull WindowInsetsAnimation animation, @NonNull WindowInsetsAnimation.Bounds bounds) { // 从回调参数直接获取系统动画真实时长 long animDuration = animation.getDuration(); if (animDuration == WindowInsetsAnimation.DURATION_UNKNOWN) { // 未知时长场景兜底 animDuration = 250; } boolean showingKeyboard = frame.getRootWindowInsets().isVisible(WindowInsets.Type.ime()); if (showingKeyboard) { yourLayoutWhenSoftKeyboardShown(animDuration); } else { yourLayoutWhenSoftKeyboardHidden(animDuration); } return super.onStart(animation, bounds); }
对应的自定义动画方法去掉硬编码时长,改为传入参数即可:
private void layoutWhenSoftKeyboardShown(long animDuration) { ValueAnimator m1 = ValueAnimator.ofFloat(15f, 1f); m1.setDuration(animDuration); m1.setInterpolator(new LinearInterpolator()); m1.addUpdateListener(animation -> { ((LinearLayout.LayoutParams) spacerOrWhatever.getLayoutParams()).weight = (float) animation.getAnimatedValue(); spacerOrWhatever.requestLayout(); }); m1.start(); }
- 该方法返回的时长会自动适配用户在系统设置中调整的全局动画缩放比例,不需要额外读取系统配置做换算,直接使用就能和系统软键盘动画完全同步。
- 不要硬编码固定动画时长,不同Android版本、不同厂商ROM的默认软键盘动画时长存在差异,硬编码很容易出现动画错位、不同步的问题。
内容的提问来源于stack exchange,提问作者Fattie
相关产品推荐
相关产品推荐

