一巨坊、簡介
隨著 Apple 發(fā)布 iPhone X 之后艇棕,各大手機廠商也開始模仿這種劉海屏的設(shè)計,而且劉海屏手機的用戶也是越來越大,前段時間將項目進行了所有主流廠商的劉海屏手機的適配矢渊,以便讓劉海屏手機的用戶也能有更好的體驗。
二枉证、劉海屏造成的 UI 顯示問題
劉海屏手機因為比平常的手機多了一塊頂部的遮擋性劉海矮男,所以會造成頂部 Toolbar 以及搜索框的遮擋,而且有些廠商的手機(vivo室谚、華為)毡鉴,默認是在「無狀態(tài)欄」的界面將狀態(tài)欄進行黑化顯示,這時候會導(dǎo)致系統(tǒng)下移秒赤,從而導(dǎo)致底部的一些 UI 被截斷猪瞬。除此之外,一些控件的顯示規(guī)則還會受到影響入篮,如 PopupWindow 的顯示高度會在「無狀態(tài)欄」的界面中比普通手機低一個「劉海的高度」陈瘦,從而遮擋住原先在 PopupWindow 周圍的圖標(biāo)。
1潮售、系統(tǒng)下移造成的底部 UI 截斷
2痊项、劉海擋住標(biāo)題欄和搜索框
3、PopupWindow 顯示異常
三酥诽、通用的適配方案
理論上來講鞍泉,通過 Android P 版本提供的劉海屏相關(guān)接口,判斷手機是否為劉海屏手機肮帐,以及進行一些相應(yīng)的處理是最合適的方式咖驮,但現(xiàn)在使用在國內(nèi)使用 Android P 的接口是不現(xiàn)實的,所以只能通過各大廠商提供的技術(shù)文檔來進行適配训枢,但適配的流程基本是一致的游沿。
其中需要著重處理的是:
????????1、應(yīng)用是否已經(jīng)適配劉海屏
????????2肮砾、頁面是否顯示狀態(tài)欄
3.1 應(yīng)用是否已經(jīng)適配劉海屏
現(xiàn)在國內(nèi)的主流機型(華為、vivo袋坑、OPPO仗处、小米)在劉海屏的顯示上分為兩個陣營:
? ? ? ? ?1、 當(dāng)不顯示狀態(tài)欄時枣宫,直接將界面進行顯示婆誓,「狀態(tài)欄原先的位置也用于顯示界面」,例如:OPPO
? ? ? ? ?2也颤、當(dāng)不顯示狀態(tài)欄時洋幻,直接「將狀態(tài)欄原先的位置進行黑化,界面整體下移」翅娶,例如:華為文留、vivo
所以好唯,我們在進行劉海屏適配的時候,首先需要通過一些手段燥翅,統(tǒng)一各大廠商的顯示方案骑篙,讓所有的劉海屏手機都利用狀態(tài)欄的界面,「告知系統(tǒng)」我們已經(jīng)適配了劉海屏森书,確保系統(tǒng)不會下移我們的應(yīng)用靶端,保留原生體驗。
這里主要有兩種方式:
1凛膏、設(shè)置屏幕高寬比例
因為劉海屏手機的「寬高比」比之前的手機大杨名,如果不適配的話,Android 默認為最大的寬高比為 1.86猖毫,
小于劉海屏手機的寬高比台谍,因此我們需要申明更高的寬高比來告訴系統(tǒng),我們應(yīng)用已經(jīng)適配了劉海屏鄙麦。
只要在 AndroidManifest.xml 中加入如下配置:
也可以在 Application 添加屬性:
android:maxAspectRatio="ratio_float"
ps:這個屬性需要 API 26 才支持
2典唇、設(shè)置應(yīng)用支持 resize
我們還可以通過設(shè)置應(yīng)用支持 resizeable,來告訴系統(tǒng)我們適配了劉海屏胯府,而且這也是 Google 官方推薦的方式介衔。不過需要注意的是,使用這個屬性之后骂因,應(yīng)用也會跟著支持分屏模式炎咖。只需要在 AndroidManifest.xml 中添加:
android:resizeableActivity="true"
3.2 頁面是否顯示狀態(tài)欄
對于劉海屏適配,我們將界面分為兩種:
????????對于有狀態(tài)欄的界面寒波,不會受到劉海屏的影響
????????全屏顯示的界面(無狀態(tài)欄)乘盼,需要根據(jù)界面的顯示進行一些控件的下移
因此,我們進行劉海屏適配俄烁,其實針對的就是沒有狀態(tài)欄的界面绸栅,而有狀態(tài)欄的界面顯示是正常的。對于沒有狀態(tài)欄的界面页屠,主要是將對被劉海遮擋到的控件粹胯,設(shè)置對應(yīng)劉海高度的 MarginTop,從而避免控件被遮擋辰企。而對于底部可能被截斷的界面风纠,可以考慮將底部做成 ScrollView 的形式。
四牢贸、各廠商的適配方案
現(xiàn)在 Android P 的接口還沒法用竹观,但各手機廠商都制定了自己的 API,對此我們需要對各大機型進行特殊的適配,這里主要介紹 vivo臭增、OPPO懂酱、華為 這三種主流手機的適配方案。
華為
華為作為國內(nèi)的手機廠商大頭速址,自己仿照 Android P 提供的 API玩焰,實現(xiàn)了一套幾乎差不多的 API,所以我們?nèi)绻胍嬖V系統(tǒng)我們的應(yīng)用適配了劉海屏芍锚,最好直接使用華為的 API昔园,這樣才是最保險的。
以下代碼來自:華為劉海屏適配官方技術(shù)指導(dǎo)
https://mini.eastday.com/bdmip/180411011257629.html
1并炮、應(yīng)用頁面設(shè)置使用劉海區(qū)顯示
① 方案一:在 AndroidManifest.xml 中增加 meta-data 屬性默刚,此屬性不僅可以針對 Application 生效,也可以對 Activity 配置生效:
增加這個屬性之后逃魄,系統(tǒng)就會對應(yīng)用進行下移處理荤西,從而保證原生體驗。
② 方案二:通過添加窗口 FLAG 的方式設(shè)置界面使用劉海區(qū):
public static void setFullScreenWindowLayoutInDisplayCutout(Window window) {? ?
????????if (window == null) {? ? ? ? return;? ? }? ?
????????WindowManager.LayoutParams layoutParams = window.getAttributes();? ?
????????try {? ? ? ?
????????????Class layoutParamsExCls = Class.forName("com.huawei.android.view.LayoutParamsEx");? ? ? ?
????????????Constructor con=layoutParamsExCls.getConstructor(LayoutParams.class);? ? ? ? ????????????Object layoutParamsExObj=con.newInstance(layoutParams);? ? ? ?
????????????Method method=layoutParamsExCls.getMethod("addHwFlags", int.class);? ? ? ? ????????????method.invoke(layoutParamsExObj, FLAG_NOTCH_SUPPORT);? ?
????} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException |InstantiationException? ? | InvocationTargetException e) {? ? ? ? Log.e("test", "hw add notch screen flag api error");? ? } catch (Exception e) {? ? ? ? Log.e("test", "other Exception");? ? }}
2伍俘、判斷該華為手機是否劉海屏
? ? public static boolean hasNotchInHuawei(Context context) {? ? ? ?
????????????boolean hasNotch = false;? ? ? ?
????????????try {? ? ? ? ? ?
????????????????????ClassLoader cl = context.getClassLoader();? ? ? ? ? ?
????????????????????Class HwNotchSizeUtil = ????????????????????cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? Method hasNotchInScreen =HwNotchSizeUtil.getMethod("hasNotchInScreen");? ? ? ? ? ?
????????????????if(hasNotchInScreen != null) {? ? ? ? ? ? ? ?
????????????????????????hasNotch = (boolean) hasNotchInScreen.invoke(HwNotchSizeUtil);? ? ? ? ?
? }? ? ? ? } catch (Exception e) {? ? ? ? ? ? e.printStackTrace();? ? ? ? }? ? ? ? return hasNotch;? ? }
3邪锌、獲取劉海的高度
? 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;? ? ? ? }
OPPO
OPPO 是主流廠商中的一股清流,學(xué) iPhoneX 是最像的癌瘾,OPPO 手機對于不顯示狀態(tài)欄的界面觅丰,采取的是「狀態(tài)欄原先的位置也用于顯示界面」的方案,所以我們只要進行相關(guān)控件的位置移動就可以了妨退。
以下代碼來自: OPPO 凹形屏適配說明
https://open.oppomobile.com/wiki/doc#id=10159
1妇萄、判斷該 OPPO 手機是否為劉海屏手機
?? public static boolean hasNotchInOppo(Context context) {? ? ? ?
return context.getPackageManager().hasSystemFeature("com.oppo.feature.screen.heteromorphism");? ? }
2、獲取劉海屏的高度
對于 OPPO 劉海屏手機的劉海高度咬荷,OPPO 官方的文檔沒有提供相關(guān)的 API冠句,但官方文檔表示 OPPO 手機的劉海高度和狀態(tài)欄的高度是一致的,而且我也對此進行了驗證幸乒,確實如此懦底。所以我們可以直接獲取狀態(tài)欄的高度,作為 OPPO 手機的劉海高度罕扎。
public static int getStatusBarHeight(Context context) {? ?
????????int statusBarHeight = 0;? ?
????????int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");? ?
????????if (resourceId > 0) {? ? ? ?
????????????statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);? ?
????????}? ?
????????return statusBarHeight ;
}
vivo
vivo 提供的技術(shù)文檔對于開發(fā)者來說是最不友好的基茵,只提供了一個 API 來進行劉海屏的判斷,并沒有提供劉海高度的獲取方式壳影,我們只能通過獲取狀態(tài)欄高度來當(dāng)做劉海的高度,但在某些機型可能會有些偏差弥臼。
官方文檔:vivo 手機適配指南
https://dev.vivo.com.cn/doc/document/info?id=103
判斷該 vivo 手機是否為劉海屏手機
public static boolean hasNotchInVivo(Context context) {? ? ? ?
????????boolean hasNotch = false;? ? ? ?
????????try {? ? ? ? ? ?
????????????????ClassLoader cl = context.getClassLoader();? ? ? ? ? ?
????????????????Class ftFeature = cl.loadClass("android.util.FtFeature");? ? ? ? ? ?
????????????????Method[] methods = ftFeature.getDeclaredMethods();? ? ? ? ? ?
????????????????if (methods != null) {? ? ? ? ? ? ? ?
????????????????????????for (int i = 0; i < methods.length; i++) {? ? ? ? ? ? ? ? ? ?
????????????????????????????????Method method = methods[i];? ? ? ? ? ? ? ? ? ?
????????????????????????????????if(method != null) {? ? ? ? ? ? ? ? ? ? ? ?
????????????????????????????????????????if (method.getName().equalsIgnoreCase("isFeatureSupport")) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hasNotch = (boolean) method.invoke(ftFeature, 0x00000020);? ? ? ? ? ? ? ? ? ? ? ? ? ?
????????????????????break;? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? } catch (Exception e) {? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? hasNotch = false;? ? ? ? }? ? ? ? return hasNotch;? ? }
五宴咧、總結(jié)
以上便是在之前在進行 Android 劉海屏適配的時候,所積累的一些經(jīng)驗和心得径缅。將其記錄下來掺栅,以便自己以后進行回顧烙肺,同時也希望這篇文章能對進行劉海屏適配的同學(xué)一些幫助。