Android 劉海屏和全面屏適配

背景

自從iphone x發(fā)布后,各大廠商也發(fā)布了類似的劉海屏手機(jī)(“頂部屏幕凹槽設(shè)計(jì)”),開發(fā)者應(yīng)該如何適配呢?

原理

為什么會(huì)有劉海屏?

因?yàn)榇蠹矣凶耘牡男枨?,需要攝像頭前置,除了攝像頭前置外,劉海屏上還有一些其他的傳感器,所以不同廠商的劉海屏長度也不相同。


這里主要是介紹一下Android P發(fā)布之后劉海屏的適配以及Android P之前的適配。為什么要分開呢?因?yàn)锳ndroid P之前官方還沒提供API來進(jìn)行適配,都是由各家廠商來提供適配方案的。

2.6 那么劉海屏該如何適配呢?

2.6.1 如果頁面存在狀態(tài)欄

[if !supportLists]·?[endif]那么很簡單,不用適配,因?yàn)閯⒑^(qū)域會(huì)包含在狀態(tài)欄中了。

[if !supportLists]·?[endif]如果不想看到劉海區(qū)域,可以使用LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER將劉海區(qū)域變成一條黑色邊。

2.6.2 如果頁面是全屏顯示

[if !supportLists]·?[endif]不適配的話將會(huì)留出一條黑色邊。

[if !supportLists]·?[endif]要做到真正全屏的話,那么就先要獲取到劉海的區(qū)域(危險(xiǎn)區(qū)域),內(nèi)容部分(操作按鈕等)應(yīng)當(dāng)避開危險(xiǎn)區(qū)域,保證在安全區(qū)域中展示。橫屏的話兩邊都需要注意避開劉海(危險(xiǎn)區(qū)域)。



華為適配劉海屏主要有以下幾個(gè)步驟:?1.配置meta-data?

[if !supportLists]2.?[endif]檢測是否存在劉海屏3.獲取劉海屏的參數(shù)4. UI適配

?

?

?

?

1.配置meta-data?華為新增的Meta-data屬性android.notch_support在應(yīng)用的AndroidManifest.xml中增加meta-data屬性,此屬性不僅可以針對(duì)Application生效,也可以對(duì)Activity配置生效,具體方式如下所示:

[if !supportLists]·?[endif]1

①對(duì)Application生效,意味著該應(yīng)用的所有頁面,(會(huì)對(duì)你App的所有頁面都進(jìn)行適配)系統(tǒng)都不會(huì)做豎屏場景的特殊下移或者是橫屏場景的右移特殊處理:

?② 對(duì)Activity生效,意味著可以針對(duì)單個(gè)頁面進(jìn)行劉海屏適配,設(shè)置了該屬性的Activity系統(tǒng)將不會(huì)做特殊處理:?


2.檢測是否存在劉海屏

public?static?boolean?hasNotchInScreen(Context context) {

???boolean?ret = false;

???try?{

???????ClassLoader cl = context.getClassLoader();

???????Class HwNotchSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");

???????Method get = HwNotchSizeUtil.getMethod("hasNotchInScreen");

???????ret = (boolean) get.invoke(HwNotchSizeUtil);

???} catch?(ClassNotFoundException e) {

???????Log.e("test", "hasNotchInScreen ClassNotFoundException");

???} catch?(NoSuchMethodException e) {

???????Log.e("test", "hasNotchInScreen NoSuchMethodException");

???} catch?(Exception e) {

???????Log.e("test", "hasNotchInScreen Exception");

???} finally?{

???????return?ret;

???}

}

?

?

3.獲取劉海屏的參數(shù)

public?static?int[] getNotchSize(Context context) {

???int[] ret = new?int[]{0, 0};

???try?{

???????ClassLoader cl = context.getClassLoader();

???????Class HwNotchSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");

???????Method get?= HwNotchSizeUtil.getMethod("getNotchSize");

???????ret = (int[]) get.invoke(HwNotchSizeUtil);

???} catch?(ClassNotFoundException e) {

???????Log.e("test", "getNotchSize ClassNotFoundException");

???} catch?(NoSuchMethodException e) {

???????Log.e("test", "getNotchSize NoSuchMethodException");

???} catch?(Exception e) {

???????Log.e("test", "getNotchSize Exception");

???} finally?{

???????return?ret;

???}

}

?

4. UI適配?通過增加上面適配方案提到的配置(meta-data或者是Flag),應(yīng)用在華為劉海屏手機(jī)上就能夠默認(rèn)使用劉海區(qū)顯示了,但是為了避免出現(xiàn)UI被劉海區(qū)遮擋的問題,還是需要應(yīng)用自己做一些額外的UI適配工作:(1)判斷是否劉海屏,通過華為劉海屏SDK的API判斷,具體參考3.2.1章節(jié)(2)如果是劉海屏手機(jī)需要應(yīng)用自己調(diào)整布局避開劉海區(qū),布局原則:保證重要的文字、圖片和視頻信息、可點(diǎn)擊的控件和圖標(biāo)還有應(yīng)用彈窗等等布局建議顯示在狀態(tài)欄區(qū)域以下(安全區(qū)域);不重要,遮擋不會(huì)出現(xiàn)問題的布局可以延伸到狀態(tài)欄區(qū)域(危險(xiǎn)區(qū)域)顯示,按照這種布局原則修改,可以一次修改就能適配所有的劉海屏手機(jī):


獲取系統(tǒng)狀態(tài)欄高度接口:

public?static?int?getStatusBarHeight(Context context) {

????int?result = 0;

????int?resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");

????if?(resourceId > 0) {

????????result = context.getResources().getDimensionPixelSize(resourceId);

????}

????return?result;

}

?

?

vivo & OPPO

vivo 和 OPPO官網(wǎng)僅僅給出了適配指導(dǎo),沒有給出具體方案,簡單總結(jié)為:?如有是具有劉海屏的手機(jī),豎屏顯示狀態(tài)欄,橫屏不要在危險(xiǎn)區(qū)顯示重要信息或者設(shè)置點(diǎn)擊事件。

首先,判斷是不是劉海屏手機(jī)。OPPO判斷方法:

public?static?boolean?hasNotchInOppo(Context context){

???return?context.getPackageManager().hasSystemFeature("com.oppo.feature.screen.heteromorphism");

}



vivo的判斷方法:

public?static?final?int?NOTCH_IN_SCREEN_VOIO=0x00000020;//是否有凹槽public?static?final?int?ROUNDED_IN_SCREEN_VOIO=0x00000008;//是否有圓角public?static?boolean?hasNotchInScreenAtVoio(Context context){

???boolean?ret = false;

???try?{

???????ClassLoader cl = context.getClassLoader();

???????Class FtFeature = cl.loadClass("com.util.FtFeature");

???????Method get = FtFeature.getMethod("isFeatureSupport",int.class);

???????ret = (boolean) get.invoke(FtFeature,NOTCH_IN_SCREEN_VOIO);


???} catch?(ClassNotFoundException e)

???{ Log.e("test", "hasNotchInScreen ClassNotFoundException"); }

???catch?(NoSuchMethodException e)

???{ Log.e("test", "hasNotchInScreen NoSuchMethodException"); }

???catch?(Exception e)

???{ Log.e("test", "hasNotchInScreen Exception"); }

???finally

???{ return?ret; }

}

然后在進(jìn)行適配,官方這方面的資料很少也不是很詳細(xì)

?

google官方

google從Android P開始為劉海屏提供支持,目前提供了一個(gè)類和三種模式:?一個(gè)類指的是可以用DisplayCutout這個(gè)類找出劉海(cutout)的位置和形狀,調(diào)用getDisplayCutout()這個(gè)方法可以獲取劉海(cut

out)的位置和區(qū)域。

3.1 開啟劉海屏

我們在全屏的頁面,需要單獨(dú)開啟支持劉海屏。而Google 提供的適配方案,可以設(shè)置是否在全屏模式下,使用劉海屏的區(qū)域。

WindowManager.LayoutParams lp

????????????????=getWindow().getAttributes();

lp.layoutInDisplayCutoutMode =

????????????????WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;

getWindow().setAttributes(lp);

新的布局屬性layoutInDisplayCutoutMode?包含三種可選的模式,

模式模式說明?

LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT只有當(dāng)DisplayCutout完全包含在系統(tǒng)欄中時(shí),才允許窗口延伸到DisplayCutout區(qū)域。 否則,窗口布局不與DisplayCutout區(qū)域重疊。?

LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER該窗口決不允許與DisplayCutout區(qū)域重疊。?

LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES該窗口始終允許延伸到屏幕短邊上的DisplayCutout區(qū)域。?

[if !supportLists]1.?[endif]LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES模式會(huì)讓屏幕到延申劉海區(qū)域中。

[if !supportLists]2.?[endif]LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER模式不會(huì)讓屏幕到延申劉海區(qū)域中,會(huì)留出一片黑色區(qū)域。

[if !supportLists]3.?[endif]LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT模式在全屏顯示下跟LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER一樣。

這里設(shè)置為全屏的顯示效果,三種模式的結(jié)果如下圖所示


[if !supportLists]1.?[endif]LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES模式會(huì)讓屏幕到延申劉海區(qū)域中。

[if !supportLists]2.?[endif]LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER模式不會(huì)讓屏幕到延申劉海區(qū)域中,會(huì)留出一片黑色區(qū)域。

[if !supportLists]3.?[endif]LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT模式在全屏顯示下跟LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER一樣。




LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT模式在沉浸式狀態(tài)欄下的效果:



3.2 劉海屏的高度

在全屏模式下,我們需要有辦法獲取到劉海屏凹槽的高度,才可以做到設(shè)計(jì)和布局的時(shí)候,留出安全距離。

雖然Google 要求,劉海屏的凹槽,必須和劉海的高度保持一致,而劉海屏又被隱藏在狀態(tài)欄了,所以有一個(gè)思路是直接獲取狀態(tài)欄的高度,來判斷劉海之外,可布局的安全區(qū)域。

不過Android P 已經(jīng)預(yù)留出了標(biāo)準(zhǔn)的測量 劉海屏凹槽 的 Api:DisplayCutout。

劉海屏的凹槽,就在屏幕的中間,所以只有g(shù)etSafeInsetTop()?方法返回的結(jié)果


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容