前言:
android上获取对软键盘的处理一直都是个头疼的问题,基本都是通过对view设置addOnGlobalLayoutListener来进行监听,但这个监听有很多问题,比如调用任意view的layout此函数就会进行回调,而且计算高度的时候经常会因为一些状态栏,导航栏的显示造成软键盘高度计算错误的问题。最终这个问题在android 11上得到了解决,可以通过调用setWindowInsetsAnimationCallback设置对系统UI的显示隐藏进行监听。
实现效果:
导入依赖:
由于setWindowInsetsAnimationCallback方法在android 11上才出现,对于低版本手机我们该如何处理,google对此进行了兼容,我们需要在项目中添加androidX core的最新版,就可以调用向下兼容的api
implementation "androidx.core:core:1.5.0-beta02"
创建callback:
首先我们创建类RootViewDeferringInsetsCallback继承自WindowInsetAnimationCompat.Callback同时实现OnAppWindowInsetsListener。
onApplyWindowInsets(此方法属于OnAppWindowInsetsListener)
在此方法中,我们获取到实时变化的windowInsets,用来进行可见性判断和后续的分发
onPrepare(系统ui动画开始)
在此方法中,我们获取到当前正在进行动画的类型,判断是软键盘的情况下设置deferredInsets为true并可以通过WindowInsets判断软键盘是否可见
onProgress(系统ui动画过程)
此处对软键盘和系统bar进行处理并获取到实时高度
onEnd(系统ui动画结束)
在此方法中,我们对deferedInsets设置为false并对insets进行分发
public class RootViewDeferringInsetsCallback extends WindowInsetsAnimationCompat.Callback implements OnApplyWindowInsetsListener {
private final KeyboardListener keyboardListener;
public RootViewDeferringInsetsCallback(int dispatchMode, KeyboardListener keyboardListener) {
super(dispatchMode);
this.keyboardListener = keyboardListener;
}
private View view;
private WindowInsetsCompat lastWindowInsets;
private boolean deferredInsets = false;
@Override
public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
view = v;
lastWindowInsets = insets;
return WindowInsetsCompat.CONSUMED;
}
@Override
public void onPrepare(@NonNull WindowInsetsAnimationCompat animation) {
if ((animation.getTypeMask() & WindowInsetsCompat.Type.ime()) != 0) {
deferredInsets = true;
boolean visible = false;
if(lastWindowInsets!=null) {
visible = lastWindowInsets.isVisible(WindowInsetsCompat.Type.ime());
}
keyboardListener.onKeyBoardAnimStart(visible);
}
}
@NonNull
@Override
public WindowInsetsCompat onProgress(@NonNull WindowInsetsCompat insets, @NonNull List<WindowInsetsAnimationCompat> runningAnimations) {
if (deferredInsets) {
//处理其他系统bar和软键盘同时显示情况高度计算问题
Insets typesInset = insets.getInsets(WindowInsetsCompat.Type.ime());
Insets otherInset = insets.getInsets(WindowInsetsCompat.Type.systemBars());
Insets subtract = Insets.subtract(typesInset, otherInset);
Insets diff = Insets.max(subtract, Insets.NONE);
//获取实时高度变化
int height = diff.bottom;
}
return insets;
}
@Override
public void onEnd(@NonNull WindowInsetsAnimationCompat animation) {
if (deferredInsets && (animation.getTypeMask() & WindowInsetsCompat.Type.ime()) != 0) {
deferredInsets = false;
boolean visible = false;
//软键盘是否显示
if(lastWindowInsets!=null) {
visible = lastWindowInsets.isVisible(WindowInsetsCompat.Type.ime());
}
//分发insets
if (lastWindowInsets != null) {
ViewCompat.dispatchApplyWindowInsets(view, lastWindowInsets);
}
}
}
}
设置监听
最后我们调用ViewCompat传入任意view设置监听即可
RootViewDeferringInsetsCallback rootViewInsetsCallback = new RootViewDeferringInsetsCallback(WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE, keyBoardListener);
ViewCompat.setOnApplyWindowInsetsListener(view, rootViewInsetsCallback);
ViewCompat.setWindowInsetsAnimationCallback(view, rootViewInsetsCallback);
其他
除了对软键盘的监听以外,我们还可以对任意系统ui进行显示和隐藏,或者进行动画操作。可以通过WindowCompat.getInsetsController()方法拿到view的insets控制器。有很多非常方便的方法,感兴趣的小伙伴可以进行探究。
为了方便大家,我对软键盘的监听进行了封装并上传了git,此依赖可以在任意地方进行软键盘高度的监听,并且可以设置任意view进行跟随平移,大家可以直接添加依赖进行使用~