详解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 不仅可以判断是否为凹口屏,同时可以获取各个凹口大小及所在位置。

步骤如下: 升级 build.gradlecompileSdkVersiontargetSdkVersion28 ; 在 ApplicationActivity 中设置 meta-data 属性,小菜测试不设置亦可;

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

根据如下方法获取相应参数;

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;
      }
    });
}

注意事项: 小菜在设置 ApplicationActivity 的主题为 NoActionBar 样式,此时要去掉 getSupportActionBar().hide(); 否则会报空指针异常;

<style name="NoBarTheme" parent="Theme.AppCompat.NoActionBar">
 <item name="android:windowNoTitle">true</item>
 <item name="android:windowContentOverlay">@null</item>
</style>

如下设置全屏使用凹口屏时要注意 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);

设置主题 NoActionBar 或代码中动态设置 getSupportActionBar().hide(); 展示效果在 Android 9.0 以下有部分差异,如下:

NoActionBar 主题

AppTheme 主题

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

(0)

相关推荐

  • 详解Android 华为凹口屏适配小结

    Android8.0以后[凹口屏]得到迅速发展,目前已有了挖孔屏/水滴屏/刘海屏等各式各样的屏幕,究其根本依旧是[凹口屏],单华为一个品牌就涵盖了基本所有类型,而对于屏幕适配也是不可逃避的问题.小菜单独对华为各型号屏幕进行适配尝试,部分方法可通用到其他品牌设备,为 Android 标准 SDK 方法. 其实凹口屏已经出现很久了,对于获取凹口宽高的方式也有很多种,但是以前主流的凹口屏中凹口位置一般是位于屏幕正上方,但随着发展,也出现了在左上角的挖孔屏样式.相应的, Android 9.0 即 SD

  • 详解Android项目多服务端接口适配(超简单)

    现状 Android项目如果是多服务端接口时,一般怎么弄呢? 方法1:服务器地址放在Header中 把服务器地址放在接口Header中,然后通过拦截器来动态修改请求地址而实现的.除了默认服务器的接口,其它都要加一个Header,有点麻烦.看起来也不爽,不简洁. interface ApiHeaderCase { /************************** server A ****************************/ @Headers("host:$SERVER_HOS

  • 详解Android中PopupWindow在7.0后适配的解决

    本文介绍了详解Android中PopupWindow在7.0后适配的解决,分享给大家,具体如下: 这里主要记录一次踩坑的经历. 需求:如上图左侧效果,想在按钮的下方弹一个PopupWindow.嗯,很简单一个效果,然当适配7.0后发现这个PopupWindow显示异常,然后网上找到了下面这种方案. 7.0适配方案(但7.1又复现了) // 将popupWindow显示在anchor下方 public void showAsDropDown(PopupWindow popupWindow, Vie

  • 详解Android Automotive车载应用对驾驶模式Safe Drive Mode的适配

    前言 最近在Android Automotive 上遇到的一些问题,有好几个都跟Android 车载操作系统上应用的驾驶模式有关,国内这方面的资料很少,自己在这里总结一下相关的知识,主要包含下面几个方面: Android Automotive 和 Android Auto的区别 Android Automotive 的驾驶模式介绍 Android Automotive 实现驾驶模式的几种实现方式和代码示例,以及实现效果 主要是还是想总结一下Android 车载应用对Automotive 驾驶模式

  • 详解Android GLide图片加载常用几种方法

    目录 缓存浅析 GLide图片加载方法 图片加载周期 图片格式(Bitmap,Gif) 缓存 集成网络框架 权限 占位符 淡入效果 变换 启动页/广告页 banner 固定宽高 圆角 圆形 总结 缓存浅析 为啥要做缓存? android默认给每个应用只分配16M的内存,所以如果加载过多的图片,为了 防止内存溢出 ,应该将图片缓存起来. 图片的三级缓存分别是: 1.内存缓存 2.本地缓存 3.网络缓存 其中,内存缓存应优先加载,它速度最快:本地缓存次优先加载,它速度也快:网络缓存不应该优先加载,它

  • 详解Android.activity销毁流程的工作原理

    继续我们的源码解析,上一篇文章我们介绍了Activity的启动流程,一个典型的场景就是Activity a 启动了一个Activity b,他们的生命周期回调方法是: onPause(a) –> onCreate(b) –> onStart(b) –> onResume(b) –> onStop(a) 而我们根据源码也验证了这样的生命周期调用序列,那么Activity的销毁流程呢?它的生命周期的调用顺序又是这样的呢? 这里我们我做一个简单的demo,让一个Activity a启动A

  • 详解Android studio 3+版本apk安装失败问题

    studio2.3升级到3.1之后将apk发给别人下载到手机上安装,华为提示安装包无效或与操作系统不兼容,魅族提示apk仅为测试版,要求下载正式版安装. 在网上找了一下,发现是studio3.0之后的instant run功能引起的,直接点击绿色箭头按钮烧出来的apk都是不完整的,也就是魅族指的测试版,并且这个apk的路径在app\build\intermediates\instant-run-apk\debug下,而原来的app\build\outputs\apk\debug路径下已经没有ap

  • 详解Android封装一个全局的BaseActivity

    1.前言 对于一个Android开发者来说,每一个页面都继承一个单独的系统Activity,有时候会带来很多不必要的困扰.比如:每一个页面会有重复的代码,阅读起来麻烦:每一次写新的页面功能总要打开原来的页面代码拷贝一部分过来:有时候代码调试排查问题也不方便等等. 如果你的项目里面没有将Activity都继承自一个自己封装的BaseActivity.或者针对自己封装的BaseActivity觉得还不够完善的,这篇博客可能会对你有帮助! 2.特点 封装:将所有Activity都用到的一部分代码封装到

  • 详解android是如何管理内存的

    目录 前言 Java Heap 进程内存分配 内存不足管理 GC 垃圾回收 内核交换守护进程 低内存终止守护进程 最后 前言 很高兴遇见你~ 内存优化一直是 Android 开发中的一个非常重要的话题,他直接影响着我们 app 的性能表现.但这个话题涉及到的内容很广且都偏向底层,让很多开发者望而却步.同时,内存优化更加偏向于"经验知识",需要在实际项目中去应用来学习. 因而本文并不想深入到底层去讲内存优化的原理,而是着眼于宏观,聊聊 android 是如何分配和管理内存.在内存不足的时

  • 详解android adb常见用法

    ADB,即 Android Debug Bridge,是 Android 开发/测试人员不可替代的强大工具. adb与应用的连接 1.启动/停止 启动 adb server 命令: adb start-server (一般无需手动执行此命令,在运行 adb 命令时若发现 adb server 没有启动会自动调起.) 停止 adb server 命令: adb kill-server 2.查看 adb 版本 命令: adb version 输出为: C:\WINDOWS\system32>adb

随机推荐