挖孔屏适配小结

avatar
avatar
kktoo
45
文章
12
评论
2020年5月21日20:18:51 2 10,858 4850字阅读16分10秒

前言

关于挖孔屏的适配,网络上已经有很多完整教程写的很好,请自行搜索。 本文的重点是总结归纳在这个过程中一些容易被忽略的小疑问和小细节,希望对大家能有所帮助。
这几年手机异形屏越来越火,已经成为时尚潮流。如下图所示举例,依次为挖孔屏,水滴屏,刘海屏。

挖孔屏适配小结

一、挖孔屏适配

1 概要

挖孔无法正常显示内容,也无法响应触摸事件。因此在挖孔区域(非安全区域),要避免布局重要的控件、文本或其他内容。

挖孔屏适配小结

2 解决方案

各个手机厂商几乎都有设计自己的异形屏适配解决方案。 从android P开始,Google提供了统一的适配方案(Cutout API),推荐采用Google推荐的统一方案进行适配。

2.1 特性介绍

Google从Android P开始支持最新的全面屏以及为摄像头和扬声器预留空间的挖孔屏幕。通过全新的 DisplayCutout 类,可以确定非功能区域的位置和形状,这些区域不应显示内容。要确定这些挖孔屏幕区域是否存在及其位置,请使用getDisplayCutout()函数。 全新的窗口布局属性layoutInDisplayCutoutMode让您的应用可以为设备挖孔屏幕周围的内容进行布局。

可以将此属性设为下列值之一:
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER

2.2 相关接口

(1)获取挖孔尺寸相关接口 https://developer.android.com/reference/android/view/DisplayCutout

方法 接口说明
List getBoundingRects() 返回Rects的列表,每个Rects都是显示屏上非功能区域的边界矩形。
int getSafeInsetBottom() 返回安全区域距离屏幕底部的距离,单位是px。
int getSafeInsetLeft () 返回安全区域距离屏幕左边的距离,单位是px。
int getSafeInsetRight () 返回安全区域距离屏幕右边的距离,单位是px。
int getSafeInsetTop () 返回安全区域距离屏幕顶部的距离,单位是px。

(2)设置是否延伸到挖孔区显示接口

方法 接口说明
int layoutInDisplayCutoutMode 默认值:LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT(只有当DisplayCutout完全包含在系统状态栏中时,才允许窗口延伸到DisplayCutout区域显示。) 其他可能取值: LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES(该窗口始终允许延伸到屏幕短边上的DisplayCutout区域。) LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER(该窗口决不允许与DisplayCutout区域重叠。)

3 UI适配

3.1 判断是否为挖孔屏幕

通过获取DisplayCutout判断是否为挖孔屏幕。实际使用时,还常同时判断安卓版本。

  1. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
  2.     {
  3.         try
  4.         {
  5.             WindowInsets windowInsets = getWindow().getDecorView().getRootWindowInsets();
  6.             if (windowInsets != null)
  7.             {
  8.                 cutoutDisp = windowInsets.getDisplayCutout();
  9.             }
  10.             else
  11.             {
  12.                 Log.e(TAG_CUTOUT, "windowInsets is null");
  13.             }
  14.         }
  15.         catch (Exception e)
  16.         {
  17.             Log.e(TAG_CUTOUT, "error:"+e.toString());
  18.         }
  19.         if(cutoutDisp != null)
  20.         {
  21.             Log.i(TAG_CUTOUT,"命中挖孔屏,cutout:"+cutoutDisp.toString());
  22.         }
  23.     }
3.2 设置显示模式
  1. WindowManager.LayoutParams windowManagerDu = getWindow().getAttributes();
  2. windowManagerDu.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
  3. getWindow().setAttributes(windowManagerDu);
3.3 获取状态栏高度
  1. public static int getStatusBarHeight(Context context) {
  2.     int result = 0;
  3.     int resourceId = context.getResources().getIdentifier("status_bar_height""dimen""android");
  4.     if (resourceId > 0) {
  5.         result = context.getResources().getDimensionPixelSize(resourceId);
  6.     }
  7.     return result;
  8. }
3.4 调整布局

如果是挖孔屏幕,根据挖孔区域的参数,调整布局避开挖孔区。 布局原则:保证重要的文字、图片、视频信息、可点击的控件和图标,应用弹窗等,建议显示在状态栏区域以下(安全区域)。如果内容不重要或者不会遮挡,布局可以延伸到状态栏区域(危险区域)。

最简陋的方案是根据状态栏的高度,对边界的元素进行整体调整。

二、测试方法

写好了代码,我们需要测试、验证。如果为了测试去专门采购一部挖孔屏手机,成本太高了。其实还有两个办法:

  • 使用Android P模拟器。
  • 在非挖孔屏真机上开启模拟挖孔屏调试。

真机安卓版本需要大于等于p版本。

在开发人员选项中,向下滚动到绘图部分,然后点击“模拟刘海屏”设置项(有些手机叫做模拟具有凹口的显示屏)。如下图所示:

挖孔屏适配小结

 

三、代码总览

  1. private DisplayCutout cutoutDisp = null;
  2. @Override
  3. public void onAttachedToWindow() {
  4.   super.onAttachedToWindow();
  5.   Log.i(TAG_CUTOUT,"onAttachedToWindow");
  6.   //挖孔屏
  7.   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
  8.     try{
  9.       WindowInsets windowInsets = getWindow().getDecorView().getRootWindowInsets();
  10.       if (windowInsets != null) {
  11.         cutoutDisp = windowInsets.getDisplayCutout();
  12.       }else{
  13.         Log.i(TAG_CUTOUT, "windowInsets is null");
  14.       }
  15.     }catch (Exception e){
  16.       Log.e(TAG_CUTOUT, "error:"+e.toString());
  17.     }
  18.     if(cutoutDisp != null){
  19.       Log.i(TAG_CUTOUT,"will set mode,cutout:"+cutoutDisp.toString());
  20.       WindowManager.LayoutParams windowManagerDu = getWindow().getAttributes();
  21.       windowManagerDu.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
  22.       getWindow().setAttributes(windowManagerDu);
  23.       AppUtil.CutOutSafeHeight = getStatusBarHeight();
  24.       Log.i(TAG_CUTOUT,"statusBar height:" + AppUtil.CutOutSafeHeight);
  25.     }
  26.   }
  27. }
  28. //获取状态栏高度
  29. public int getStatusBarHeight() {
  30.   Context context = getApplicationContext();
  31.   int result = 0;
  32.   if(cutoutDisp != null){
  33.     int resourceId = context.getResources().getIdentifier("status_bar_height""dimen""android");
  34.     if (resourceId > 0) {
  35.       result = context.getResources().getDimensionPixelSize(resourceId);
  36.     }
  37.   }
  38.   return result;
  39. }

四、需要注意的几个坑

4.1 DisplayCutout未定义

有些小白,看了文档就上手写代码,却发现无法通过编译,报错undefined symbol DisplayCutout

这是因为编译所用的版本导致的。需要条件:

  • 在gradle中设置的compileSdkVersion需要大于等于p版本,可以设置compileSdkVersion 28
  • 在代码中导入DisplayCutout
import android.view.DisplayCutout;

扩展阅读:compileSdkVersion、minSdkVersion 和 targetSdkVersion的区别

4.2 在OnCreate方法中设置窗口属性无效

改为在onAttachedToWindow中设置。

不能在onCreate()中修改Window的布局参数。Window的创建以及显示View是在onResume执行后,在WindowManagerService中完成的,然后才会回调Activity的onAttachedToWindow(),此时Window已经显示了附属在其上的View。

onAttachedToWindow在activity中的生命周期为:
onCreate->onStart->onResume->onAttachedToWindow

扩展阅读:onAttachedToWindow()在整个Activity生命周期的位置及使用

4.3 获取getDisplayCutout结果为null

改为在onAttachedToWindow中获取。

扩展阅读:https://stackoverflow.com/questions/53575066/null-window-insets

 

继续阅读
avatar
  • 文本由 发表于 2020年5月21日20:18:51
  • 除非特殊声明,本站文章均为原创,转载请务必保留本文链接
Android实现定位的解决方案 Android

Android实现定位的解决方案

1 前言 相当数量的app应用和游戏,都需要用到定位功能。主要有两类实现思路:一是使用Android自带的位置服务;二是接入第三方SDK,常见的有百度地图,高德地图,腾讯地图等等; 开门见山,先说结论...
Unity接入安卓SDK(3)厘清Gradle的版本 Android

Unity接入安卓SDK(3)厘清Gradle的版本

接入过程中,很多人遇到gradle的各种错误,由于对各种gradle版本的概念不甚了了,模模糊糊一顿操作猛如虎,糊弄的能编译通过就万事大吉,下次再遇到又是一脸懵逼。所以我们还是一起先厘清gradle的...
华为渠道接入的小细节 Android

华为渠道接入的小细节

一、集成HMS SDK 3.0报错AGConnectInitializeProvider 现象: 华为渠道接入使用的maven仓,兴冲冲配置完毕之后,一运行就crash了,真扫兴,查看logcat: ...
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

评论:2   其中:访客  1   博主  1
    • avatar 点点 @回复 0

      AppUtil.CutOutSafeHeight
      AppUtil这个是什么类型的啊QAQ 指哪个

        • avatar kktoo 院长 @回复

          @点点 是个辅助类,这个看业务运用的需要哈,和核心功能无关。它长这样:
          public class AppUtil {
          public static int CutOutSafeHeight = 0;
          }