1. 程式人生 > >Android劉海屏適配方案

Android劉海屏適配方案

隨著iPhone X釋出,國內一些廠商也推出了劉海屏手機,即將釋出的Android p也提供了對劉海屏的支援。so,我們的app也要提前做好適配。

什麼是劉海屏?

螢幕的正上方居中位置(下圖黑色區域)會被挖掉一個孔,螢幕被挖掉的區域無法正常顯示內容,這種型別的螢幕就是劉海屏,也有其他叫法:挖孔屏、凹凸屏等等,這裡統一按劉海屏命名。

 

OPPO R15.png

 

目前國內廠商已經推出的劉海屏Android手機有華為P20 pro, vivo X21,OPPO R15。

如果沒有適配劉海屏會有什麼後果?

後果一:導航欄中title被遮擋

 

未適配劉海屏1

 

後果二:顯示內容下移,頭部出現黑條,底部出現遮擋

 

未適配劉海屏2

如何適配劉海屏?

由於Android p正式版今日剛釋出, 當前市面上的Android 劉海屏手機還不能用Android 官方提供的方案來解決,那怎麼辦呢?還好幾個廠商自己給出了適配方案。我們先講理論後上程式碼,如果您只想要程式碼就直接往下翻:

華為P20 pro

華為劉海屏適配官方文件
華為給出的文件最為詳細(業界良心),P20 pro預裝系統對未做劉海屏適配處理的app有一定處理,處理邏輯如下

預處理流程圖


可見,會被華為系統做偏移處理的有2種情況:
1.未設定meta-data值,頁面橫屏狀態
2.未設定meta-data值,頁面豎屏狀態,不顯示狀態列
這2種情況都會出現後果二。如果你的app中頁面沒有這兩種情況,例如都是豎屏且顯示狀態列,你就可以淡定地不做處理。
現在我們知道原因了就可以對症下藥了,這裡給出我推薦的解決方案(官方給出的解決方案不止一種,可以根據自己的需要採用) 分為4步:
1.配置meta-data


<meta-data android:name="android.notch_support" android:value="true"/>
①對Application生效,意味著該應用的所有頁面,系統都不會做豎屏場景的特殊下移或者是橫屏場景的右移特殊處理:

配置在application下

 

② 對Activity生效,意味著可以針對單個頁面進行劉海屏適配,設定了該屬性的Activity系統將不會做特殊處理:

 

配置在Activity下

2.檢測是否存在劉海屏

 
  1. public static boolean hasNotchInScreen(Context context) {

  2. boolean ret = false;

  3. try {

  4. ClassLoader cl = context.getClassLoader();

  5. Class HwNotchSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");

  6. Method get = HwNotchSizeUtil.getMethod("hasNotchInScreen");

  7. ret = (boolean) get.invoke(HwNotchSizeUtil);

  8. } catch (ClassNotFoundException e) {

  9. Log.e("test", "hasNotchInScreen ClassNotFoundException");

  10. } catch (NoSuchMethodException e) {

  11. Log.e("test", "hasNotchInScreen NoSuchMethodException");

  12. } catch (Exception e) {

  13. Log.e("test", "hasNotchInScreen Exception");

  14. } finally {

  15. return ret;

  16. }

  17. }

3.獲取劉海屏的引數

 
  1. public static int[] getNotchSize(Context context) {

  2. int[] ret = new int[]{0, 0};

  3. try {

  4. ClassLoader cl = context.getClassLoader();

  5. Class HwNotchSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");

  6. Method get = HwNotchSizeUtil.getMethod("getNotchSize");

  7. ret = (int[]) get.invoke(HwNotchSizeUtil);

  8. } catch (ClassNotFoundException e) {

  9. Log.e("test", "getNotchSize ClassNotFoundException");

  10. } catch (NoSuchMethodException e) {

  11. Log.e("test", "getNotchSize NoSuchMethodException");

  12. } catch (Exception e) {

  13. Log.e("test", "getNotchSize Exception");

  14. } finally {

  15. return ret;

  16. }

  17. }

4. UI適配
沒錯,第1步僅僅是告訴EMUI系統不要瞎操作你的頁面,真正適配的活還要你自己幹。
①判斷是否劉海屏,程式碼上面給出了
②如果是劉海屏手機需要應用自己調整佈局避開劉海區,佈局原則:保證重要的文字、圖片和視訊資訊、可點選的控制元件和圖示還有應用彈窗等等佈局建議顯示在狀態列區域以下(安全區域);不重要,遮擋不會出現問題的佈局可以延伸到狀態列區域(危險區域)顯示,按照這種佈局原則修改,可以一次修改就能適配所有的劉海屏手機:

UI適配

 

vivo & OPPO

OPPO劉海屏適配官方文件
vivo劉海屏適配官方文件

vivo 和 OPPO官網僅僅給出了適配指導,沒有給出具體方案,簡單總結為:
如有是具有劉海屏的手機,豎屏顯示狀態列,橫屏不要在危險區顯示重要資訊或者設定點選事件。
那怎麼知道是不是劉海屏手機呢?
OPPO判斷方法:

 
  1. public static boolean hasNotchInOppo(Context context){

  2. return context.getPackageManager().hasSystemFeature("com.oppo.feature.screen.heteromorphism");

  3. }

vivo的判斷方法:

 
  1. public static final int NOTCH_IN_SCREEN_VOIO=0x00000020;//是否有凹槽

  2. public static final int ROUNDED_IN_SCREEN_VOIO=0x00000008;//是否有圓角

  3. public static boolean hasNotchInScreenAtVoio(Context context){

  4. boolean ret = false;

  5. try {

  6. ClassLoader cl = context.getClassLoader();

  7. Class FtFeature = cl.loadClass("com.util.FtFeature");

  8. Method get = FtFeature.getMethod("isFeatureSupport",int.class);

  9. ret = (boolean) get.invoke(FtFeature,NOTCH_IN_SCREEN_VOIO);

  10.  
  11. } catch (ClassNotFoundException e)

  12. { Log.e("test", "hasNotchInScreen ClassNotFoundException"); }

  13. catch (NoSuchMethodException e)

  14. { Log.e("test", "hasNotchInScreen NoSuchMethodException"); }

  15. catch (Exception e)

  16. { Log.e("test", "hasNotchInScreen Exception"); }

  17. finally

  18. { return ret; }

  19. }

例如圖一是在OPPO R15上出現的title被遮擋,顯示狀態列後顯示效果如下:

 

顯示狀態列後

以上所講的是各廠商提供的解決方案, 那麼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)的位置和形狀,呼叫getDisplayCutout()這個方法可以獲取劉海(cutout)的位置和區域,我們看看這個類提供了什麼方法:

DisplayCutout提供的方法.png


所以我們可用這個類判斷是否有劉海的存在以及劉海的位置

 

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 : 僅僅當系統提供的bar完全包含了劉海區時才允許window擴充套件到劉海區,否則window不會和劉海區重疊
    第二種模式:
    LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES :允許window擴充套件到劉海區(原文說的是短邊的劉海區, 目前有劉海的手機都在短邊,所以就不糾結了)
    第三種模式:
    LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER: 不允許window擴充套件到劉海區

我們可以設定是否允許window擴充套件到劉海區

 
  1. WindowManager.LayoutParams lp =getWindow().getAttributes();

  2. lp.layoutInDisplayCutoutMode

  3. =WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;

  4. getWindow().setAttributes(lp);

例如一個有狀態列的頁面, 我們可以這樣適配:

 
  1. DisplayCutout cutout = getDisplayCutout();

  2. if(cutout != null){

  3. WindowManager.LayoutParams lp =getWindow().getAttributes();

  4. lp.layoutInDisplayCutoutMode=WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;

  5. getWindow().setAttributes(lp);

  6. }

如果覺得有幫助, 點個贊再走可好啊~



作者:your_genius
連結:https://www.jianshu.com/p/62c6625db7ab
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。