前言:
android上獲取對(duì)軟鍵盤的處理一直都是個(gè)頭疼的問(wèn)題,基本都是通過(guò)對(duì)view設(shè)置addOnGlobalLayoutListener來(lái)進(jìn)行監(jiān)聽,但這個(gè)監(jiān)聽有很多問(wèn)題,比如調(diào)用任意view的layout此函數(shù)就會(huì)進(jìn)行回調(diào),而且計(jì)算高度的時(shí)候經(jīng)常會(huì)因?yàn)橐恍顟B(tài)欄,導(dǎo)航欄的顯示造成軟鍵盤高度計(jì)算錯(cuò)誤的問(wèn)題。最終這個(gè)問(wèn)題在android 11上得到了解決,可以通過(guò)調(diào)用setWindowInsetsAnimationCallback設(shè)置對(duì)系統(tǒng)UI的顯示隱藏進(jìn)行監(jiān)聽。
實(shí)現(xiàn)效果:
導(dǎo)入依賴:
由于setWindowInsetsAnimationCallback方法在android 11上才出現(xiàn),對(duì)于低版本手機(jī)我們?cè)撊绾翁幚?,google對(duì)此進(jìn)行了兼容,我們需要在項(xiàng)目中添加androidX core的最新版,就可以調(diào)用向下兼容的api
implementation "androidx.core:core:1.5.0-beta02"
創(chuàng)建callback:
首先我們創(chuàng)建類RootViewDeferringInsetsCallback繼承自WindowInsetAnimationCompat.Callback同時(shí)實(shí)現(xiàn)OnAppWindowInsetsListener。
onApplyWindowInsets(此方法屬于OnAppWindowInsetsListener)
在此方法中,我們獲取到實(shí)時(shí)變化的windowInsets,用來(lái)進(jìn)行可見性判斷和后續(xù)的分發(fā)
onPrepare(系統(tǒng)ui動(dòng)畫開始)
在此方法中,我們獲取到當(dāng)前正在進(jìn)行動(dòng)畫的類型,判斷是軟鍵盤的情況下設(shè)置deferredInsets為true并可以通過(guò)WindowInsets判斷軟鍵盤是否可見
onProgress(系統(tǒng)ui動(dòng)畫過(guò)程)
此處對(duì)軟鍵盤和系統(tǒng)bar進(jìn)行處理并獲取到實(shí)時(shí)高度
onEnd(系統(tǒng)ui動(dòng)畫結(jié)束)
在此方法中,我們對(duì)deferedInsets設(shè)置為false并對(duì)insets進(jìn)行分發(fā)
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) {
//處理其他系統(tǒng)bar和軟鍵盤同時(shí)顯示情況高度計(jì)算問(wèn)題
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);
//獲取實(shí)時(shí)高度變化
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());
}
//分發(fā)insets
if (lastWindowInsets != null) {
ViewCompat.dispatchApplyWindowInsets(view, lastWindowInsets);
}
}
}
}
設(shè)置監(jiān)聽
最后我們調(diào)用ViewCompat傳入任意view設(shè)置監(jiān)聽即可
RootViewDeferringInsetsCallback rootViewInsetsCallback = new RootViewDeferringInsetsCallback(WindowInsetsAnimationCompat.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE, keyBoardListener);
ViewCompat.setOnApplyWindowInsetsListener(view, rootViewInsetsCallback);
ViewCompat.setWindowInsetsAnimationCallback(view, rootViewInsetsCallback);
其他
除了對(duì)軟鍵盤的監(jiān)聽以外,我們還可以對(duì)任意系統(tǒng)ui進(jìn)行顯示和隱藏,或者進(jìn)行動(dòng)畫操作??梢酝ㄟ^(guò)WindowCompat.getInsetsController()方法拿到view的insets控制器。有很多非常方便的方法,感興趣的小伙伴可以進(jìn)行探究。
為了方便大家,我對(duì)軟鍵盤的監(jiān)聽進(jìn)行了封裝并上傳了git,此依賴可以在任意地方進(jìn)行軟鍵盤高度的監(jiān)聽,并且可以設(shè)置任意view進(jìn)行跟隨平移,大家可以直接添加依賴進(jìn)行使用~