美文网首页
刘海屏适配方案

刘海屏适配方案

作者: 竹竹竹子 | 来源:发表于2020-07-06 14:51 被阅读0次

    首先要清楚的是哪些界面需要适配刘海屏:

    「有状态栏的界面」:刘海区域会显示状态栏,无需适配

    「全屏界面」:刘海区域可能遮挡内容,需要适配

    如果你的应用里所有界面都有状态栏,那么恭喜你,你不用做任何操作,状态栏就那么自然的显示在刘海区域,毫无违和,刘海屏已适配完毕,可以拉到下面点个赞出去了。

    不幸的是,你的应用中很大几率会有全屏界面,所谓的刘海屏适配,也正是针对这些全屏界面。

    参考各大厂商的适配文档,我们可以知道如何允许,比如华为机型只需在 AndroidManifest 中配置:

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

    配置后,华为机型上的全屏界面就会显示到刘海区域了,但这个刘海,是可能挡住我们全屏界面中的内容的。这时需要将全屏界面中的视图元素适当下移,保证不会被刘海遮挡住,就 ok 了。

    这里我们搞清楚: 「允许全屏界面内容显示到刘海区域的机型,才需要将全屏界面中的视图元素适当下移」

    比如若只允许华为机型全屏界面内容显示到刘海区域,那只有华为的刘海屏机型才需要将全屏界面中的视图元素适当下移,其他厂商的刘海屏机型则不需要下移。

    如果允许华为、小米、oppo、vivo 全屏界面内容显示到刘海区域,那么华为、小米、oppo、vivo 刘海屏机型需要将全屏界面中的视图元素适当下移。

    另外也不一定要通过全屏界面中的视图元素适当下移方式来适配刘海屏,如果产品形态允许的话,你也可以让该界面显示状态栏啊

    至此刘海屏适配完毕,是不是很简单!?

    代码奉上,拿走不谢:

    允许全屏界面内容显示到刘海区域配置:
      <!--允许绘制到oppo、vivo刘海屏机型的刘海区域 -->
            <meta-data
                android:name="android.max_aspect"
                android:value="2.2" />
    
            <!-- 允许绘制到华为刘海屏机型的刘海区域 -->
            <meta-data
                android:name="android.notch_support"
                android:value="true" />
    
            <!-- 允许绘制到小米刘海屏机型的刘海区域 -->
            <meta-data
                android:name="notch.config"
                android:value="portrait" />
    

    上面在 AndroidManifest 的配置在 Android 9.0 之前有效,9.0 系统针对刘海屏制定了新的 api,默认保留一条黑边,即不允许绘制到刘海区域。所以如果你还没有适配 Android 9.0,那在判断是否是允许全屏界面内容显示到刘海区域的刘海屏机型时,就要加上版本判断。

    判断是否是允许全屏界面内容显示到刘海区域的刘海屏机型:
    public class CutoutUtil {
    
        private static Boolean sAllowDisplayToCutout;
    
        /**
         * 是否为允许全屏界面显示内容到刘海区域的刘海屏机型(与AndroidManifest中配置对应)
         */
        public static boolean allowDisplayToCutout() {
            if (sAllowDisplayToCutout == null) {
                if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O_MR1) {
                    // 9.0系统全屏界面默认会保留黑边,不允许显示内容到刘海区域
                    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;
            }
        }
    
    
        /**
         * 是否是华为刘海屏机型
         */
        @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刘海屏机型
         */
        @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刘海屏机型
         */
        @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;
            }
        }
    
        /**
         * 是否是小米刘海屏机型
         */
        @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);
                //参数
                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;
            }
        }
    
    }
    

    上面提到,不一定要通过全屏界面中的视图元素适当下移方式来适配刘海屏,如果产品形态允许的话,也可以让该界面显示状态栏。

    显示状态栏的方案是较为通用简单的,或者说,在一个应用中,一些全屏界面往往是允许使用显示状态栏的方案来适配的,如果你考虑使用这种方案,那便会是这种效果:

    「在你的应用中,你期望某些全屏界面在刘海屏机型上必须全屏展示,那你就自行将界面元素适当下移,从而避免被刘海遮挡;而某些全屏界面不是非要全屏显示,允许在刘海屏机型显示状态栏,那就通过显示状态栏的方式,从而避免被刘海遮挡。」

    为了实现这种效果,我们需要标记区分哪些界面必须全屏展示、哪些界面允许显示状态栏。这里提供一种实现方式,让允许显示状态栏的界面 Activity 继承一个接口,比如:

    public interface CutoutAdapt {
    }
    

    然后在 ActivityLifecycleCallbacks 回调,统一适配允许通过显示状态栏的全屏界面:

    @Override
    public void onActivityCreate(Activity activity) {
        // 如果是允许全屏显示到刘海屏区域的刘海屏机型
        if (CutoutUtil.allowDisplayToCutout()) {
            if (isFullScreen(activity)) {
                // 如果允许通过显示状态栏方式适配刘海屏
                if (activity instanceof CutoutAdapt) {
                    // 显示状态栏
                    StatusBarUtil.showStatusbar(activity.getWindow());
                } else {
                    // 需自行将该界面视图元素下移,否则可能会被刘海遮挡
                }
            } else {
                // 非全屏界面无需适配刘海屏
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:刘海屏适配方案

          本文链接:https://www.haomeiwen.com/subject/zwwwqktx.html