全面屏虛擬鍵適配方案
寫之前先吐槽下自己,工作了這麼多年,終於能靜下心來寫部落格了
最近公司有個需求,要實現類似於抖音的小視訊全屏播放的樣式,當虛擬鍵盤展示的時候,豎屏視訊就撐滿整個螢幕,當虛擬鍵盤隱藏的時候,就需要讓視訊底部距離螢幕底部有個虛擬鍵高度的黑邊,總結起來就是要保持視訊的原始比例,效果如下圖:
所以,我們要做的很簡單,就是監聽NavigationBar的顯示和隱藏。
方案一:監聽一個全屏 View的高度
之前看到一個思路是,使用addOnGlobalLayoutListener監聽一個全屏 View 的高度,然後不停的去檢測當前是否展示了 NavigationBar,個人不太喜歡這個方案,有興趣可以自行查詢。
方案二:監聽資料庫System表字段變化
該方案通過監控settings資料庫System表中navigationbar_is_min的變化,來判斷當前是否顯示虛擬鍵盤。經過測試,部分手機onChange方法並不會觸發。
經過多番查證,問題有兩個:
1.原來android5.0之後增加了多使用者的特性,虛擬鍵盤的navigationbar_is_min欄位從Settings.db的System表格移到了Global表。
2.不同手機品牌使用的註冊欄位也不一樣。
優化後的方法如下:
private void initDeviceInfo() { String brand = Build.BRAND; if (brand.equalsIgnoreCase("HUAWEI")) { mDeviceInfo = "navigationbar_is_min"; } else if (brand.equalsIgnoreCase("XIAOMI")) { mDeviceInfo = "force_fsg_nav_bar"; } else if (brand.equalsIgnoreCase("VIVO")) { mDeviceInfo = "navigation_gesture_on"; } else if (brand.equalsIgnoreCase("OPPO")) { mDeviceInfo = "navigation_gesture_on"; } else { mDeviceInfo = "navigationbar_is_min"; } }
/** * 註冊監聽實時監控虛擬鍵 */ private void registerNavigationBarObserver() { if (null == mActivity || !checkDeviceHasNavigationBar()) { return; } if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { mActivity.getContentResolver().registerContentObserver(Settings.System.getUriFor (mDeviceInfo), true, mNavigationBarObserver); } else { mActivity.getContentResolver().registerContentObserver(Settings.Global.getUriFor (mDeviceInfo), true, mNavigationBarObserver); } } private ContentObserver mNavigationBarObserver = new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { if (null == mActivity) { return; } resetVideoHeightByNavigation(checkNavigation()); } };
當然,我們註冊也不是說所有手機都註冊,非全面屏的手機不用註冊,所以需要判斷一下,但是google 官方提供的檢測手機是否有NavigationBar 的方法需要在9.0之後才能用(不是很明白為什麼設定 NavigationBar顏色的方法早就有了,但是檢測的方法要現在才出╮(╯_╰)╭),所以我們只能另闢蹊徑了。 我這裡是用檢測手機是否存在物理按鍵的方式來反向判斷是否存在虛擬鍵的,因為全面屏的定義就是去除物理按鍵,替換為虛擬鍵。經檢測,目前在紅米6 pro 上檢測不準確,尷尬。。。程式碼如下:
/**
* 檢查裝置是否有虛擬鍵
*
* @return
*/
public boolean checkDeviceHasNavigationBar() {
//通過判斷裝置是否有返回鍵、選單鍵(不是虛擬鍵,是手機螢幕外的按鍵)來確定是否有navigation bar
boolean hasMenuKey = ViewConfiguration.get(MeetyouFramework.getContext())
.hasPermanentMenuKey();
boolean hasBackKey = KeyCharacterMap
.deviceHasKey(KeyEvent.KEYCODE_BACK);
if (!hasMenuKey && !hasBackKey) {
// 做任何你需要做的,這個裝置有一個導航欄
return true;
}
return false;
}
註冊完成之後,當觸發 onChange 回撥時,我們就可以根據當前是否展示了虛擬鍵盤來做對應的處理了。原理同樣是檢查settings資料庫中欄位的變化,但是當你去之前的表中檢查時你會發現,在 VIVO 和 OPPO 的手機上永遠返回0!!! 最後終於查到,是 VIVO 和 OPPO 又移到 Secure 表中!!!什麼?你問我那為什麼註冊的時候能成功?我只能說,不造啊╮(╯_╰)╭
獲取當前虛擬鍵是否展示的方法:
/**
* 是否展示了 navigationbar
*
* @return
*/
private boolean checkNavigation() {
int navigationBarIsMin = 0;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
navigationBarIsMin = Settings.System.getInt(mActivity.getContentResolver(),
mDeviceInfo, 0);
} else {
if (Build.BRAND.equalsIgnoreCase("VIVO") || Build.BRAND.equalsIgnoreCase("OPPO")) {
navigationBarIsMin = Settings.Secure.getInt(mActivity.getContentResolver(),
mDeviceInfo, 0);
} else {
navigationBarIsMin = Settings.Global.getInt(mActivity.getContentResolver(),
mDeviceInfo, 0);
}
}
return navigationBarIsMin != 1;
}
最後附上獲取虛擬鍵高度的方法:
/**
* 獲取虛擬鍵的高度
*
* @return
*/
public int getNavigationBarHeight() {
Resources resources = mActivity.getResources();
int resourceId = resources.getIdentifier("navigation_bar_height",
"dimen", "android");
//獲取NavigationBar的高度
int height = resources.getDimensionPixelSize(resourceId);
return height;
}
沒錯,你猜對了!這個方法在魅族pro6上有問題!明明是一個沒有虛擬鍵的手機,結果人家非的給你返回了一個高度出來!!!就問你牛不牛!!
總結:以上方法還需更多的驗證和完善,而且該方法均是沒考慮劉海屏的情況下。別急,我已經看見產品大佬已經拿著需求向我走來了,祝我平安~