Android 劉海屏來襲,全網最全適配技巧
Apple 一直在引領設計的潮流,自從 iPhone X 釋出之後,"劉海屏" 就一直存在爭議。不過不管你怎樣,Android 也要躋入 "劉海屏" 的行列,尤其是 Android P 釋出之後,也從系統級支援頂部凹槽螢幕設計。
很多廠商也在逐漸推出 “劉海屏” 設計的手機,在國內比較常見的就是 OPPO x21 和 華為 P20。
螢幕不一樣了,迎來的就是一些適配上的問題。今天就來聊聊,Android 的 “劉海屏”,以及我們如何去適配它。
二、劉海屏的背景介紹
2.1 背景介紹
劉海屏的外觀,我想大家應該都有概念,不過不同廠商劉海屏的實現方式也有所不太,這一點需要先有個概念。
就現在市場上的情況來說,會區分成兩類,一類是標準的 Android P Api,另外一類就是廠商在 Android P 以下的系統,做的特殊適配。
例如:華為 P20 就是採用的 Android P 標準 Api 的方式,而 OPPO x21 就不一樣了,它有自己的適配 Api。
2.2 那些需要單獨適配
就算是增加了劉海屏,你也可以發現,大部分都是“切割”的狀態列的區域,所以就面臨了三種情況。
-
有狀態列的頁面,不會收到劉海屏的影響。
-
全屏未適配劉海屏的頁面,系統會對劉海屏區域進行切割,讓整體 UI 頁面做下移處理,避開劉海屏的顯示。
-
全屏已適配劉海屏的頁面,可以相容劉海屏,做到真正的全屏顯示。
後面會單獨講解這幾種方式的區別。
2.3 搶先體驗 Android P
在手邊沒有對應系統的裝置的時候,模擬器是一條不錯的路,最近 Google 也釋出了 Android P 的模擬器,還有一個辦法就是找一些支援真機雲測的平臺,租用一臺需要的遠端裝置,也是一個解決方案。
我這裡選擇 Android P 的模擬器,有需要自己更新 SDK ,無腦下載更新就好。
劉海的凹槽區域,大部分是為了給攝像頭或者其他感測器留出區域。而在沒有劉海的裝置或者模擬器上,可以通過開發者選項裡的 “Simulate a display with a cutout”,開啟劉海屏的支援。
如果你把所有的模式都試過一遍,你會發現,其實劉海屏的劉海,在 Android P 上,是有多種樣式的,並非統一的。
2.4 劉海屏的適配
2.2 也講清楚了,劉海屏的切割區域,都存在於狀態列上,所以在有狀態列的頁面上,是無需我們特殊處理的,系統會幫我們處理好。
而對於全屏的頁面,就需要單獨的處理了。我這裡,簡單做了一個全屏頁面,每個橫條都是等寬的這樣能看到佈局上的差異。
從左至右分別是:關閉劉海屏、開啟劉海屏但不支援、適配劉海屏。
一個全屏的頁面,當沒有支援劉海屏又碰到了劉海屏,會導致 UI 下沉,如果這不是一個列表的佈局,底部的控制元件就會被遮擋。
例如下面這樣的情況:
圖片來自:華為適配指南
還有一些被劉海遮擋區域的效果,其實主要是依賴 UI 設計師來規避了,不要在可能出現劉海切割的地方,設計可操作的區域,影響使用者操作。
三、技術適配劉海屏
說那麼多,最終我們還是需要用技術的方式來適配劉海屏。Android P 的劉海屏,是有標準的 Api 來進行適配,而對於一些廠商自己的劉海屏裝置,例如:OPPO x21,就需要遵循它的開發文件進行單獨適配。
Android P 為最新的劉海屏,提供了專門的 Api 來支援:DisplayCutout。
3.1 開啟劉海屏
我們在全屏的頁面,需要單獨開啟支援劉海屏。而 Google 提供的適配方案,可以設定是否在全屏模式下,使用劉海屏的區域。
WindowManager.LayoutParams lp
=getWindow().getAttributes();
lp.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
getWindow().setAttributes(lp);
新的佈局屬性 layoutInDisplayCutoutMode
包含三種可選的模式,
public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS = 1;
public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT = 0;
public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER = 2;
3.2 劉海屏的高度
在全屏模式下,我們需要有辦法獲取到劉海屏凹槽的高度,才可以做到設計和佈局的時候,留出安全距離。
雖然 Google 要求,劉海屏的凹槽,必須和劉海的高度保持一致,而劉海屏又被隱藏在狀態列了,所以有一個思路是直接獲取狀態列的高度,來判斷劉海之外,可佈局的安全區域。
不過 Android P 已經預留出了標準的測量 劉海屏凹槽 的 Api:DisplayCutout
。
劉海屏的凹槽,就在螢幕的中間,所以只有 getSafeInsetTop()
方法返回的結果,是我們需要的,而其他的 getSafeInsetXxx()
方法,直接返回的是 0 。
view.postDelayed(new Runnable() {
@Override
public void run() {
DisplayCutout displayCutout = view.getRootWindowInsets().getDisplayCutout();
Log.i("cxmyDev", "SafeInsetBottom:" + displayCutout.getSafeInsetBottom());
Log.i("cxmyDev", "SafeInsetLeft:" + displayCutout.getSafeInsetLeft());
Log.i("cxmyDev", "SafeInsetRight:" + displayCutout.getSafeInsetRight());
Log.i("cxmyDev", "SafeInsetTop:" + displayCutout.getSafeInsetTop());
}
}, 100);
得到的結果,也可以看一下:
I/cxmyDev: SafeInsetBottom:0
I/cxmyDev: SafeInsetLeft:0
I/cxmyDev: SafeInsetRight:0
I/cxmyDev: SafeInsetTop:112
3.3 非標準 Api
像 OPPO 這樣的廠商,實現劉海屏的方式,也並不是按照 Android P 的標準做的,它完全是自己修改了劉海屏的實現方式。不過好在,都是會提供完備的適配文件,這就需要我們直接閱讀他們提供的開發文件來進行適配。
oppo 的劉海屏適配文件:
https://open.oppomobile.com/wiki/doc#id=10139
對於 OPPO 而言,它劉海的高度是固定的,就是 80px。
判斷當前裝置是否是劉海屏,也提供了對應的 Api,可以用以下方法獲取。
context.getPackageManager().hasSystemFeature(“com.oppo.feature.screen.heteromorphism”)
返回 true 為劉海屏,但是這種方法只能識別 OPPO 品牌所支援的劉海屏。