Android齐刘海适配完全攻略

0
回复
274
查看
[复制链接]

1

主题

1

帖子

10

安币

初级码农

Rank: 1

发表于 2019-5-21 17:40:01 | 显示全部楼层 |阅读模式
如果对本篇文章感兴趣,请前往,原文地址:http://www.apkbus.com/blog-982260-80105.html

![齐刘海](//upload-images.jianshu.io/upload_images/1999609-24c2ebf7ad7afc36.jpeg)## 背景Apple 一直在引领设计的潮流,自从 iPhone X 发布之后,"刘海屏" 就一直存在争议。但用户体验的提升,越来越多的Android厂家逐渐开始在自家旗舰机上使用刘海屏,尤其是 Android P 发布之后,也从系统级支持凹槽屏幕设计。目前在国内比较常见的就是 OPPO x21 和 华为 P20。对于刘海屏,在享受其带来的视觉体验上的同事,我们也要对自家APP做相应的适配,才能最大化的利用刘海屏。- 在Android8.0,Google官方并未提供刘海屏的相关API,因此,对于8.0的系统,各厂家会对刘海屏做特殊适配- 在Android9.0,Google官方提供了DisplayCutout来支持刘海屏适配### 哪些页面需要单独适配就算是增加了刘海屏,你也可以发现,大部分都是“切割”的状态栏的区域,所以就面临了三种情况。- 有状态栏的页面,不会收到刘海屏的影响。- 全屏未适配刘海屏的页面,系统会对刘海屏区域进行切割,让整体 UI 页面做下移处理,避开刘海屏的显示。- 全屏已适配刘海屏的页面,可以兼容刘海屏,做到真正的全屏显示。>因此,目前我们需要做适配的页面只有**全屏无状态栏**的页面进行适配即可## 技术适配刘海屏### 概念说明为了方便讨论,此处引用小米官方文档引用的概念:![](//upload-images.jianshu.io/upload_images/1999609-cb464f92225b7586.png)### 1、基于Google提供的API进行适配在全屏模式下,我们需要有办法获取到刘海屏凹槽的高度,才可以做到设计和布局的时候,留出安全距离。虽然 Google 要求,刘海屏的凹槽,必须和刘海的高度保持一致,而刘海屏又被隐藏在状态栏了,所以有一个思路是直接获取状态栏的高度,来判断刘海之外,可布局的安全区域。不过 Android P 已经预留出了标准的测量 刘海屏凹槽 的API:```DisplayCutout```>此方案仅支持Android9.0及以上(API>=28)### 允许Window扩展到刘海区域Google官方支持三种刘海屏支持模式:- 默认模式(LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT)- 刘海区绘制模式( LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)- 刘海区不绘制模式(LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER)如果开发者未作任何声明,则会按默认模式处理。以下将具体介绍这三种模式的表现。**默认模式**  - *(LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT)*为了在不影响操作的情况下,尽可能利用刘海屏的显示区域,有以下表现:非全屏(normal mode)全屏(fullscreen mode)竖屏(portrait mode)使用耳朵区禁用耳朵区横屏(landscape mode)禁用耳朵区禁用耳朵区注:所谓全屏(fullscreen mode),指隐藏状态栏(status bar),即通过 SYSTEM_UI_FLAG_FULLSCREEN 实现的效果。**刘海区绘制模式 ** - *( LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)*如上所述,默认模式下某些场景会禁用耳朵区,那是因为这些场景下,系统无法判断开发者是否会把控件放置在耳朵区,所以只好默认禁用。如果开发者想要在那些场景下使用耳朵区,需要主动声明,即使用 LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES 来主动声明。**刘海区不绘制模式** - *(LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER )*开发者选用这个模式后,意味着不绘制内容到耳朵区。如非必需,我们不建议采用这种模式,因为那样会浪费不少屏幕空间,用户体验不佳。当开发者选用 NEVER 模式时, [DisplayCutout](https://developer.android.com/reference/android/view/DisplayCutout) 对象 的方法都会返回空值,因为 Google 认为既然开发者不使用耳朵区,就不需要关心刘海的大小了。**设置刘海区绘制模式:**- 可以使用硬编码的方式设置是否允许window扩展到刘海区的代码```javaif (Build.VERSION.SDK_INT >= 28) {  WindowManager.LayoutParams lp = getWindow().getAttributes();  lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;  getWindow().setAttributes(lp); }```- 也可以通过主题设置```xml      shortEdges   ```>注意:Android可能不允许内容视图与状态栏重叠,因此,需要使用[View.setSystemUiVisibility(int)](https://developer.android.com/reference/android/view/View#setSystemUiVisibility(int)(int) 来设置一下属性:- **SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN** //全屏- **SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION** //隐藏导航栏- **SYSTEM_UI_FLAG_LAYOUT_STABLE** //不变### 通过代码规避重要内容```javaview.postDelayed(new Runnable() {    @Override    public void run() {        DisplayCutout displayCutout = view.getRootWindowInsets().getDisplayCutout();        Log.i("AskSky", "SafeInsetBottom:"   displayCutout.getSafeInsetBottom());        Log.i("AskSky", "SafeInsetLeft:"   displayCutout.getSafeInsetLeft());        Log.i("AskSky", "SafeInsetRight:"   displayCutout.getSafeInsetRight());        Log.i("AskSky", "SafeInsetTop:"   displayCutout.getSafeInsetTop());    }}, 100);```使用官方API,可以获取到凹槽的上下左右的距离,这样就可以根据返回的尺寸,合理规划布局,规避无法显示的区域。### 获取状态栏高度方法由于 Notch 设备的状态栏高度与正常机器不一样,因此在需要使用状态栏高度时,不建议写死一个值,而应该改为读取系统的值。以下是获取当前设备状态栏高度的方法:```javaint resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");if (resourceId > 0) {    result = context.getResources().getDimensionPixelSize(resourceId);}```### 其他注意事项- 不要让剪切区域遮挡重要的文本,控件或其他信息。- 不要将需要精细触摸识别的交互式元素放置或扩展到剪切区域。 切口区域的触摸灵敏度可能较低。- 避免使用硬编码获取状态栏高度,因为这有可能导致内容重叠或裁切。尽量使用[WindowInsetsCompat](https://translate.googleusercontent.com/translate_c?depth=1&hl=zh-CN&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://developer.android.com/reference/android/support/v4/view/WindowInsetsCompat&xid=17259,15700002,15700021,15700124,15700149,15700186,15700190,15700201,15700213&usg=ALkJrhhrlATGl1nMujhhCda6yQV6ZT4x7Q)来检索状态栏高度并进行内容填充- 应用程序可能不会充满整个屏幕,应尽量使用[View.getLocationInWindow()](https://developer.android.com/reference/android/view/View#getLocationInWindow(int[])()来代替[View.getLocationOnScreen()](https://developer.android.com/reference/android/view/View.#getLocationOnScreen(int[]))。- 正确使用进出全屏模式的过渡,具体请[查看此文档](https://android-developers.googleblog.com/2017/12/tuning-your-apps-and-games-for-long.html)- 在纵向模式下使用默认剪切行为时,如果剪切区域位于顶部边缘且窗口未设置[FLAG_FULLSCREEN](https://translate.googleusercontent.com/translate_c?depth=1&hl=zh-CN&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://developer.android.com/reference/android/view/WindowManager.LayoutParams&xid=17259,15700002,15700021,15700124,15700149,15700186,15700190,15700201,15700213&usg=ALkJrhjKfXeNzpJnJegk06YT4VxWtNg_Kw#FLAG_FULLSCREEN)或[View.SYSTEM_UI_FLAG_FULLSCREEN](https://translate.googleusercontent.com/translate_c?depth=1&hl=zh-CN&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://developer.android.com/reference/android/view/View&xid=17259,15700002,15700021,15700124,15700149,15700186,15700190,15700201,15700213&usg=ALkJrhgl_34RjO8N8JtaqKFVlSwkYRae2g#SYSTEM_UI_FLAG_FULLSCREEN)[View.SYSTEM_UI_FLAG_HIDE_NAVIGATION](https://translate.googleusercontent.com/translate_c?depth=1&hl=zh-CN&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://developer.android.com/reference/android/view/View&xid=17259,15700002,15700021,15700124,15700149,15700186,15700190,15700201,15700213&usg=ALkJrhgl_34RjO8N8JtaqKFVlSwkYRae2g#SYSTEM_UI_FLAG_HIDE_NAVIGATION)或[View.SYSTEM_UI_FLAG_FULLSCREEN](https://translate.googleusercontent.com/translate_c?depth=1&hl=zh-CN&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://developer.android.com/reference/android/view/View&xid=17259,15700002,15700021,15700124,15700149,15700186,15700190,15700201,15700213&usg=ALkJrhgl_34RjO8N8JtaqKFVlSwkYRae2g#SYSTEM_UI_FLAG_FULLSCREEN) ,则窗口可以延伸到剪切区域。 同样,如果剪切区域位于底部边缘且窗口未设置[View.SYSTEM_UI_FLAG_HIDE_NAVIGATION](https://translate.googleusercontent.com/translate_c?depth=1&hl=zh-CN&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://developer.android.com/reference/android/view/View&xid=17259,15700002,15700021,15700124,15700149,15700186,15700190,15700201,15700213&usg=ALkJrhgl_34RjO8N8JtaqKFVlSwkYRae2g#SYSTEM_UI_FLAG_HIDE_NAVIGATION) ,则窗口可以延伸到剪切区域。在全屏或横向模式中,窗口布局不与切口区域重叠。- 如果您的应用需要转换为全屏模式,请使用```shortEdges```或```never```裁切模式。 因为默认剪切行为可能会导致应用中的内容在转换期间上下移动,如下图所示:![默认裁切模式可能导致内容上下移动](//upload-images.jianshu.io/upload_images/1999609-d88eb00779e5eebc.png)- 在全屏模式下,使用窗口与屏幕坐标应该注意,因为应用程序显示通知栏时,不会占用整个屏幕,因为有通知栏,此时屏幕原点坐标与窗口原点坐标不再相同,此时可以使用 [getLocationOnScreen()](https://developer.android.com/reference/android/view/View#getLocationOnScreen(int[])()方法获取当前屏幕坐标,然后再根据需要转换为窗口坐标。下图显示了有通知栏时内容窗口与通知栏原点坐标的不同之处:![显示状态栏时原点坐标的差异](//upload-images.jianshu.io/upload_images/1999609-18ac60ad22d0dcb2.png)同理,在处理```MotionEvent```事件时,尽量使用 [MotionEvent.getX()](https://developer.android.com/reference/android/view/MotionEvent#getX()()和[MotionEvent.getY()](https://developer.android.com/reference/android/view/MotionEvent#getY()()来避免坐标的问题,不要使用 [MotionEvent.getRawX()](https://developer.android.com/reference/android/view/MotionEvent#getRawX()) 或 [MotionEvent.getRawY()](https://developer.android.com/reference/android/view/MotionEvent#getRawY())### 测试结果如果没有刘海屏设备,可以使用Google提供的模拟器,在开发者选项中打开支持```模拟具有凹口的显示屏(Simulate a display with a cutout)```![模拟刘海屏](//upload-images.jianshu.io/upload_images/1999609-dd9ab2771d436700.png)### 2、基于不同厂商的刘海屏适配方案### 小米全面屏适配方案 - [官方文档](https://dev.mi.com/console/doc/detail?pId=1160)**1、判断是否全面屏**系统增加了 property ro.miui.notch,值为1时则是 Notch 屏手机。```SystemProperties.getInt("ro.miui.notch", 0) == 1;```**2、 应用页面设置使用刘海区显示**使用新增的Meta-data属性notch.config,在应用的AndroidManifest.xml中增加meta-data属性,此属性不仅可以针对Application生效,也可以对Activity配置生效- 对Application生效,意味着该应用的所有页面,系统都不会做竖屏场景的特殊下移或者是横屏场景的右移特殊处理:```xml```其中,value 的取值可以是以下4种:```java"none" 横竖屏都不绘制耳朵区"portrait" 竖屏绘制到耳朵区"landscape" 横屏绘制到耳朵区"portrait|landscape" 横竖屏都绘制到耳朵区```注:一旦开发者声明了meta-data,系统就会优先遵从开发者的声明。- 对Activity生效,如果开发者希望对特定 Window 作处理,可以使用该接口。 在 WindowManager.LayoutParams 增加 extraFlags 成员变量,用以声明该 window 是否使用耳朵区。其中,extraFlags 有以下变量:```java0x00000100 开启配置0x00000200 竖屏配置0x00000400 横屏配置```组合后表示 Window 的配置,如:```java0x00000100 | 0x00000200 竖屏绘制到耳朵区0x00000100 | 0x00000400 横屏绘制到耳朵区0x00000100 | 0x00000200 | 0x00000400 横竖屏都绘制到耳朵区```控制 extraFlags 时注意只控制这几位,不要影响其他位。可以用 Window 的 addExtraFlags 和 clearExtraFlags 来修改, 这两个方法是 MIUI 增加的方法,需要反射调用。```javaint flag = 0x00000100 | 0x00000200 | 0x00000400;try {    Method method = Window.class.getMethod("addExtraFlags",int.class);    method.invoke(getWindow(), flag);} catch (Exception e) {    Log.i(TAG, "addExtraFlags not found.");}```**3、获取挖孔区域宽高**获取当前设备刘海高度的方法:```javaint resourceId = context.getResources().getIdentifier("notch_height", "dimen", "android");if (resourceId > 0) {result = context.getResources().getDimensionPixelSize(resourceId);}```![Notch高度示意](//upload-images.jianshu.io/upload_images/1999609-a9e73c9ee5bd64cf.png)获取当前设备刘海宽度的方法:```javaint resourceId = context.getResources().getIdentifier("notch_width", "dimen", "android");if (resourceId > 0) {result = context.getResources().getDimensionPixelSize(resourceId);}```![Notch宽度示意](//upload-images.jianshu.io/upload_images/1999609-1d21edf78fa68f7d.png)**4、“隐藏屏幕刘海”适配**MIUI 针对 Notch 设备,有一个“隐藏屏幕刘海”的设置项(设置-全面屏-隐藏屏幕刘海),具体表现是:系统会强制盖黑状态栏(无视应用的Notch使用声明),视觉上达到隐藏刘海的效果。但会给某些应用带来适配问题(控件/内容遮挡或过于靠边等)。因此开发者在适配时,还需要检查开启“隐藏屏幕刘海”后,应用的页面是否显示正常。针对有问题的页面,做特殊适配。如有需要,可以通过查询以下 Global settings 来确定「隐藏屏幕刘海」是否开启了,然后再作针对性优化。```javaSettings.Global.getInt(mContext.getContentResolver(), "force_black", 0) == 1```### 华为全面屏适配方案 - [官方文档](https://developer.huawei.com/consumer/cn/devservice/doc/50114)**1、判断是否全面屏**接口描述:类文件接口接口说明com.huawei.android.util.HwNotchSizeUtilpublic static boolean hasNotchInScreen()true:是刘海屏;false:非刘海屏调用方式:```java  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("AskSky", "hasNotchInScreen ClassNotFoundException");        } catch (NoSuchMethodException e) {Log.e("AskSky", "hasNotchInScreen NoSuchMethodException");        } catch (Exception e) {Log.e("AskSky", "hasNotchInScreen Exception");        }        return ret;    }```**2、 应用页面设置使用刘海区显示****方案一:**使用新增的Meta-data属性android.notch_support,在应用的AndroidManifest.xml中增加meta-data属性,此属性不仅可以针对Application生效,也可以对Activity配置生效``````- 对Application生效,意味着该应用的所有页面,系统都不会做竖屏场景的特殊下移或者是横屏场景的右移特殊处理:```xml                            ```- 对Activity生效,意味着可以针对单个页面进行刘海屏适配,设置了该属性的Activity系统将不会做特殊处理:```xml                                                ```**方案二:**可以通过clearHwFlags接口清除添加的华为刘海屏Flag,恢复应用不使用刘海区显示。接口描述:类文件接口接口说明com.huawei.android.view.LayoutParamsExpublic void clearHwFlags (int hwFlags)通过去除窗口FLAG的方式设置页面不使用刘海区显示:调用方式:```java/*刘海屏全屏显示FLAG*/public static final int FLAG_NOTCH_SUPPORT=0x00010000;/** * 设置应用窗口在华为刘海屏手机使用刘海区 * @param window 应用页面window对象 */public static void setNotFullScreenWindowLayoutInDisplayCutout (Window window) {    if (window == null) {        return;    }    WindowManager.LayoutParams layoutParams = window.getAttributes();    try {        Class layoutParamsExCls = Class.forName("com.huawei.android.view.LayoutParamsEx");        Constructor con=layoutParamsExCls.getConstructor(LayoutParams.class);        Object layoutParamsExObj=con.newInstance(layoutParams);        Method method=layoutParamsExCls.getMethod("clearHwFlags", int.class);        method.invoke(layoutParamsExObj, FLAG_NOTCH_SUPPORT);    } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException |InstantiationException     | InvocationTargetException e) {        Log.e("AskSky", "hw clear notch screen flag api error");    } catch (Exception e) {        Log.e("AskSky", "other Exception");    }}```华为刘海屏flag动态添加和删除代码:```javabtn.setOnClickListener(new View.OnClickListener() {    @Override    public void onClick(View view) {        if(isAdd) {//add flagisAdd = false;NotchSizeUtil.setFullScreenWindowLayoutInDisplayCutout(getWindow());                     getWindowManager().updateViewLayout(getWindow().getDecorView(),getWindow().getDecorView().getLayoutParams());        } else{//clear flagisAdd = true;NotchSizeUtil.setNotFullScreenWindowLayoutInDisplayCutout(getWindow());getWindowManager().updateViewLayout(getWindow().getDecorView(),getWindow().getDecorView().getLayoutParams());        }    }});```**3、华为隐藏刘海区开关适配**华为系统提供隐藏刘海区开关供用户选择,当用户打开该开关后会出现以下几种情况- 未适配刘海屏页面:横竖屏都会做页面移动处理,防止被遮挡- 做过刘海屏适配,横屏状态:页面右移,刘海区域涂黑。建议按照未适配刘海平的方案处理- 做过刘海屏适配,竖屏状态:页面不会下移处理,状态栏涂黑,状态栏会覆盖在页面上方,此时整个状态栏区域都会出现遮挡问题。建议按照适配刘海屏的方案处理,布局原则应遵从状态栏区域不显示文字和重要内容。为了更好的适配刘海区开关功能,华为提供相关API来判断用户是否打开隐藏刘海区开关:```javapublic static final String DISPLAY_NOTCH_STATUS = "display_notch_status";int mIsNotchSwitchOpen = Settings.Secure.getInt(getContentResolver(),DISPLAY_NOTCH_STATUS, 0);  // 0表示“默认”,1表示“隐藏显示区域”```![华为刘海屏开关效果](//upload-images.jianshu.io/upload_images/1999609-233a05362602cc1d.png)>注意:虽然Google官方在Android8.0开始支持刘海屏,但是在实际测试中发现,华为依旧只能使用华为官方提供的方式,因此,即使APP只做8.0的适配,依旧要单独做华为的适配### OPPO全面屏适配方案 - [官方文档](https://open.oppomobile.com/service/message/detail?id=61876)**1、判断是否全面屏**```context.getPackageManager().hasSystemFeature(“com.oppo.feature.screen.heteromorphism”)```返回 true为凹形屏 ,可识别OPPO的手机是否为凹形屏。**2、 应用页面设置使用刘海区显示****方案一:**AndroidManifest.xml中配置支持最大高宽比```xmlandroid:maxAspectRatio="ratio_float"   (Android8.0以上)ratio_float = 屏幕高 / 屏幕宽 ```(如oppo新机型屏幕分辨率为2280 x 1080, ratio_float = 2280 / 1080 = 2.11,建议设置 ratio_float为2.2或者更大)**方案二:**支持分屏,注意验证分屏下界面兼容性```xmlandroid:resizeableActivity=”true” (Android7.0以上)```由于设置```maxAspectRatio``` 和 ```resizeableActivity``` 方案都有API版本限制,因此使用``````可以不需要考虑版本号兼容问题,但如果APP本身需要支持分屏,则使用方案二最佳**3、获取挖孔区域大小**获取ro.oppo.screen.heteromorphism属性值可获取凹形区域的范围,例 [ro.oppo.screen.heteromorphism]: [378,0:702,80],含义如下- 378:表示竖屏下左上角横坐标- 0 :表示竖屏下左上角竖坐标- 702:表示竖屏下右下角横坐标- 80 :表示竖屏下右下角竖坐标调用方式:```javaString mProperty = ""; mProperty = SystemProperties.get("ro.oppo.screen.heteromorphism"); …… public static class SystemProperties {        public static String get(String key) {   String value = "";   Class cls = null;   try {        cls = Class.forName("android.os.SystemProperties");        Method hideMethod = cls.getMethod("get", String.class);        Object object = cls.newInstance();        value = (String) hideMethod.invoke(object, key); } catch (ClassNotFoundException e) {       Log.e("AskSky", "get error() ", e); } catch (NoSuchMethodException e) {       Log.e("AskSky", "get error() ", e); } catch (InstantiationException e) {       Log.e("AskSky", "get error() ", e); } catch (IllegalAccessException e) {       Log.e("AskSky", "get error() ", e); } catch (IllegalArgumentException e) {       Log.e("AskSky", "get error() ", e); } catch (InvocationTargetException e) {       Log.e("AskSky", "get error() ", e); } return value;       } }```![凹槽区域尺寸示意图](//upload-images.jianshu.io/upload_images/1999609-ee1b5e8d1227a1e5.png)### VIVO全面屏适配方案 - [官方文档](https://swsdl.vivo.com.cn/appstore/developer/uploadfile/20180328/20180328152252602.pdf)**1、判断是否全面屏**接口描述:包名:android.util.FtFeature接口:public static boolean isFtFeatureSupport (int mask)参数说明:```    0x00000020  表示是否有凹槽    0x00000008  表示是否有圆角```返回值: true表示有此特征调用方式:```java    public static final int HAS_GROOVE = 0x00000020;    public static final int HAS_CIRCULAR = 0x00000008;    private void initView() {        boolean hasGroove = isFtFeatureSupport(HAS_GROOVE);        boolean hasCircular = isFtFeatureSupport(HAS_CIRCULAR);        Log.d("AskSky", "是否有刘海屏:"   hasGroove);        Log.d("AskSky", "是否有圆角:"   hasCircular);    }    @SuppressLint("PrivateApi")    private boolean isFtFeatureSupport(int tag) {        boolean value = false;        try {Class cls = Class.forName("android.util.FtFeature");Method hideMethod = cls.getMethod("isFtFeatureSupport", int.class);Object object = cls.newInstance();value = (boolean) hideMethod.invoke(object, tag);        } catch (ClassNotFoundException e) {Log.e("AskSky", "get error() ", e);        } catch (NoSuchMethodException e) {Log.e("AskSky", "get error() ", e);        } catch (InstantiationException e) {Log.e("AskSky", "get error() ", e);        } catch (IllegalAccessException e) {Log.e("AskSky", "get error() ", e);        } catch (IllegalArgumentException e) {Log.e("AskSky", "get error() ", e);        } catch (InvocationTargetException e) {Log.e("AskSky", "get error() ", e);        }        return value;    }```**2、应用页面设置使用刘海区** *与OPPO设置方式相同***方案一:**AndroidManifest.xml中配置支持最大高宽比```xmlandroid:maxAspectRatio="ratio_float"   (Android8.0以上)ratio_float = 屏幕高 / 屏幕宽 ```(如oppo新机型屏幕分辨率为2280 x 1080, ratio_float = 2280 / 1080 = 2.11,建议设置 ratio_float为2.2或者更大)**方案二:**支持分屏,注意验证分屏下界面兼容性```xmlandroid:resizeableActivity=”true” (Android7.0以上)```由于设置```maxAspectRatio``` 和 ```resizeableActivity``` 方案都有API版本限制,因此使用``````可以不需要考虑版本号兼容问题,但如果APP本身需要支持分屏,则使用方案二最佳>个人认为,VIVO官方文档是最坑爹的,仅告诉你方法名和参数,但没有具体Demo,只能自己闭着眼写其次,VIVO没有提供获取挖孔屏位置的官方API,因此,在开发过程中只能获取状态栏高度,尽量避免在状态栏区域显示重要内容和文字关于圆角边,VIVO官方建议是**半径小于50dp的区域为安全区域**,但官方又说了,尽量不要考虑圆角的具体半径,尽量只考虑圆角遮盖问题,尽量将内容放到安全区域。## 对于全面屏适配的一些建议**1、声明 Maximum Aspect Ratio**>Android 标准接口中,支持应用声明其支持的最大屏幕高宽比(maximum aspect ratio)。具体声明如下,其中的 ratio_float 被定义为是高除以宽,以 16:9 为例,ratio_float = 16/9 = 1.778 (18:9则为2.0)。```xml    ```若开发者没有声明该属性,ratio_float 的默认值为1.86,小于2.0,因此这类应用在全面屏手机上,默认不会全屏显示,屏幕底部会留黑。考虑到将有更多 19.5:9 甚至更长的手机出现,建议开发者声明 Maximum Aspect Ratio ≥ 2.2 或更多。值得一提的是,如果应用的 android:resizeableActivity 已经设置为 true,就不必设置 Maximum Aspect Ratio 了。详见 Android 官方文档 [Declaring maximum aspect ratio](https://developer.android.com/guide/practices/screens_support.html#screen-independence)。**2、避免内容拉伸、变形**从16:9变成18:9甚至更长的比例,图片往往被会拉伸变形,此问题常见于应用开屏图。开发者应使用更灵活的布局,以适应不同的屏幕比例。**3、充分利用屏幕空间**开发者应充分利用全面屏显示更多内容。如下图,王者荣耀已修改了 Maximum Aspect Ratio,在全面屏有更宽阔的游戏视野![王者荣耀对比](//upload-images.jianshu.io/upload_images/1999609-c8865ba6e70ef72c.png)**4、虚拟按键适配**为了实现更高的屏占比,屏幕内的虚拟导航键就成了标准功能,如何让其应用界面在视觉上统一,同样需要开发者的积极适配。Android 已经有相关接口允许开发者自定义虚拟键的样式,以下是可供选择的样式。![虚拟键的样式](//upload-images.jianshu.io/upload_images/1999609-bb8feaf5e329d60c.png)修改虚拟按键样式,Android 有标准的实现方式, 调用以下接口即可 [window.setNavigationBarColor (int color)](https://developer.android.com/reference/android/view/Window.html#setNavigationBarColor(int)(int color)。在调用该接口时,还需要设置一些flag```java/** * Sets the color of the navigation bar to {@param color}. * * For this to take effect, * the window must be drawing the system bar backgrounds with * {@link android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS} and * {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_NAVIGATION} must not be set. * * If {@param color} is not opaque, consider setting * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION}. *  * The transitionName for the view background will be "android:navigation:background". *
*/public abstract void setNavigationBarColor(@ColorInt int color);```>以上内容是我根据各大官方文档总结写的,如有遗漏欢迎指正。其中Android9.0适配,是我根据官方文档,并且在9.0手机上实践过的,真心不容易,如有转载,请注明出处。作者:啸天AskSky 文章:https://www.jianshu.com/p/96265f9ab386  继续阅读全文



想在安卓巴士找到更多优质博文,可移步博客区

如果对本篇文章感兴趣,请前往,
原文地址:
http://www.apkbus.com/blog-982260-80105.html
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

领先的中文移动开发者社区
18620764416
7*24全天服务
意见反馈:1294855032@qq.com

扫一扫关注我们

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 粤ICP备15117877号 )