Android華為凹口屏適配小結

阿策~發表於2019-02-19

      Android8.0 以後【凹口屏】得到迅速發展,目前已有了挖孔屏/水滴屏/劉海屏等各式各樣的螢幕,究其根本依舊是【凹口屏】,單華為一個品牌就涵蓋了基本所有型別,而對於螢幕適配也是不可逃避的問題。小選單獨對華為各型號螢幕進行適配嘗試,部分方法可通用到其他品牌裝置,為 Android 標準 SDK 方法。

      其實凹口屏已經出現很久了,對於獲取凹口寬高的方式也有很多種,但是以前主流的凹口屏中凹口位置一般是位於螢幕正上方,但隨著發展,也出現了在左上角的挖孔屏樣式。相應的,Android 9.0SDK28 也釋出了獲取凹口屏的方法。

Android 9.0 以下適配方案

      對華為裝置凹口屏適配情況來說,若僅需獲取凹口位置的寬高,如下方法即可,在 Android 各版本中均可(Android 9.0 及以上亦可)。此時獲取螢幕水平方向安全位置時,可根據螢幕寬度-凹口寬度再左右均分即可。

/**
 * 華為凹口屏判斷方法 Android 各版本均可
 * @param context
 * @return
 */
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(TAG, "hasNotchInScreen ClassNotFoundException");
    } catch (NoSuchMethodException e) {
        Log.e(TAG, "hasNotchInScreen NoSuchMethodException");
    } catch (Exception e) {
        Log.e(TAG, "hasNotchInScreen Exception");
    } finally {
        return ret;
    }
}

/**
 * 華為凹口屏寬高獲取方式 int[]{width, height}
 * @param context
 * @return
 */
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(TAG, "getNotchSize ClassNotFoundException");
    } catch (NoSuchMethodException e) {
        Log.e(TAG, "getNotchSize NoSuchMethodException");
    } catch (Exception e) {
        Log.e(TAG, "getNotchSize Exception");
    } finally {
        notchWidth = ret[0];
        notchHeight = ret[1];
        return ret;
    }
}

Android 9.0 及以上適配

      對於華為新出的挖孔屏裝置基本均為 Android 9.0 及以上,Android 9.0 提供了對凹口屏相關的 SDK,谷歌認為凹口位置可以不固定位置也不固定個數,但是對於裝置一條邊只能有一個;如下方法對於 Android 9.0 及以上裝置判斷均可。SDK 不僅可以判斷是否為凹口屏,同時可以獲取各個凹口大小及所在位置。

步驟如下:
  1. 升級 build.gradlecompileSdkVersiontargetSdkVersion28
  2. ApplicationActivity 中設定 meta-data 屬性,小菜測試不設定亦可;
<meta-data android:name="android.notch_support" android:value="true"/>
  1. 根據如下方法獲取相應引數;
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    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);
    getWindow().getDecorView()
        .findViewById(android.R.id.content)
        .getRootView()
        .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判斷是否凹口手機
                    isNotchScreen = false;
                } else {
                    List<Rect> rects = cutout.getBoundingRects();
                    if (rects == null || rects.size() == 0) {
                        Log.e(TAG, "rects==null || rects.size()==0, is not notch screen");
                        isNotchScreen = true;
                    } else {
                        Log.e(TAG, "rect size:" + rects.size());//注意:凹口的數量可以是多個
                        isNotchScreen = true;
                        for (Rect rect : rects) {
                            notchRight = rect.right;
                            notchLeft = rect.left;
                            notchTop = rect.top;
                            notchBottom = rect.bottom;
                            notchWidth = notchRight - notchLeft;
                            notchHeight = notchBottom - notchLeft;
                            safeLeft = cutout.getSafeInsetLeft();
                            safeRight = cutout.getSafeInsetRight();
                            safeTop = cutout.getSafeInsetTop();
                            safeBottom = cutout.getSafeInsetBottom();
                        }
                    }
                }
                return windowInsets;
            }
        });
}

注意事項:

  1. 小菜在設定 ApplicationActivity 的主題為 NoActionBar 樣式,此時要去掉 getSupportActionBar().hide(); 否則會報空指標異常;
<style name="NoBarTheme" parent="Theme.AppCompat.NoActionBar">
  <item name="android:windowNoTitle">true</item>
  <item name="android:windowContentOverlay">@null</item>
</style>
  1. 如下設定全屏使用凹口屏時要注意 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN,否則引數很有可能獲取不到;
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);
  1. 設定主題 NoActionBar 或程式碼中動態設定 getSupportActionBar().hide(); 展示效果在 Android 9.0 以下有部分差異,如下:

NoActionBar 主題

AppTheme 主題


      對於凹口屏適配還有很多機型要單獨處理,以上僅對華為裝置進行參考;如果有不對的地方還希望多多指出。


相關文章