安卓開發(fā)的同學(xué)們都知道,在項目中經(jīng)常需要獲取屏幕的寬高來做UI適配。經(jīng)常用的一個方法是:
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
通常來講,使用這種方法可以獲取到正確的寬高值。但是在最近的一個項目中,app設(shè)置為全屏。物理寬高為1024x600的情況下,使用這個方法獲取到的分辨率竟然是1024x552。這就詭異了,通過在網(wǎng)上查找資料發(fā)現(xiàn),自安卓4.4之后,使用這個方法獲取寬高是不完備的,原因是4.4后有了虛擬導(dǎo)航欄這個選項。但是app中并沒有定義虛擬導(dǎo)航欄啊,別著急,繼續(xù)往下找,發(fā)現(xiàn)除了app定義之外,還有一個系統(tǒng)選項可以定義這個導(dǎo)航欄。也就是qemu.hw.mainkeys。需要注意的是,這個選項為0時表示有導(dǎo)航欄,為1則反之。
問題找到了,怎么解決呢?
- 很明顯的方案就是如果想繼續(xù)沿用getMetrics方法而又不需要虛擬導(dǎo)航欄,那么就把qemu.hw.mainkeys設(shè)置為1,或者直接去掉。有些同學(xué)表示,也許我并沒有ROM權(quán)限,無法修改系統(tǒng)參數(shù),那怎么辦呢?這個時候我們就需要方法2了。
- 我們可以使用接口getRealMetrics()來代替,這個方法不會將虛擬導(dǎo)航欄的高度計算進去。
延伸一下,具體的原理在哪里呢?答案就在phoneWindowManager的getNonDecorDisplayHeight方法,這其中有幾行代碼這樣寫的
if( displayId == DEFAULT_DISPLAY && mHasNavigationBar ) {
if(!mNavigationBarCanMove || fullWidth < fullHeight) {
return fullHeight - getNavigationBarHeight(rotation, uiMode);
}
}
顯而易見,是fullHeight - getNavigationBarHeight(rotation, uiMode);這句搗的鬼,但是想進入這里,還有倆關(guān)需要闖。
第一關(guān):在displayId是DEFAULT_DISPLAY的條件下,需要mHasNavigationBar成立, 檢查邏輯是: 首先R.bool.config_showNavigationBar,其次是qemu.hw.mainkeys, 而qemu.hw.mainkeys如果定義為0可覆蓋config_showNavigationBar的配置。
第二關(guān):mNavigationBarCanMove不成立或者fullWidth < fullHeight, fullWidth < fullHeight比較好理解,即寬度小于高度時,系統(tǒng)會認為虛擬導(dǎo)航欄在垂直底部,會減去這部分。那么前者是什么意思呢,別急,看下代碼就知道了:
// Allow the navigation bar to move on non-square small devices (phones).
mNavigationBarCanMove = width != height && shortSizeDp < 600;
從注釋來看,也就是說當(dāng)針對寬高不等,且較小的邊小于600dp的情況下,導(dǎo)航欄允許移動到較短邊的側(cè)邊。
具體可參考getNonDecorDisplayWidth中的:
2744 if (mNavigationBarCanMove && fullWidth > fullHeight) {
2745 return fullWidth - getNavigationBarWidth(rotation, uiMode);
2746 }
可看到這部分代碼邏輯恰好與getNonDecorDisplayHeight相反,也就是驗證了上邊說的結(jié)論。
好了,這部分的解釋就到此為止。