安卓P版本也就是刚发布的安卓9.0

其中就加入了刘海屏相关的API。其相关行为与功能变更也是非常多的,从其变更的趋势及功能来看,google在进一步的收紧权限。

其P版本变更行为中就加入了对非SDK接口使用的新限制。无论是直接,通过反射还是通过JNI。只要应用程序引用非SDK接口或尝试使用反射或JNI获取其句柄,就会应用这些限制。通常,应用程序应仅使用SDK中类的官方记录部分。特别是,这意味着当您通过诸如反射之类的语义与类交互时,您不应该计划访问SDK中未列出的方法或字段。使用此类方法或字段存在破坏应用程序的高风险。

对于非SDK行为的限制,其中google列出了灰名单/黑名单,其对应的方法和属性非常之多,目前还没来得及查找。当然谷歌也提供了检查我们应用中是否含有这些非限制的行为,具体办法见https://developer.android.google.cn/about/versions/pie/restrictions-non-sdk-interfaces。当然这个不是我们今天要讲的重点,对于非SDK行为的限制我会再抽时间好好研究后再发布一篇博客。

在安卓P发布公测版到7月底,想必不少开发者都收到了来自各大应用市场发来的邮件。

邮件内容以其中一家为例(具体市场名称已隐匿):

  1. 尊敬的开发者:

  2. 您好!为保障用户的使用体验,应用市场已在3月启动Android P版本应用适配检测工作,

  3. 针对未做适配的应用开发者陆续进行邮件通知。

  4. 请于2018年7月底前务必完成Android P版本适配工作并自检通过。

  5. 针对未适配或在Android P版本体验欠佳的应用,

  6. 应用市场将在Android P版本机型上采取屏蔽或不推荐更新策略,

  7. 可能会对您的推广、用户口碑及品牌产生影响。

  8. 相关技术资料见:https://developer.android.google.cn/preview/overview.html

  9. 请各相关开发者及时调整。

这样就导致我们不得不去适配安卓P了。

在收到邮件后,查阅谷歌官方的资料,更新SDK。其中UI最大的改变就是需要适配刘海屏。

但是国内厂商为了快速抢占全面屏手机市场,在安卓系统8.1的基础上,硬件就弄出了刘海屏,例如华为nova 3e, oneplus 6, oppo r15, vivo x21,但是系统支持没有更上,没有一个统一的API。但发现他们的分辨率都出奇的接近,就像达成了默契,2280 * 1080,高宽比达到了2.1。

今天就来说说怎么适配安卓8.1及P上的刘海屏。

首先下载谷歌最新的SDK API 28及系统镜像。新建API 28的模拟器后,自定义分辨率,高宽比最好设置为2以上,比如2280 * 1080。安卓系统默认支持的宽高比是1.7-1.8左右。具体多少值忘记了。有同学知道的在下方留言哦。但是现在出现了比值大于1.8了会出现什么情况呢?那我先给大家放一张没有做适配较大宽高比应用出现的情况的样子:

第一张是compileSdkVersion 为25,targetSdkVersion为22的时候的情形。第二张是compileSdkVersion 为28,targetSdkVersion为28的正常需要显示的场景,模拟器分辨率为1080 * 2280。那为何在最下方出现黑色区域呢?

正如前面说到的安卓系统默认支持的宽高比是1.7-1.8左右,现在流行的全面屏手机分辨率宽高比大于了这个数,导致屏幕区域不能利用完全,就出现了黑色区域。解决办法也很简单。

  1. 就是将编译版本compileSdkVersion升到24以上,目标版本targetSdkVersion升到24以上;
  2. 如果不愿升级targetSdkVersion,可以在AndroidManifest.xml中application节点下设置android:resizeableActivity="true"
    1. <application

    2. android:name=".App"

    3. android:allowBackup="true"

    4. android:icon="@mipmap/ic_launcher"

    5. android:label="@string/app_name"

    6. android:resizeableActivity="true"

    7. android:roundIcon="@mipmap/ic_launcher_round"

    8. android:theme="@style/AppTheme">

  3. 或者在AndroidManifest.xml中application中设置
    1. <!-- Render on full screen up to screen aspect ratio of 2.4 -->

    2. <!-- Use a letterbox on screens larger than 2.4 -->

    3. <meta-data android:name="android.max_aspect" android:value="2.4" />

刘海屏

有刘海屏真机的同学可以跳过这部分,首先在设置中打开“开发者选项”,找到“绘图”,找到“模拟具有凹口的显示屏”,选择“长型显示屏凹口”。如图:

第二张图就是开启模拟选项后显示的切口样式,当然这个缺口是模拟的,目前真机普遍的刘海屏高度在90px左右,安卓模拟器模拟的刘海屏高度为144px。第三张图是开启一个应用的闪屏页没有适配刘海屏的高度时候的样子,顶部出现了黑色区域。这个黑色区域是因为刘海缺口导致的整个界面整体向下移。因为我们的闪屏页一般都是会全屏显示,那我们就需要适配刘海屏上的全屏样式了。

坐好了,我开始吹一波了。

  1. <style name="theme_loading" parent="@android:style/Theme.Light.NoTitleBar.Fullscreen">

  2. <item name="android:windowBackground">@drawable/loading</item>

  3. </style>

闪屏页的样式我设置了全屏

代码中这样判断:

  1. /**

  2. * adapt fullScreen mode

  3. *

  4. * @param mActivity a

  5. */

  6. public static void openFullScreenModel(Activity mActivity) {

  7. try {

  8. if (Build.VERSION.SDK_INT >= 28) {

  9. mActivity.requestWindowFeature(Window.FEATURE_NO_TITLE);

  10. WindowManager.LayoutParams lp = mActivity.getWindow().getAttributes();

  11. lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;

  12. mActivity.getWindow().setAttributes(lp);

  13. View decorView = mActivity.getWindow().getDecorView();

  14. int systemUiVisibility = decorView.getSystemUiVisibility();

  15. int flags = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

  16. | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION

  17. | View.SYSTEM_UI_FLAG_FULLSCREEN;

  18. systemUiVisibility |= flags;

  19. mActivity.getWindow().getDecorView().setSystemUiVisibility(systemUiVisibility);

  20. }

  21. } catch (Exception e) {

  22. e.printStackTrace();

  23. }

  24. }

如果是需要全屏显示的activity,就需要调用这个方法。这样设置后我们的内容区域就可以全屏显示了。如下图:

第二张是进入的主页。

我另外又同时为大家准备了另外一个适配场景,其中做了适配和没有做适配的页面如下图:

想必大家从以上2个场景中也能总结出刘海屏的适配规则了:

就是界面的重要元素(例如按钮)不能被状态栏遮挡,需要充分的利用状态栏的显示区域。

这个也是根据场景而定,如果你的界面最顶部元素不需要侵入状态栏,那就只需要关注设置了全屏样式的activity的适配。否则你还得继续做一部分适配工作。那我们来看看显示正常的activity代码:

  1. /**

  2. * Author:William Time:2018/8/12

  3. * Class Comment:has handled cutout

  4. */

  5. public class HasAdaptActivity extends AppCompatActivity {

  6. @Override

  7. protected void onCreate(Bundle savedInstanceState) {

  8. StatusBarManager.setStatusBar(this, true);

  9. super.onCreate(savedInstanceState);

  10. setContentView(R.layout.activity_has_adapt);

  11. // adapt cutout

  12. int pH = DisplayCutoutUtil.getStatusBarHeight(this);

  13. RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(

  14. ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);

  15. params.topMargin = pH;

  16. findViewById(R.id.rl_top).setLayoutParams(params);

  17. }

  18. }

  1. /**

  2. * get statusBar height

  3. *

  4. * @param context c

  5. * @return h

  6. */

  7. public static int getStatusBarHeight(Context context) {

  8. try {

  9. int result = 0;

  10. int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");

  11. if (resourceId > 0) {

  12. result = context.getResources().getDimensionPixelSize(resourceId);

  13. }

  14. Log.d(TAG, "getStatusBarHeight==========>" + result);

  15. return result;

  16. } catch (Exception e) {

  17. e.printStackTrace();

  18. }

  19. return 0;

  20. }

layout.xml

  1. <?xml version="1.0" encoding="utf-8"?>

  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

  3. xmlns:tools="http://schemas.android.com/tools"

  4. android:layout_width="match_parent"

  5. android:layout_height="match_parent"

  6. android:orientation="vertical"

  7. tools:context=".activity.NoAdaptActivity">

  8. <ImageView

  9. android:id="@+id/iv_image"

  10. android:layout_width="match_parent"

  11. android:layout_height="250dp"

  12. android:scaleType="centerCrop"

  13. android:src="@drawable/image" />

  14. <Button

  15. android:id="@+id/button"

  16. style="@style/tv_wrapContent.txt14"

  17. android:layout_below="@+id/iv_image"

  18. android:layout_centerHorizontal="true"

  19. android:layout_marginTop="20dp"

  20. android:text="保证界面按钮不被状态栏遮挡" />

  21. <RelativeLayout

  22. android:id="@+id/rl_top"

  23. android:layout_width="match_parent"

  24. android:layout_height="wrap_content">

  25. <Button

  26. android:layout_width="wrap_content"

  27. android:layout_height="wrap_content"

  28. android:layout_margin="10dp"

  29. android:text="按钮1" />

  30. <Button

  31. android:layout_width="wrap_content"

  32. android:layout_height="wrap_content"

  33. android:layout_alignParentRight="true"

  34. android:layout_margin="10dp"

  35. android:text="按钮2" />

  36. </RelativeLayout>

  37. </RelativeLayout>

另外处理状态栏的工具类就不贴出来了,大家可以去文章最后的地址去下载。

代码大家一看就明白,就是给最顶部的按钮设置了一个marginTop即可,值即为状态栏的高度,通过代码设置可以直接适配所有版本。

另外附上厂商提供的适配方案:

  1. 华为官方适配方案:http://mini.eastday.com/bdmip/180411011257629.html
  2. oppo官方适配方案:https://open.oppomobile.com/wiki/doc#id=10159
  3. vivo官方适配方案:https://dev.vivo.com.cn/documentCenter/doc/103
  4. 谷歌适配指南:https://developer.android.google.cn/about/versions/pie/

结合华为,oppo,vivo 的适配文档,较为完整的处理缺口适配工具类代码如下:

  1. package com.william.adaptnotch.utils;

  2. import android.app.Activity;

  3. import android.content.Context;

  4. import android.os.Build;

  5. import android.util.Log;

  6. import android.view.View;

  7. import android.view.Window;

  8. import android.view.WindowManager;

  9. import java.lang.reflect.Method;

  10. /**

  11. * 作者:William 时间:2018/7/23

  12. * 类说明:adapt cutout utils

  13. */

  14. public class DisplayCutoutUtil {

  15. private static final String TAG = "DisplayCutoutUtil";

  16. /**

  17. * adapt fullScreen mode

  18. *

  19. * @param mActivity a

  20. */

  21. public static void openFullScreenModel(Activity mActivity) {

  22. try {

  23. if (needAdaptNotch(mActivity)) {

  24. mActivity.requestWindowFeature(Window.FEATURE_NO_TITLE);

  25. WindowManager.LayoutParams lp = mActivity.getWindow().getAttributes();

  26. if (Build.VERSION.SDK_INT >= 28) {

  27. lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;

  28. }

  29. mActivity.getWindow().setAttributes(lp);

  30. View decorView = mActivity.getWindow().getDecorView();

  31. int systemUiVisibility = decorView.getSystemUiVisibility();

  32. int flags = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

  33. | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION

  34. | View.SYSTEM_UI_FLAG_FULLSCREEN;

  35. systemUiVisibility |= flags;

  36. mActivity.getWindow().getDecorView().setSystemUiVisibility(systemUiVisibility);

  37. }

  38. } catch (Exception e) {

  39. e.printStackTrace();

  40. }

  41. }

  42. /**

  43. * need to adapt Notch screen

  44. *

  45. * @return true otherwise false

  46. */

  47. private static boolean needAdaptNotch(Context c) {

  48. return Build.VERSION.SDK_INT >= 28 || isHuaweiNotch(c) || isOppoNotch(c) || isVivoNotch(c);

  49. }

  50. /**

  51. * huawei

  52. *

  53. * @param context c

  54. * @return hasNotch

  55. */

  56. private static boolean isHuaweiNotch(Context context) {

  57. boolean ret = false;

  58. try {

  59. ClassLoader cl = context.getClassLoader();

  60. Class HwNotchSizeUtil = cl.loadClass("com.huawei.android.util.HwNotchSizeUtil");

  61. Method get = HwNotchSizeUtil.getMethod("hasNotchInScreen");

  62. ret = (boolean) get.invoke(HwNotchSizeUtil);

  63. } catch (ClassNotFoundException e) {

  64. Log.e("test", "hasNotchInScreen ClassNotFoundException");

  65. } catch (NoSuchMethodException e) {

  66. Log.e("test", "hasNotchInScreen NoSuchMethodException");

  67. } catch (Exception e) {

  68. Log.e("test", "hasNotchInScreen Exception");

  69. }

  70. return ret;

  71. }

  72. /**

  73. * OPPO

  74. *

  75. * @param context Context

  76. * @return hasNotch

  77. */

  78. private static boolean isOppoNotch(Context context) {

  79. try {

  80. return context.getPackageManager().hasSystemFeature("com.oppo.feature.screen.heteromorphism");

  81. } catch (Exception e) {

  82. e.printStackTrace();

  83. }

  84. return false;

  85. }

  86. /**

  87. * VIVO

  88. * param:

  89. * 0x00000020表示是否有凹槽;

  90. * 0x00000008表示是否有圆角。

  91. *

  92. * @param context Context

  93. * @return hasNotch

  94. */

  95. private static boolean isVivoNotch(Context context) {

  96. boolean hasNotch = false;

  97. try {

  98. ClassLoader cl = context.getClassLoader();

  99. Class FtFeature = cl.loadClass("android.util.FtFeature");

  100. Method get = FtFeature.getMethod("isFeatureSupport");

  101. hasNotch = (boolean) get.invoke(FtFeature, new Object[]{0x00000020});

  102. } catch (Exception e) {

  103. e.printStackTrace();

  104. }

  105. return hasNotch;

  106. }

  107. /**

  108. * get statusBar height

  109. *

  110. * @param context c

  111. * @return h

  112. */

  113. public static int getStatusBarHeight(Context context) {

  114. try {

  115. int result = 0;

  116. int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");

  117. if (resourceId > 0) {

  118. result = context.getResources().getDimensionPixelSize(resourceId);

  119. }

  120. Log.d(TAG, "getStatusBarHeight==========>" + result);

  121. return result;

  122. } catch (Exception e) {

  123. e.printStackTrace();

  124. }

  125. return 0;

  126. }

《转》安卓P 刘海屏的适配相关推荐

  1. 安卓P 刘海屏的适配

    安卓P版本也就是刚发布的安卓9.0 其中就加入了刘海屏相关的API.其相关行为与功能变更也是非常多的,从其变更的趋势及功能来看,google在进一步的收紧权限. 其P版本变更行为中就加入了对非SDK接 ...

  2. Android 刘海屏的适配

    1.Android8.0以上的适配说明 主要说明国内外几大主流产商:小米.oppo.vivo.华为.三星.Google. 小米 MIUI Notch 屏适配说明 Notch 机型在界面上会带来两个问题 ...

  3. html刘海屏高度,iphone刘海屏网页适配方法

    1. 下面是实现iphonex 刘海屏前端网页适配的一个插值算法小案例 Title body, ul { margin: 0; } ul { padding-left: 10px; } li { li ...

  4. Android P (9.0)刘海屏(DisplayCutout)适配方法

    简介 Android P版本提供了统一的刘海屏方案和三方适配刘海屏方案: 对于有状态栏的页面,不会受到刘海屏特性的影响 全屏显示的页面,系统刘海屏方案会对应用界面做下移处理,避开刘海区显示 已经适配A ...

  5. iPhoneX(刘海屏)适配

    iPhoneX(刘海屏)适配 在 iOS11的时候,苹果破天荒发布了一款新屏幕样式的手机,俗称刘海屏.世人皆道丑,而后纷纷买之.自此,iOS 开发者们也走向了 iPhoneX系列的适配之旅. 区别 状 ...

  6. android刘海屏高度适配,Android刘海屏的适配

    这里主要是介绍一下Android P中刘海屏的适配以及Android P之前的适配.为什么要分开呢?因为Android P之前官方还没提供API来进行适配,都是由各家厂商来提供适配方案的. 1.And ...

  7. 1.屏幕分类和刘海屏的适配原则

    1.屏幕分类和刘海屏的适配原则_哔哩哔哩_bilibili 百分百布局不在维护 淘汰了  头条怎么做的 阿里怎么做的? 将近几千种  rom6 7种,之前适配宽和高  曲面屏 分屏 异形屏 andro ...

  8. android 全面屏/刘海屏有效适配

    黑底问题 手机厂商追求高用户体验,屏幕宽高比越做越高.17:9 19:10 18:9 18.5:9所谓全面屏. 原来一般主流手机1920*1080分辨率 16:9高宽比.如果没有单独去配置属性,会导致 ...

  9. CSS 适配刘海屏 CSS适配iOS屏幕 ios可视窗口

    CSS 适配刘海屏 CSS适配iOS屏幕 ios可视窗口 由于苹果手机上面刘海会遮挡住一些内容,所以我们在开发的时候需要进行一些适配 //使用padding-top的让内容往下挤,适配刘海屏 padd ...

最新文章

  1. php csv文件的读取,写入,输出下载操作详解
  2. 根据下拉框生成控件列表
  3. Android开发:Handler的简单使用(一)
  4. 从 DevOps 到 Serverless:通过“不用做”的方式解决“如何更高效做”的问题
  5. 重写GridView的Intellisence问题
  6. Web框架之Django_06 模型层了解(F查询、Q查询、事务、update和save、only和defer、choice属性、bulk_create)
  7. C#基础-应用程序域
  8. 各种触发器的特性方程_薛定谔方程,究竟神奇在哪里?
  9. c语言指针代码大全,C语言之指针(示例代码)
  10. 【JSF框架】 是一种标准
  11. JavaScript 高级程序设计笔记
  12. h3c交换机配置nat_H3C-NAT 命令配置
  13. python当中的列表函数和列表推导式
  14. linux有什么好的软件,Linux国产软件大盘点
  15. twitter账户受限_如何为您的企业设置Twitter帐户
  16. Problem A: 判断是否是素数
  17. 如何选择统计检验方法
  18. python 面试题
  19. 超级详细的 shell编程知识讲解 —— 第二部分(全文3万多字,看完之后不想成为高手也难!)
  20. 红帽linux更新资源库,如何用APT维护红帽企业版Linux

热门文章

  1. 用quartus搭建soc-串口发送学号完整流程(软件+硬件)
  2. 数字图像处理(四)——图像编码技术(一)
  3. mlock家族:锁定物理内存
  4. mysql数据库约束详解_基于MySQL数据库的数据约束实例及五种完整性约束介绍
  5. 选择开还是关 pxe_商用披萨(53)开披萨店是选择加盟还是自创品牌呢?
  6. java中用流提取文档中的文字,语言实现从word文档中提取文本
  7. python sys os_python常用的一些东西——sys、os等(转)
  8. 【力扣】NO.136.只出现一次的数字
  9. 使用token实现在有效期内APP自动登录功能
  10. [P4063][JXOI2017]数列(DP)