無需再怨恨“劉海屏”了,因為適配十分簡單
網(wǎng)上關(guān)于劉海屏適配的文章不少,可講清楚的卻沒幾篇,大多是拷貝文檔、長篇大論,甚至熱情的貼圖告訴你什么是劉海屏,到***你仍不確定到底是怎樣的一個適配方案,才能讓你的 app 真正的適配所有的劉海屏機(jī)型。
看到這篇文章你就無需再怨恨各大廠商的跟風(fēng)“劉海”了,因為劉海屏的適配十分簡單。
ok,廢話說完了,開始適配。
首先要清楚的是哪些界面需要適配劉海屏:
- 有狀態(tài)欄的界面:劉海區(qū)域會顯示狀態(tài)欄,無需適配
- 全屏界面:劉海區(qū)域可能遮擋內(nèi)容,需要適配
如果你的應(yīng)用里所有界面都有狀態(tài)欄,那么恭喜你,你不用做任何操作,狀態(tài)欄就那么自然的顯示在劉海區(qū)域,毫無違和,劉海屏已適配完畢,可以點(diǎn)叉出去了。
不幸的是,你的應(yīng)用中很大幾率會有全屏界面,所謂的劉海屏適配,也正是針對這些全屏界面。
如果你什么都不做,默認(rèn)規(guī)則不允許全屏界面內(nèi)容顯示到劉海區(qū)域,即劉海屏區(qū)域會保留一條黑邊,你的全屏界面會在劉海下方展示,這看起來好像也是可以接受的,然后你竟說服產(chǎn)品達(dá)成共識,“無為而治”才是***大的劉海屏適配方案!
但有些手機(jī)廠商(譬如oppo)不開心了,我辛辛苦苦研發(fā)的劉海屏手機(jī),你們這些開發(fā)者竟直接放棄劉海區(qū)域!然后就在你的全屏界面下方加了一條提示:“全屏顯示”,當(dāng)用戶點(diǎn)擊開啟后,強(qiáng)行把你的全屏界面顯示到劉海區(qū)域,然后一切都亂套了...
嗯~ “無為而治”行不通。
只能允許全屏界面內(nèi)容顯示到劉海區(qū)域了,參考各大廠商的適配文檔,我們可以知道如何允許,比如華為機(jī)型只需在 AndroidManifest 中配置:
配置后,華為機(jī)型上的全屏界面就會顯示到劉海區(qū)域了,但這個劉海,是可能擋住我們?nèi)两缑嬷械膬?nèi)容的。這時需要將全屏界面中的視圖元素適當(dāng)下移,保證不會被劉海遮擋住,就 ok 了。
這里我們搞清楚:允許全屏界面內(nèi)容顯示到劉海區(qū)域的機(jī)型,才需要將全屏界面中的視圖元素適當(dāng)下移。
比如若只允許華為機(jī)型全屏界面內(nèi)容顯示到劉海區(qū)域,那只有華為的劉海屏機(jī)型才需要將全屏界面中的視圖元素適當(dāng)下移,其他廠商的劉海屏機(jī)型則不需要下移。
如果允許華為、小米、oppo、vivo 全屏界面內(nèi)容顯示到劉海區(qū)域,那么華為、小米、oppo、vivo 劉海屏機(jī)型需要將全屏界面中的視圖元素適當(dāng)下移。
另外也不一定要通過全屏界面中的視圖元素適當(dāng)下移方式來適配劉海屏,如果產(chǎn)品形態(tài)允許的話,你也可以讓該界面顯示狀態(tài)欄啊。
至此劉海屏適配完畢,是不是很簡單!?
***代碼奉上,拿走不謝:
1、允許全屏界面內(nèi)容顯示到劉海區(qū)域配置:
- <!--允許繪制到oppo、vivo劉海屏機(jī)型的劉海區(qū)域 -->
- <meta-data
- android:name="android.max_aspect"
- android:value="2.2" />
- <!-- 允許繪制到華為劉海屏機(jī)型的劉海區(qū)域 -->
- <meta-data
- android:name="android.notch_support"
- android:value="true" />
- <!-- 允許繪制到小米劉海屏機(jī)型的劉海區(qū)域 -->
- <meta-data
- android:name="notch.config"
- android:value="portrait" />
上面在 AndroidManifest 的配置在 Android 9.0 之前有效,9.0 系統(tǒng)針對劉海屏制定了新的 api,默認(rèn)保留一條黑邊,即不允許繪制到劉海區(qū)域。所以如果你還沒有適配 Android 9.0,那在判斷是否是允許全屏界面內(nèi)容顯示到劉海區(qū)域的劉海屏機(jī)型時,就要加上版本判斷。
2、判斷是否是允許全屏界面內(nèi)容顯示到劉海區(qū)域的劉海屏機(jī)型:
- public class CutoutUtil {
- private static Boolean sAllowDisplayToCutout;
- /**
- * 是否為允許全屏界面顯示內(nèi)容到劉海區(qū)域的劉海屏機(jī)型(與AndroidManifest中配置對應(yīng))
- */
- public static boolean allowDisplayToCutout() {
- if (sAllowDisplayToCutout == null) {
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O_MR1) {
- // 9.0系統(tǒng)全屏界面默認(rèn)會保留黑邊,不允許顯示內(nèi)容到劉海區(qū)域
- return sAllowDisplayToCutoutDevice = false;
- }
- Context context = App.get();
- if (hasCutout_Huawei(context)) {
- return sAllowDisplayToCutout = true;
- }
- if (hasCutout_OPPO(context)) {
- return sAllowDisplayToCutout = true;
- }
- if (hasCutout_VIVO(context)) {
- return sAllowDisplayToCutout = true;
- }
- if (hasCutout_XIAOMI(context)) {
- return sAllowDisplayToCutout = true;
- }
- return sAllowDisplayToCutout = false;
- } else {
- return sAllowDisplayToCutout;
- }
- }
- /**
- * 是否是華為劉海屏機(jī)型
- */
- @SuppressWarnings("unchecked")
- private static boolean hasCutout_Huawei(Context context) {
- if (!Build.MANUFACTURER.equalsIgnoreCase("HUAWEI")) {
- return false;
- }
- try {
- ClassLoader cl = context.getClassLoader();
- Class HwNotchSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");
- if (HwNotchSizeUtil != null) {
- Method get = HwNotchSizeUtil.getMethod("hasNotchInScreen");
- return (boolean) get.invoke(HwNotchSizeUtil);
- }
- return false;
- } catch (Exception e) {
- return false;
- }
- }
- /**
- * 是否是oppo劉海屏機(jī)型
- */
- @SuppressWarnings("unchecked")
- private static boolean hasCutout_OPPO(Context context) {
- if (!Build.MANUFACTURER.equalsIgnoreCase("oppo")) {
- return false;
- }
- return context.getPackageManager().hasSystemFeature("com.oppo.feature.screen.heteromorphism");
- }
- /**
- * 是否是vivo劉海屏機(jī)型
- */
- @SuppressWarnings("unchecked")
- private static boolean hasCutout_VIVO(Context context) {
- if (!Build.MANUFACTURER.equalsIgnoreCase("vivo")) {
- return false;
- }
- try {
- ClassLoader cl = context.getClassLoader();
- @SuppressLint("PrivateApi")
- Class ftFeatureUtil = cl.loadClass("android.util.FtFeature");
- if (ftFeatureUtil != null) {
- Method get = ftFeatureUtil.getMethod("isFeatureSupport", int.class);
- return (boolean) get.invoke(ftFeatureUtil, 0x00000020);
- }
- return false;
- } catch (Exception e) {
- return false;
- }
- }
- /**
- * 是否是小米劉海屏機(jī)型
- */
- @SuppressWarnings("unchecked")
- private static boolean hasCutout_XIAOMI(Context context) {
- if (!Build.MANUFACTURER.equalsIgnoreCase("xiaomi")) {
- return false;
- }
- try {
- ClassLoader cl = context.getClassLoader();
- @SuppressLint("PrivateApi")
- Class SystemProperties = cl.loadClass("android.os.SystemProperties");
- Class[] paramTypes = new Class[2];
- paramTypes[0] = String.class;
- paramTypes[1] = int.class;
- Method getInt = SystemProperties.getMethod("getInt", paramTypes);
- //參數(shù)
- Object[] params = new Object[2];
- params[0] = "ro.miui.notch";
- params[1] = 0;
- return (Integer) getInt.invoke(SystemProperties, params) == 1;
- } catch (Exception e) {
- return false;
- }
- }
- }
上面提到,不一定要通過全屏界面中的視圖元素適當(dāng)下移方式來適配劉海屏,如果產(chǎn)品形態(tài)允許的話,也可以讓該界面顯示狀態(tài)欄。
顯示狀態(tài)欄的方案是較為通用簡單的,或者說,在一個應(yīng)用中,一些全屏界面往往是允許使用顯示狀態(tài)欄的方案來適配的,如果你考慮使用這種方案,那便會是這種效果:
在你的應(yīng)用中,你期望某些全屏界面在劉海屏機(jī)型上必須全屏展示,那你就自行將界面元素適當(dāng)下移,從而避免被劉海遮擋;而某些全屏界面不是非要全屏顯示,允許在劉海屏機(jī)型顯示狀態(tài)欄,那就通過顯示狀態(tài)欄的方式,從而避免被劉海遮擋。
為了實現(xiàn)這種效果,我們需要標(biāo)記區(qū)分哪些界面必須全屏展示、哪些界面允許顯示狀態(tài)欄。這里提供一種實現(xiàn)方式,讓允許顯示狀態(tài)欄的界面 Activity 繼承一個接口,比如:
- public interface CutoutAdapt {
- }
然后在 ActivityLifecycleCallbacks 回調(diào),統(tǒng)一適配允許通過顯示狀態(tài)欄的全屏界面:
- @Override
- public void onActivityStarted(Activity activity) {
- // 如果是允許全屏顯示到劉海屏區(qū)域的劉海屏機(jī)型
- if (CutoutUtil.allowDisplayToCutout()) {
- if (isFullScreen(activity)) {
- // 如果允許通過顯示狀態(tài)欄方式適配劉海屏
- if (activity instanceof CutoutAdapt) {
- // 顯示狀態(tài)欄
- StatusBarUtil.showStatusbar(activity.getWindow());
- } else {
- // 需自行將該界面視圖元素下移,否則可能會被劉海遮擋
- }
- } else {
- // 非全屏界面無需適配劉海屏
- }
- }
- }
【編輯推薦】