Android劉海屏、水滴屏全面屏適配小結(jié)
現(xiàn)在,市面上的屏幕尺寸和全面屏方案五花八門。這里我使用了小米的圖來說明:

上述兩種屏幕都可以統(tǒng)稱為劉海屏,不過對于右側(cè)較小的劉海,業(yè)界一般稱為水滴屏或美人尖。為便于說明,后文提到的「劉海屏」「劉海區(qū)」都同時指代上圖兩種屏幕。
劉海屏、水滴屏全面屏適配細(xì)節(jié)
當(dāng)我們在談屏幕適配時,我們具體談什么呢?
- 適應(yīng)更長的屏幕
- 防止內(nèi)容被劉海遮擋
其中第一點是所有應(yīng)用都需要適配的,對應(yīng)下文的聲明最大長寬比,而第二點,如果應(yīng)用本身不需要全屏顯示或使用沉浸式狀態(tài)欄,是不需要適配的。
針對需要適配第二點的應(yīng)用,需要獲取劉海的位置和寬高,然后將顯示內(nèi)容避開即可。
聲明最大長寬比
以前的普通屏長寬比為16:9,全面屏手機的屏幕長寬比增大了很多,如果不適配的話就會類似下面這樣:

適配方式
適配方式有兩種:
- 將targetSdkVersion版本設(shè)置到API 24及以上;
這個操作將會為<application> 標(biāo)簽隱式添加一個屬性,android:resizeableActivity="true", 該屬性的作用后面將詳細(xì)說明。
在 <application> 標(biāo)簽中增加屬性:android:resizeableActivity="false",同時在節(jié)點下增加一個meta-data標(biāo)簽:
<!-- Render on full screen up to screen aspect ratio of 2.4 --> <!-- Use a letterbox on screens larger than 2.4 --> <meta-data android:name="android.max_aspect" android:value="2.4" />
原理說明
在 Android 7.0(API 級別 24)或更高版本的應(yīng)用,android:resizeableActivity屬性默認(rèn)為true(對應(yīng)適配方式1)。這個屬性是控制多窗口顯示的,決定當(dāng)前的應(yīng)用或者Activity是否支持多窗口。
可以在清單的<activity>或 <application>節(jié)點中設(shè)置該屬性,啟用或禁用多窗口顯示,配置如下:
android:resizeableActivity=["true" | "false"]
如果該屬性設(shè)置為 true,Activity 將能以分屏和自由形狀模式啟動。 如果此屬性設(shè)置為 false,Activity 將不支持多窗口模式。 如果該值為 false,且用戶嘗試在多窗口模式下啟動 Activity,該 Activity 將全屏顯示。
適配方式2即為設(shè)置屏幕的最大長寬比,這是官方提供的設(shè)置方式。
如果設(shè)置了最大長寬比,必須android:resizeableActivity="false"。 否則最大長寬比沒有任何作用。
適配劉海屏
Android9.0適配
Android P(9.0)開始,官方開始提供了官方的挖孔屏適配API,具體可以參考Support display cutouts。
通過Android P提供的 DisplayCutout 類,可以確定非功能區(qū)域的位置和形狀,這些區(qū)域不應(yīng)顯示內(nèi)容。 要確定這些凹口屏幕區(qū)域是否存在及其位置,請使用 getDisplayCutout() 函數(shù)。
全新的窗口布局屬性 layoutInDisplayCutoutMode 讓您的應(yīng)用可以為設(shè)備凹口屏幕周圍的內(nèi)容進(jìn)行布局。 您可以將此屬性設(shè)為下列值之一:
- LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
- LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
- LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
默認(rèn)值是LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT,劉海區(qū)域不會顯示內(nèi)容,需要顯示時可以將值設(shè)置為LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES。
您可以按如下步驟在任何運行 Android P 的設(shè)備或模擬器上模擬屏幕缺口:
- 啟用開發(fā)者選項;
- 在 Developer options 屏幕中,向下滾動至 Drawing 部分并選擇 Simulate a display with a cutout。
適配參考示例:
// 延伸顯示區(qū)域到劉海 WindowManager.LayoutParams lp = window.getAttributes(); lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; window.setAttributes(lp); // 設(shè)置頁面全屏顯示 final View decorView = window.getDecorView(); decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
其中延伸顯示區(qū)域到劉海的代碼,也可以通過修改Activity或應(yīng)用的style實現(xiàn),例如:
<?xml version="1.0" encoding="utf-8"?> <resources> <style name="AppTheme" parent="xxx"> <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item> </style> </resources>
Android O 適配
因Google官方的適配方案到Android P才推出,因此在Android O(8.0版本)設(shè)備上,各家廠商有自己的實現(xiàn)方案。
華為Android O適配
方案一:
具體方式如下所示:
<meta-data android:name="android.notch_support" android:value="true"/>
對Application生效,意味著該應(yīng)用的所有頁面,系統(tǒng)都不會做豎屏場景的特殊下移或者是橫屏場景的右移特殊處理。例如:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:testOnly="false"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<meta-data android:name="android.notch_support" android:value="true"/>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
對Activity生效,意味著可以針對單個頁面進(jìn)行劉海屏適配,設(shè)置了該屬性的Activity系統(tǒng)將不會做特殊處理。例如:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:testOnly="false"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".LandscapeFullScreenActivity" android:screenOrientation="sensor">
</activity>
<activity android:name=".FullScreenActivity">
<meta-data android:name="android.notch_support" android:value="true"/>
</activity>
</application>
方案二:
對Application生效,意味著該應(yīng)用的所有頁面,系統(tǒng)都不會做豎屏場景的特殊下移或者是橫屏場景的右移特殊處理。
1,設(shè)置應(yīng)用窗口在華為劉海屏手機使用劉海區(qū)。
/*劉海屏全屏顯示FLAG*/
public static final int FLAG_NOTCH_SUPPORT=0x00010000;
/**
* 設(shè)置應(yīng)用窗口在華為劉海屏手機使用劉海區(qū)
* @param window 應(yīng)用頁面window對象
*/
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.清除添加的華為劉海屏Flag,恢復(fù)應(yīng)用不使用劉海區(qū)顯示。
public static void setNotFullScreenWindowLayoutInDisplayCutout (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("clearHwFlags", int.class);
method.invoke(layoutParamsExObj, FLAG_NOTCH_SUPPORT);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException |InstantiationException
| InvocationTargetException e) {
Log.e("test", "hw clear notch screen flag api error");
} catch (Exception e) {
Log.e("test", "other Exception");
}
}
小米Android O適配
判斷是否是劉海屏。
private static boolean isNotch() {
try {
Method getInt = Class.forName("android.os.SystemProperties").getMethod("getInt", String.class, int.class);
int notch = (int) getInt.invoke(null, "ro.miui.notch", 0);
return notch == 1;
} catch (Throwable ignore) {
}
return false;
}
設(shè)置顯示到劉海區(qū)域
@Override
public void setDisplayInNotch(Activity activity) {
int flag = 0x00000100 | 0x00000200 | 0x00000400;
try {
Method method = Window.class.getMethod("addExtraFlags",
int.class);
method.invoke(activity.getWindow(), flag);
} catch (Exception ignore) {
}
}
獲取劉海寬高
public static int getNotchHeight(Context context) {
int resourceId = context.getResources().getIdentifier("notch_height", "dimen", "android");
if (resourceId > 0) {
return context.getResources().getDimensionPixelSize(resourceId);
}
return 0;
}
public static int getNotchWidth(Context context) {
int resourceId = context.getResources().getIdentifier("notch_width", "dimen", "android");
if (resourceId > 0) {
return context.getResources().getDimensionPixelSize(resourceId);
}
return 0;
}
oppo Android O適配
判斷是否是劉海屏
@Override
public boolean hasNotch(Activity activity) {
boolean ret = false;
try {
ret = activity.getPackageManager().hasSystemFeature("com.oppo.feature.screen.heteromorphism");
} catch (Throwable ignore) {
}
return ret;
}
獲取劉海的左上角和右下角的坐標(biāo)
private static String getScreenValue() {
String value = "";
Class<?> cls;
try {
cls = Class.forName("android.os.SystemProperties");
Method get = cls.getMethod("get", String.class);
Object object = cls.newInstance();
value = (String) get.invoke(object, "ro.oppo.screen.heteromorphism");
} catch (Throwable ignore) {
}
return value;
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android自定義控件之組合控件學(xué)習(xí)筆記分享
這篇文章主要為大家分享了Android自定義控件之組合控件學(xué)習(xí)筆記,具有一定的實用性和參考價值,感興趣的小伙伴們可以參考一下2016-05-05
Android開發(fā)實現(xiàn)TextView顯示豐富的文本
這篇文章主要介紹了Android開發(fā)實現(xiàn)TextView顯示豐富的文本,涉及Android中TextView的使用技巧,需要的朋友可以參考下2015-12-12
Android 多層嵌套后的 Fragment 懶加載實現(xiàn)示例
這篇文章主要介紹了Android 多層嵌套后的 Fragment 懶加載實現(xiàn)示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-04-04
AccessibilityService實現(xiàn)微信發(fā)紅包功能
這篇文章主要為大家詳細(xì)介紹了AccessibilityService實現(xiàn)微信發(fā)紅包功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-12-12
Android實現(xiàn)系統(tǒng)狀態(tài)欄的隱藏和顯示功能
這篇文章主要介紹了Android實現(xiàn)系統(tǒng)狀態(tài)欄的隱藏和顯示功能,文中還給大家?guī)硭姆N方法,大家可以根據(jù)自己需要參考下2018-07-07
Flutter利用ORM框架簡化本地數(shù)據(jù)庫管理詳解
使用?sqflite?相對來說還是有點復(fù)雜,比如遇到數(shù)據(jù)不兼容的時候需要手動轉(zhuǎn)換,增加了不少繁瑣的代碼。本篇我們就來介紹一個?ORM?框架,來簡化數(shù)據(jù)庫的管理,感興趣的可以了解一下2023-04-04
Android 重寫ViewGroup 分析onMeasure()和onLayout()方法
這篇文章主要介紹了Android 重寫ViewGroup 分析onMeasure()和onLayout()方法的相關(guān)資料,需要的朋友可以參考下2017-06-06
Android 類似UC瀏覽器的效果:向上滑動地址欄隱藏功能
這篇文章主要介紹了Android 類似UC瀏覽器的效果:向上滑動地址欄隱藏功能,需要的朋友可以參考下2017-12-12
Android設(shè)置TextView樣式SpannableString教程
這篇文章主要為大家介紹了Android設(shè)置TextView樣式SpannableString教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02

