Android 华为凹口屏适配小结

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 以下有部分差异,如下:

NoBarTheme 主题样式⬆️

AppTheme 主题样式⬆️


对于凹口屏适配还有很多机型要单独处理,以上仅对华为设备进行参考;如果有不对的地方还希望多多指出。

本文分享自微信公众号 - 阿策小和尚(gh_8297e718c166)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-02-19

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Pou光明

qt listView识别U盘过滤文件

心得:昨天晚上又温习了互斥量(锁),又看了一遍临界区、不变量和谓词,结合三个程序员舀水的故事,那个水桶就是互斥量,而舀水的动作则是临界区。

10030
来自专栏GA小站

用户的唯一性识别

不同的设备终端数识别的人方式不同,基本原则都是通过尽量通过各种唯一的ID去作为人的唯一标识,具体如下表:

55830
来自专栏Pou光明

Qt ListView 配合Model 显示文件与删除文件

表格、列表和树型窗口部件是 GUI 开发中经常会用到的窗口部件。这些窗口部件有两种不同的方式来获取数据。传统的方式是窗口部件本身包含用于存储数据的内置容器。这种...

31450
来自专栏Eureka伽罗的技术时光轴

Android JNI和NDK学习(03)--动态方式实现JNI

前面总结了静态实现JNI的方法,本文介绍如何动态实现JNI:JNI在加载时,会调用JNI_OnLoad,而卸载时会调用JNI_UnLoad,所以我们可以通过在J...

9820
来自专栏终身开发者

一文看懂ConstraintLayout的用法

ConstraintLayout 相对于 RelativeLayout来说性能更好,布局上也更加灵活。在最新的Google Android开发文档中是推荐使用 ...

8630
来自专栏终身开发者

如何使用WorkManager执行后台任务(上)

WorkManager 是 Android Jetpack中的一部分,它主要是封装了 Android 后台任务的调度逻辑。在前文《Android后台任务处理指南...

15920
来自专栏AI科技评论

如何又快又好地搜索代码?Facebook 提出基于机器学习的新工具!

AI 科技评论按:日前,Facebook 提出了新型代码搜索工具——神经代码搜索(NCS),能够基于机器学习直接使用自然语言处理(NLP)和信息检索(IR)技术...

13120
来自专栏Eureka伽罗的技术时光轴

Android-Could not download kotlin-reflect.jar

Could not download kotlin-reflect.jar (org.jetbrains.kotlin:kotlin-reflect:1.1.3...

33330
来自专栏程序源代码

《传奇世界》基于cocos2d开源手游

基于cocos2d游戏引擎开发lua脚本编写的经典游戏《传奇世界》手游版,支持android、ios、windows等不同平台,职业设定(战士、法师、道士), ...

87130
来自专栏GA小站

Deeplink技术是什么?

Deeplink,又叫深度链接技术,是实际中是应用非常广泛的,你一定有见过,我们来看一个例子:

93840

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励