天天看點

android相容huawei手機劉海屏解決方案

引用自華為官方文檔:doc/50114 ,這裡縮減了一些内容,撿取重要内容。

轉載請标明出處:

https://blog.csdn.net/djy1992/article/details/80683575 本文出自:【奧特曼超人的部落格】

推薦:

目錄:

劉海屏指的是手機螢幕正上方由于追求極緻邊框而采用的一種手機解決方案。因形似劉海兒而得名。也有一些其他叫法:挖孔屏、凹口屏等,本文檔統一按照劉海屏來命名。市場上已經有越來越多的手機都支援這種螢幕形式。

谷歌在安卓P版本中已經提供了統一的适配方案,可是在安卓O版本上如何适配呢?本文将詳細介紹華為安卓O版本劉海屏适配方案。使用華為提供的劉海屏SDK進行适配,此方案也會繼承到華為安卓P版本手機上。在華為P版本手機中将同時支援兩種方案:華為O版本方案+谷歌P版本方案。另外因為安卓O版本的劉海屏手機已經在市場上大量上市,這些手機在市場上會存續2~3年。是以建議大家現在要同時适配華為O版本方案以及谷歌P版本方案。

設計理念:盡量減少APP的開發工作量

處理邏輯:

4.1 判斷是否劉海屏

4.1.1 接口描述

是否是劉海屏手機:

true:是劉海屏 false:非劉海屏

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

4.2 擷取劉海尺寸

4.2.1 接口描述

4.2.2 調用範例

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.3 應用頁面設定使用劉海區顯示

4.3.1 方案一

使用新增的Meta-data屬性android.notch_support,在應用的

AndroidManifest.xml

中增加meta-data屬性,此屬性不僅可以針對Application生效,也可以對Activity配置生效。

具體方式如下所示:

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

4.3.2 方案二

使用給window添加新增的

FLAG_NOTCH_SUPPORT

(1)接口1描述:應用通過增加華為自定義的劉海屏flag,請求使用劉海區顯示

調用範例參考:

/*劉海屏全屏顯示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)接口2描述:可以通過clearHwFlags接口清除添加的華為劉海屏Flag,恢複應用不使用劉海區顯示。

/*劉海屏全屏顯示FLAG*/
public static final int FLAG_NOTCH_SUPPORT=0x00010000;
/**
 * 設定應用視窗在華為劉海屏手機使用劉海區
 * @param window 應用頁面window對象
 */
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");
    }
}           

華為劉海屏flag動态添加和删除代碼:

btn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        if(isAdd) {//add flag
            isAdd = false;
            NotchSizeUtil.setFullScreenWindowLayoutInDisplayCutout(getWindow());           
            getWindowManager().updateViewLayout(getWindow().getDecorView(),getWindow().getDecorView().getLayoutParams());
        } else{//clear flag
            isAdd = true;
            NotchSizeUtil.setNotFullScreenWindowLayoutInDisplayCutout(getWindow());            
            getWindowManager().updateViewLayout(getWindow().getDecorView(),getWindow().getDecorView().getLayoutParams());
        }
    }
});           

使用和不使用劉海區效果對比:

4.4 擷取預設和隐藏劉海區開關值接口

4.4.1 開關頁面介紹

(1)老版本路徑:系統設定→顯示→顯示區域控制

(2)新版本路徑:系統設定→顯示→螢幕頂部顯示

4.4.2 隐藏開關打開之後,顯示規格

4.4.3 讀取開關狀态調用範例

public static final String DISPLAY_NOTCH_STATUS = “display_notch_status”;

int mIsNotchSwitchOpen = Settings.Secure.getInt(getContentResolver(),DISPLAY_NOTCH_STATUS, 0);

// 0表示“預設”,1表示“隐藏顯示區域”

5.1 特性介紹

谷歌稱劉海屏為凹口屏以及螢幕缺口支援, 内容摘自:

https://developer.android.com/preview/features#cutout 《android 相容所有劉海屏的方案大全》

5.2 接口介紹

(1)擷取劉海尺寸相關接口:

https://developer.android.com/reference/android/view/DisplayCutout

所屬類 方法 接口說明

  • android.view.DisplayCutout List getBoundingRects() 傳回Rects的清單,每個Rects都是顯示屏上非功能區域的邊界矩形。裝置的每個短邊最多隻有一個非功能區域,而長邊上則沒有
  • android.view.DisplayCutout int getSafeInsetBottom() 傳回安全區域距離螢幕底部的距離,機關是px。
  • android.view.DisplayCutout int getSafeInsetLeft() 傳回安全區域距離螢幕左邊的距離,機關是px。
  • android.view.DisplayCutout int getSafeInsetRight() 傳回安全區域距離螢幕右邊的距離,機關是px。
  • android.view.DisplayCutout int getSafeInsetTop() 傳回安全區域距離螢幕頂部的距離,機關是px。

(2)設定是否延伸到劉海區顯示接口:

https://developer.android.com/reference/android/view/WindowManager.LayoutParams#layoutInDisplayCutoutMode https://developer.android.com/reference/android/view/WindowManager.LayoutParams.html#LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES

5.3 參考實作代碼

(1)設定使用劉海區顯示代碼:

getSupportActionBar().hide();
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); 
//設定頁面全屏顯示
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.layoutInDisplayCutoutMode = 
       windowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; 
//設定頁面延伸到劉海區顯示
getWindow().setAttributes(lp);
注意:如果需要應用的布局延伸到劉海區顯示,需要設定SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN。           

屬性:

(1)擷取劉海屏安全顯示區域和劉海尺寸資訊:

contentView = getWindow().getDecorView().findViewById(android.R.id.content).getRootView();
contentView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
    @Override
    public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
        DisplayCutout cutout = windowInsets.getDisplayCutout();
        if (cutout == null) {
            Log.e(TAG, "cutout==null, is not notch screen");//通過cutout是否為null判斷是否劉海屏手機
        } else {
            List<Rect> rects = cutout.getBoundingRects();
            if (rects == null || rects.size() == 0) {
                Log.e(TAG, "rects==null || rects.size()==0, is not notch screen");
            } else {
                Log.e(TAG, "rect size:" + rects.size());//注意:劉海的數量可以是多個
                for (Rect rect : rects) {
                    Log.e(TAG, "cutout.getSafeInsetTop():" + cutout.getSafeInsetTop()
                            + ", cutout.getSafeInsetBottom():" + cutout.getSafeInsetBottom()
                            + ", cutout.getSafeInsetLeft():" + cutout.getSafeInsetLeft()
                            + ", cutout.getSafeInsetRight():" + cutout.getSafeInsetRight()
                            + ", cutout.rects:" + rect
                    );
                }
            }
        }
        return windowInsets;
    }
});           

(3)說明:

通過windowInsets.getDisplayCutout()是否為null判斷是否劉海屏手機,如果為null為非劉海屏:

Line 6203: 05-24 11:16:46.766 11036 11036 E Cutout_test: cutout==null, is not notch screen           

如果是劉海屏手機可以通過接口擷取劉海資訊:

Line 6211: 05-24 11:11:16.839 10733 10733 E Cutout_test: cutout.getSafeInsetTop():126, cutout.getSafeInsetBottom():0, 
cutout.getSafeInsetLeft():0, cutout.getSafeInsetRight():0, cutout.rects:Rect(414, 0 - 666, 126)           

劉海個數可以是多個:

Line 6291: 05-24 11:27:04.517 11036 11036 E Cutout_test: rect size:2
Line 6292: 05-24 11:27:04.517 11036 11036 E Cutout_test: cutout.getSafeInsetTop():84, 
cutout.getSafeInsetBottom():84, cutout.getSafeInsetLeft():0, cutout.getSafeInsetRight():0, cutout.rects:Rect(351, 0 - 729, 84)
Line 6293: 05-24 11:27:04.517 11036 11036 E Cutout_test: cutout.getSafeInsetTop():84, 
cutout.getSafeInsetBottom():84, cutout.getSafeInsetLeft():0, cutout.getSafeInsetRight():0, cutout.rects:Rect(351, 1836 - 729, 1920)           

通過上面的2種方案(meta-data或者Flag),應用在華為劉海屏手機上就能預設使用劉海區顯示了,為了避免UI被劉海區遮擋,應用還要進一步進行UI适配:

6.1 判斷是否為劉海屏

判斷是否為劉海屏?可以調用華為劉海屏API,參考4.1。

6.2 調整布局

如果是劉海屏,調整布局避開劉海區。

布局原則:保證重要的文字、圖檔、視訊資訊、可點選的控件和圖示,應用彈窗等,建議顯示在狀态欄區域以下(安全區域)。如果内容不重要或者不會遮擋,布局可以延伸到狀态欄區域(危險區域)。

建議按照如下布局原則修改:

擷取系統狀态欄高度接口:

public static int getStatusBarHeight(Context context) {
    int result = 0;
    int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
    if (resourceId > 0) {
        result = context.getResources().getDimensionPixelSize(resourceId);
    }
    return result;
}           

建議視窗顯示内容在狀态欄區域以下的另外一個原因:華為有一個自研的隐藏劉海的需求,可以參考章節4.4。

繼續閱讀