简洁明了的刘海屏适配方案

网上关于刘海屏适配的文章不少,可讲清楚的却没几篇,大多是拷贝文档、长篇大论,甚至热情的贴图告诉你什么是刘海屏,到最后你仍不确定到底是怎样的一个适配方案,才能让你的 app 真正的适配所有的刘海屏机型。

看到这篇文章你就无需再怨恨各大厂商的跟风“刘海”了,因为刘海屏的适配十分简单。

ok,废话说完了,开始适配。

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

  • 有状态栏的界面:刘海区域会显示状态栏,无需适配
  • 全屏界面:刘海区域可能遮挡内容,需要适配

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

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

如果你什么都不做,默认规则不允许全屏界面内容显示到刘海区域,即刘海屏区域会保留一条黑边,你的全屏界面会在刘海下方展示,这看起来好像也是可以接受的,然后你竟说服产品达成共识,“无为而治”才是最强大的刘海屏适配方案!

但有些手机厂商(譬如oppo)不开心了,我辛辛苦苦研发的刘海屏手机,你们这些开发者竟直接放弃刘海区域!然后就在你的全屏界面下方加了一条提示:“全屏显示”,当用户点击开启后,强行把你的全屏界面显示到刘海区域,然后一切都乱套了...

嗯~ “无为而治”行不通。

只能允许全屏界面内容显示到刘海区域了,参考各大厂商的适配文档,我们可以知道如何允许,比如华为机型只需在 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 onActivityStarted(Activity activity) {
            // 如果是允许全屏显示到刘海屏区域的刘海屏机型
            if (CutoutUtil.allowDisplayToCutout()) {
                if (isFullScreen(activity)) {
                    // 如果允许通过显示状态栏方式适配刘海屏
                    if (activity instanceof CutoutAdapt) {
                        // 显示状态栏
                        StatusBarUtil.showStatusbar(activity.getWindow());
                    } else {
                        // 需自行将该界面视图元素下移,否则可能会被刘海遮挡
                    }
                } else {
                    // 非全屏界面无需适配刘海屏
                }
            }
        }

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352

推荐阅读更多精彩内容

  • 背景 刘海屏指的是手机屏幕正上方由于追求极致边框而采用的一种手机解决方案。因形似刘海儿而得名。也有一些其他叫法:挖...
    _九卿_阅读 6,078评论 0 26
  • 0.版权声明 本文由玉刚说写作平台提供写作赞助,版权归玉刚说微信公众号所有原作者:四月葡萄版权声明:未经玉刚说许可...
    四月葡萄阅读 59,869评论 17 68
  • 一、简介 随着 Apple 发布 iPhone X 之后,各大手机厂商也开始模仿这种刘海屏的设计,而且刘海屏手机的...
    Android_Jieyao阅读 4,715评论 2 42
  • 每天傍晚我会徒步十公里, 走到尽头挨着田间的河溪放足休息, 晚霞绚烂的辉光稍纵即逝, 山谷向河溪田间吹送着扑面而来...
    MaTao时光久美阅读 381评论 0 0
  • 20180616杨燕分享:【每天进步一点点】 觉察生活中发生的人事物,世界上事没有无缘无故的发生,既然发生总有它的...
    杨燕_爱自然生命力阅读 143评论 0 0