Android劉海屏、水滴屏全面屏適配小結

這篇文章主要介紹了Android劉海屏、水滴屏全面屏適配小結,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨着小編來一起學習學習吧

現在,市面上的屏幕尺寸和全面屏方案五花八門。這裏我使用了小米的圖來說明:


上述兩種屏幕都可以統稱爲劉海屏,不過對於右側較小的劉海,業界一般稱爲水滴屏或美人尖。爲便於說明,後文提到的「劉海屏」「劉海區」都同時指代上圖兩種屏幕。

劉海屏、水滴屏全面屏適配細節

當我們在談屏幕適配時,我們具體談什麼呢?

  • 適應更長的屏幕
  • 防止內容被劉海遮擋

其中第一點是所有應用都需要適配的,對應下文的聲明最大長寬比,而第二點,如果應用本身不需要全屏顯示或使用沉浸式狀態欄,是不需要適配的。

針對需要適配第二點的應用,需要獲取劉海的位置和寬高,然後將顯示內容避開即可。

聲明最大長寬比

以前的普通屏長寬比爲16:9,全面屏手機的屏幕長寬比增大了很多,如果不適配的話就會類似下面這樣:

適配方式

適配方式有兩種:

  1. 將targetSdkVersion版本設置到API 24及以上;

這個操作將會爲<application> 標籤隱式添加一個屬性,android:resizeableActivity="true", 該屬性的作用後面將詳細說明。

在 <application> 標籤中增加屬性:android:resizeableActivity="false",同時在節點下增加一個meta-data標籤:

 <!-- 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)或更高版本的應用,android:resizeableActivity屬性默認爲true(對應適配方式1)。這個屬性是控制多窗口顯示的,決定當前的應用或者Activity是否支持多窗口。

可以在清單的<activity><application>節點中設置該屬性,啓用或禁用多窗口顯示,配置如下:

android:resizeableActivity=["true" | "false"]

如果該屬性設置爲 true,Activity 將能以分屏和自由形狀模式啓動。 如果此屬性設置爲 false,Activity 將不支持多窗口模式。 如果該值爲 false,且用戶嘗試在多窗口模式下啓動 Activity,該 Activity 將全屏顯示。

適配方式2即爲設置屏幕的最大長寬比,這是官方提供的設置方式。
如果設置了最大長寬比,必須android:resizeableActivity="false"。 否則最大長寬比沒有任何作用。

適配劉海屏

Android9.0適配

Android P(9.0)開始,官方開始提供了官方的挖孔屏適配API,具體可以參考Support display cutouts。
通過Android P提供的 DisplayCutout 類,可以確定非功能區域的位置和形狀,這些區域不應顯示內容。 要確定這些凹口屏幕區域是否存在及其位置,請使用 getDisplayCutout() 函數。

全新的窗口布局屬性 layoutInDisplayCutoutMode 讓您的應用可以爲設備凹口屏幕周圍的內容進行佈局。 您可以將此屬性設爲下列值之一:

  • 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,劉海區域不會顯示內容,需要顯示時可以將值設置爲LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES。

您可以按如下步驟在任何運行 Android P 的設備或模擬器上模擬屏幕缺口:

  • 啓用開發者選項;
  • 在 Developer options 屏幕中,向下滾動至 Drawing 部分並選擇 Simulate a display with a cutout。

適配參考示例:

// 延伸顯示區域到劉海
 WindowManager.LayoutParams lp = window.getAttributes();
 lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
 window.setAttributes(lp);
 // 設置頁面全屏顯示
 final View decorView = window.getDecorView();
 decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);

其中延伸顯示區域到劉海的代碼,也可以通過修改Activity或應用的style實現,例如:

 <?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版本)設備上,各家廠商有自己的實現方案。

華爲Android O適配

方案一:

具體方式如下所示:

<meta-data android:name="android.notch_support" android:value="true"/>

對Application生效,意味着該應用的所有頁面,系統都不會做豎屏場景的特殊下移或者是橫屏場景的右移特殊處理。例如:

<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生效,意味着可以針對單個頁面進行劉海屏適配,設置了該屬性的Activity系統將不會做特殊處理。例如:

<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生效,意味着該應用的所有頁面,系統都不會做豎屏場景的特殊下移或者是橫屏場景的右移特殊處理。

1,設置應用窗口在華爲劉海屏手機使用劉海區。

/*劉海屏全屏顯示FLAG*/
 public static final int FLAG_NOTCH_SUPPORT=0x00010000;
 /**
 * 設置應用窗口在華爲劉海屏手機使用劉海區
 * @param window 應用頁面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,恢復應用不使用劉海區顯示。

 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;
 }

設置顯示到劉海區域

@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;
 }

獲取劉海的左上角和右下角的座標

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;
 }

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持神馬文庫。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章