隨著iPhone X發(fā)布,國內(nèi)一些廠商也推出了劉海屏手機(jī),即將發(fā)布的Android p也提供了對劉海屏的支持外邓。so揩懒,我們的app也要提前做好適配。
什么是劉海屏?
屏幕的正上方居中位置(下圖黑色區(qū)域)會被挖掉一個孔,屏幕被挖掉的區(qū)域無法正常顯示內(nèi)容单旁,這種類型的屏幕就是劉海屏,也有其他叫法:挖孔屏饥伊、凹凸屏等等象浑,這里統(tǒng)一按劉海屏命名。
目前國內(nèi)廠商已經(jīng)推出的劉海屏Android手機(jī)有華為P20 pro琅豆, vivo X21愉豺,OPPO R15。
如果沒有適配劉海屏?xí)惺裁春蠊?
后果一:導(dǎo)航欄中title被遮擋
后果二:顯示內(nèi)容下移,頭部出現(xiàn)黑條,底部出現(xiàn)遮擋
如何適配劉海屏?
由于Android p正式版今日剛發(fā)布, 當(dāng)前市面上的Android 劉海屏手機(jī)還不能用Android 官方提供的方案來解決,那怎么辦呢?還好幾個廠商自己給出了適配方案茫因。我們先講理論后上代碼蚪拦,如果您只想要代碼就直接往下翻:
華為P20 pro
華為劉海屏適配官方文檔
華為給出的文檔最為詳細(xì)(業(yè)界良心),P20 pro預(yù)裝系統(tǒng)對未做劉海屏適配處理的app有一定處理,處理邏輯如下
可見冻押,會被華為系統(tǒng)做偏移處理的有2種情況:
1.未設(shè)置meta-data值驰贷,頁面橫屏狀態(tài)
2.未設(shè)置meta-data值,頁面豎屏狀態(tài)洛巢,不顯示狀態(tài)欄
這2種情況都會出現(xiàn)后果二括袒。如果你的app中頁面沒有這兩種情況,例如都是豎屏且顯示狀態(tài)欄稿茉,你就可以淡定地不做處理锹锰。
現(xiàn)在我們知道原因了就可以對癥下藥了,這里給出我推薦的解決方案(官方給出的解決方案不止一種,可以根據(jù)自己的需要采用) 分為4步:
1.配置meta-data
<meta-data android:name="android.notch_support" android:value="true"/>
①對Application生效,意味著該應(yīng)用的所有頁面漓库,系統(tǒng)都不會做豎屏場景的特殊下移或者是橫屏場景的右移特殊處理:
② 對Activity生效恃慧,意味著可以針對單個頁面進(jìn)行劉海屏適配,設(shè)置了該屬性的Activity系統(tǒng)將不會做特殊處理:
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適配
沒錯,第1步僅僅是告訴EMUI系統(tǒng)不要瞎操作你的頁面米苹,真正適配的活還要你自己干糕伐。
①判斷是否劉海屏,代碼上面給出了
②如果是劉海屏手機(jī)需要應(yīng)用自己調(diào)整布局避開劉海區(qū)蘸嘶,布局原則:保證重要的文字良瞧、圖片和視頻信息陪汽、可點擊的控件和圖標(biāo)還有應(yīng)用彈窗等等布局建議顯示在狀態(tài)欄區(qū)域以下(安全區(qū)域);不重要褥蚯,遮擋不會出現(xiàn)問題的布局可以延伸到狀態(tài)欄區(qū)域(危險區(qū)域)顯示挚冤,按照這種布局原則修改,可以一次修改就能適配所有的劉海屏手機(jī):
vivo & OPPO
vivo 和 OPPO官網(wǎng)僅僅給出了適配指導(dǎo)赞庶,沒有給出具體方案训挡,簡單總結(jié)為:
如有是具有劉海屏的手機(jī),豎屏顯示狀態(tài)欄歧强,橫屏不要在危險區(qū)顯示重要信息或者設(shè)置點擊事件澜薄。
那怎么知道是不是劉海屏手機(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; }
}
例如圖一是在OPPO R15上出現(xiàn)的title被遮擋,顯示狀態(tài)欄后顯示效果如下:
以上所講的是各廠商提供的解決方案, 那么google官方對劉海屏做了什么樣的支持呢?
google官方劉海屏適配方案
google從Android P開始為劉海屏提供支持摊册,目前提供了一個類和三種模式:
一個類
The new DisplayCutout
class lets you find out the location and shape of the non-functional areas where content shouldn't be displayed. To determine the existence and placement of these cutout areas, use thegetDisplayCutout()
method
就是說可以用DisplayCutout這個類找出劉海(cutout)的位置和形狀肤京,調(diào)用getDisplayCutout()這個方法可以獲取劉海(cutout)的位置和區(qū)域,我們看看這個類提供了什么方法:
所以我們可用這個類判斷是否有劉海的存在以及劉海的位置
DisplayCutout cutout = mContext.getDisplayCutout();
三種模式
A new window layout attribute, layoutInDisplayCutoutMode
, allows your app to lay out its content around a device's cutouts. You can set this attribute to one of the following values:
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
-
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
第一種模式:
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT : 僅僅當(dāng)系統(tǒng)提供的bar完全包含了劉海區(qū)時才允許window擴(kuò)展到劉海區(qū),否則window不會和劉海區(qū)重疊
第二種模式:
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES :允許window擴(kuò)展到劉海區(qū)(原文說的是短邊的劉海區(qū), 目前有劉海的手機(jī)都在短邊,所以就不糾結(jié)了)
第三種模式:
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER: 不允許window擴(kuò)展到劉海區(qū)
我們可以設(shè)置是否允許window擴(kuò)展到劉海區(qū)
WindowManager.LayoutParams lp =getWindow().getAttributes();
lp.layoutInDisplayCutoutMode
=WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
getWindow().setAttributes(lp);
例如一個有狀態(tài)欄的頁面, 我們可以這樣適配:
DisplayCutout cutout = getDisplayCutout();
if(cutout != null){
WindowManager.LayoutParams lp =getWindow().getAttributes();
lp.layoutInDisplayCutoutMode=WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
getWindow().setAttributes(lp);
}
如果覺得有幫助, 點個贊再走可好啊~