做者 : 快乐丸 转载自 :www.jianshu.com/p/8ead0701d…android
目前市面上的刘海屏和水滴屏手机愈来愈多了,颜值方面是因人而异,有的人以为很好看,也有人以为丑爆了,我我的以为是还能够。可是做为移动开发者来讲,这并非一件好事,愈来愈多异形屏手机的出现意味着咱们须要投入大量精力在适配上(就不提以后会出的折叠屏手机了)。本文总结了当下主流手机的刘海屏适配方案,鉴于目前Android碎片化的状况,想要覆盖全部的机型是不可能的,可是能适配一些是一些,总比什么都不作要好。git
所谓刘海屏,指的是手机屏幕正上方因为追求极致边框而采用的一种手机解决方案。因形似刘海儿而得名——来自百度百科,水滴屏也是相似,为了简单起见,下文就统称这两种为刘海屏了。github
这里先上一张官方的图api
从图中能够看出,刘海区域是镶嵌在状态栏内部的,刘海区域的高度通常是不超过状态栏高度的。所以,当咱们的应用布局须要占据状态栏来显示时,就须要考虑到刘海区域是否会遮挡住页面上的控件或者背景,这就是为何将状态栏区域称为危险区域。若是应用不须要占据状态栏显示,所有显示在安全区域内,那么恭喜你,不须要作任何适配处理。总结来讲,只有当应用须要全屏显示时才须要进行适配。 全屏显示无非就是两种状况:第一种是咱们常说的沉浸式状态栏,也就是状态栏透明,页面的布局延伸到状态栏显示,这种状况下状态栏依然可见;第二种是相似应用的闪屏页风格,页面全屏显示,状态栏不可见。这两种状况下若是不进行适配处理都会产生一些问题。 先来看第一种状况,沉浸式风格。须要将状态栏设置为透明,须要注意只有在Android 4.4(API Level 19)以上才支持设置透明状态栏。有两种设置方法: 方法一:为Activity设置style,添加一个属性:安全
<item name="android:windowTranslucentStatus">true</item>
复制代码
方法二:在Activity的onCreate()中为Window添加Flagbash
public class ImmersiveActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_immersive);
// 透明状态栏
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
}
复制代码
页面的布局很简单,只包含一个按钮,为了明显,我为根布局设置了一个背景。 activity_immersive.xmlapp
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@mipmap/bg"
android:orientation="vertical">
<Button
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />
</LinearLayout>
复制代码
运行以后发现按钮会被刘海区域所遮挡,如图所示:ide
再说第二种状况,全屏风格,状态栏不可见。一样有两种设置方法: 方法一:为Activity设置style,添加属性:工具
<item name="android:windowFullscreen">true</item>
<!-- 这里为了简单,直接从style中指定一个背景 -->
<item name="android:windowBackground">@mipmap/bg</item>
复制代码
方法二:在Activity的OnCreate()中添加代码:布局
public class FullScreenActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 全屏显示
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
}
复制代码
补充说明一点,如今的手机屏幕高宽比例愈来愈大,咱们还须要额外作一下适配才能使应用在全部手机上都能全屏显示,具体方式有两种: 方式一:在AndroidManifest.xml中配置支持最大高宽比
<meta-data android:name="android.max_aspect"
android:value="ratio_float" />
复制代码
或者
android:maxAspectRatio="ratio_float" (API LEVEL 26)
复制代码
说明:以上两种接口能够二选一,ratio_float = 屏幕高 / 屏幕宽 (如oppo新机型屏幕分辨率为2280 x 1080, ratio_float = 2280 / 1080 = 2.11,建议设置 ratio_float为2.2或者更大) 方式二:在AndroidManifest.xml中配置支持分屏,注意验证分屏下界面兼容性
android:resizeableActivity="true"
复制代码
也能够经过设置targetSdkVersion>=24(即Android 7.0),该属性的值会默认为true,就不须要在AndroidManifest.xml中配置了。 运行以后,咱们发现状态栏的部分留出了一条黑边,看上起很奇怪,这显然不是咱们想要的效果。
上文中已经展现了刘海屏中全屏显示带来的问题,那么如何去解决呢?
其实沉浸式状态栏带来的遮挡问题与刘海屏无关,本质上是因为设置了透明状态栏致使布局延伸到了状态栏中,就算是不具备刘海屏,必定程度上也会形成布局的遮挡。不过既然刘海屏是处在状态栏当中的,那么咱们就把这种状况也包含在刘海屏的适配中。清楚了缘由以后,解决起来就很简单了,咱们只须要让控件或布局避开状态栏显示就能够了,具体的解决方法有三种。 方法一.利用fitsSystemWindows属性 当咱们给最外层View设置了android:fitsSystemWindows="true"属性后,当设置了透明状态栏或者透明导航栏后,就会自动给View添加paddingTop或paddingBottom属性,这样就在屏幕上预留出了状态栏的高度,咱们的布局就不会占用状态栏来显示了。 activity_immersive.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ll_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@mipmap/bg"
android:fitsSystemWindows="true"
android:orientation="vertical">
<Button
android:layout_width="150dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />
</LinearLayout>
复制代码
方法二.根据状态栏高度手动设置paddingTop 这种方法的实现本质上和设置fitsSystemWindows是同样的,首先获取状态栏高度,而后设置根布局的paddingTop等于状态栏高度就能够了,代码以下:
public class ImmersiveActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_immersive);
// 透明状态栏
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
LinearLayout llRoot = findViewById(R.id.ll_root);
// 设置根布局的paddingTop
llRoot.setPadding(0, getStatusBarHeight(this), 0, 0);
}
/**
* 获取状态栏高度
*
* @param context
* @return
*/
public int getStatusBarHeight(Context context) {
int statusBarHeight = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
}
return statusBarHeight;
}
}
复制代码
方法三.在布局中添加一个和状态栏高度相同的View 和前两种方法原理相似,一样是让屏幕预留出状态栏的高度,这里在根布局中添加了一个透明的View,高度和状态栏高度相同。这种方法的好处是能够自定义填充状态栏View的背景,更灵活地实现咱们想要的效果。
public class ImmersiveActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_immersive);
// 透明状态栏
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
LinearLayout llRoot = findViewById(R.id.ll_root);
View statusBarView = new View(this);
statusBarView.setBackgroundColor(Color.TRANSPARENT);
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
getStatusBarHeight(this));
// 在根布局中添加一个状态栏高度的View
llRoot.addView(statusBarView, 0, lp);
}
/**
* 获取状态栏高度
*
* @param context
* @return
*/
public int getStatusBarHeight(Context context) {
int statusBarHeight = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
}
return statusBarHeight;
}
}
复制代码
适配以后成功地将控件避开了状态栏(危险区域),以下图所示:
对于全屏显示的状况,处理起来要相对麻烦一些,下面重点说一下这种状况下的适配方案。
谷歌官方从Android P开始给开发者提供了刘海屏相关的API,能够经过直接调用API来进行刘海屏的适配处理。 经过DisplayCutout类能够得到安全区域的范围以及刘海区域(官方的叫法是缺口)的信息,须要注意只有API Level在28及以上才能够调用。
/**
* 得到刘海区域信息
*/
@TargetApi(28)
public void getNotchParams() {
final View decorView = getWindow().getDecorView();
if (decorView != null) {
decorView.post(new Runnable() {
@Override
public void run() {
WindowInsets windowInsets = decorView.getRootWindowInsets();
if (windowInsets != null) {
// 当全屏顶部显示黑边时,getDisplayCutout()返回为null
DisplayCutout displayCutout = windowInsets.getDisplayCutout();
Log.e("TAG", "安全区域距离屏幕左边的距离 SafeInsetLeft:" + displayCutout.getSafeInsetLeft());
Log.e("TAG", "安全区域距离屏幕右部的距离 SafeInsetRight:" + displayCutout.getSafeInsetRight());
Log.e("TAG", "安全区域距离屏幕顶部的距离 SafeInsetTop:" + displayCutout.getSafeInsetTop());
Log.e("TAG", "安全区域距离屏幕底部的距离 SafeInsetBottom:" + displayCutout.getSafeInsetBottom());
// 得到刘海区域
List<Rect> rects = displayCutout.getBoundingRects();
if (rects == null || rects.size() == 0) {
Log.e("TAG", "不是刘海屏");
} else {
Log.e("TAG", "刘海屏数量:" + rects.size());
for (Rect rect : rects) {
Log.e("TAG", "刘海屏区域:" + rect);
}
}
}
}
});
}
}
复制代码
这里我在测试时也发现了一个问题,就是若是是在style中设置了全屏模式,在适配以前,顶部状态栏区域显示一条黑边,这时候调用getDisplayCutout()获取DisplayCutout对象返回的结果是null,其实这也不难理解,由于这时候是看不出刘海区域的,可是这样会致使在适配以前没法经过DisplayCutout判断是否存在刘海屏,只能在适配后才能获取到刘海区域信息,所以只能对于全部设备都添加适配代码。 那么接下来如何进行适配呢,Android P中增长了一个窗口布局参数属性layoutInDisplayCutoutMode,该属性有三个值能够取:
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT:默认的布局模式,仅当刘海区域彻底包含在状态栏之中时,才容许窗口延伸到刘海区域显示,也就是说,若是没有设置为全屏显示模式,就容许窗口延伸到刘海区域,不然不容许。 LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER:永远不容许窗口延伸到刘海区域。 LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES:始终容许窗口延伸到屏幕短边上的刘海区域,窗口永远不会延伸到屏幕长边上的刘海区域。
还有一个LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS模式,目前已经被LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES所取代,不容许使用了,这里就不提了。 这么看可能仍是有些不理解,接下来咱们在一个全屏显示的页面分别设置三种布局模式,看看有什么区别。
public class FullScreenActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
WindowManager.LayoutParams lp = getWindow().getAttributes();
// 仅当缺口区域彻底包含在状态栏之中时,才容许窗口延伸到刘海区域显示
// lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
// 永远不容许窗口延伸到刘海区域
// lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
// 始终容许窗口延伸到屏幕短边上的刘海区域
lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
getWindow().setAttributes(lp);
}
}
}
复制代码
三种模式下的显示效果以下图所示:
LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
能够看出,当在全屏显示状况下,LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT和LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER的效果是同样的,都是在状态栏显示一条黑边,也就是不容许窗口布局延伸到刘海区域,而LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES则容许窗口布局延伸到了刘海区域,这里须要注意是短边刘海区域,不过通常市面上的手机刘海区域都是在短边上的,我是没见过刘海长在“腰”上的,所以利用这个模式就实现适配了。 经过以前沉浸式状态栏的显示效果能够看出,LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT在此时是容许窗口布局延伸到刘海区域的,所以更证明了只有在全屏显示的状况下该模式才不容许窗口布局延伸到刘海区域。 适配后效果以下,如今看起来就很舒服了:
我这里为了简单没有添加任何控件,实际开发中在全屏显示后咱们仍然须要考虑刘海区域是否会遮挡显示的内容和控件,一样须要避开危险区域来显示。作法和沉浸式状态栏的适配相同,原理一样是将布局下移,预留出状态栏的高度,这里就不一一列举了。
目前市面上的刘海屏手机能够说是琳琅满目,各大厂商都在追求极致的屏占比,推出的新机型也基本上都有刘海屏,针对Android P如下的手机,咱们只能依照各个厂商提供的适配方案来进行适配。我也查阅了网上的一些适配文章,主要仍是针对目前主流的手机品牌,本文总结了华为、小米、Vivo和Oppo的适配方案,其余品牌的手机以后有时间的话可能会再考虑。
华为官方提供的适配文档:华为刘海屏手机安卓O版本适配指导 文档中提供了不少刘海屏相关的方法,这里就不一一列举了,着重看一下咱们须要用到的方法。 判断是否有刘海屏
/**
* 判断是否有刘海屏
*
* @param context
* @return true:有刘海屏;false:没有刘海屏
*/
public static boolean hasNotch(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("test", "hasNotchInScreen ClassNotFoundException");
} catch (NoSuchMethodException e) {
Log.e("test", "hasNotchInScreen NoSuchMethodException");
} catch (Exception e) {
Log.e("test", "hasNotchInScreen Exception");
} finally {
return ret;
}
}
复制代码
应用页面设置使用刘海区显示 官方提供了两种适配方案: 方案一.使用新增的meta-data属性android.notch_support,在应用的AndroidManifest.xml中增长meta-data属性,此属性不只能够针对Application生效,也能够对Activity配置生效。 使用方式以下:
<meta-data android:name="android.notch_support" android:value="true"/>
复制代码
能够在Application下添加,意味着该应用的全部页面,系统都不会作竖屏场景的特殊下移或者是横屏场景的右移特殊处理。
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<meta-data
android:name="android.notch_support"
android:value="true" />
...
</application>
复制代码
也能够针对指定的Activity添加,意味着能够针对单个页面进行刘海屏适配,设置了该属性的Activity系统将不会作特殊处理。
<!-- 全屏显示页面 -->
<activity
android:name=".ui.FullScreenActivity"
android:screenOrientation="portrait"
android:theme="@style/FullScreenTheme">
<meta-data
android:name="android.notch_support"
android:value="true" />
</activity>
复制代码
方案二.使用给window添加新增的FLAG_NOTCH_SUPPORT 代码以下:
/**
* 设置应用窗口在刘海屏手机使用刘海区
* <p>
* 经过添加窗口FLAG的方式设置页面使用刘海区显示
*
* @param window 应用页面window对象
*/
public static void setFullScreenWindowLayoutInDisplayCutout(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(WindowManager.LayoutParams.class);
Object layoutParamsExObj = con.newInstance(layoutParams);
Method method = layoutParamsExCls.getMethod("addHwFlags", int.class);
method.invoke(layoutParamsExObj, FLAG_NOTCH_SUPPORT);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException
| InvocationTargetException e) {
Log.e("test", "hw add notch screen flag api error");
} catch (Exception e) {
Log.e("test", "other Exception");
}
}
复制代码
官方提供的全部方法我已经放到了工具类HwNotchUtils里,能够根据需求来使用。
小米官方提供的适配文档:dev.mi.com/console/doc…
咱们一样看一下关键方法。 判断是否有刘海屏
/**
* 判断是否有刘海屏
*
* @param context
* @return true:有刘海屏;false:没有刘海屏
*/
public static boolean hasNotch(Context context) {
boolean ret = false;
try {
ClassLoader cl = context.getClassLoader();
Class SystemProperties = cl.loadClass("android.os.SystemProperties");
Method get = SystemProperties.getMethod("getInt", String.class, int.class);
ret = (Integer) get.invoke(SystemProperties, "ro.miui.notch", 0) == 1;
} catch (Exception e) {
e.printStackTrace();
} finally {
return ret;
}
}
复制代码
应用页面设置使用刘海区显示 小米提供的适配方案一样有两种(meta-data和Flag),使用方法和华为相似。 方案一.Application级别的控制接口 在 Application 下增长一个 meta-data,用以声明该应用窗口是否能够延伸到状态栏。
<meta-data
android:name="notch.config"
android:value="portrait|landscape"/>
复制代码
其中,value的值能够是如下四种
"none" 横竖屏都不绘制耳朵区
"portrait" 竖屏绘制到耳朵区
"landscape" 横屏绘制到耳朵区
"portrait|landscape" 横竖屏都绘制到耳朵区
复制代码
这里的耳朵区指的就是刘海区两侧的状态栏区域
虽然官方文档上说的是Application级别的,可是我以为也能够针对某一个Activity来配置,不过因为手头上的手机条件不知足,我并无验证,若是有小伙伴测试过的话能够反馈一下,我再修正一下这里的说法。 方案二.Window级别的控制接口 经过给Window添加Flag也能够实现将窗口布局延伸到状态栏中显示。
/*刘海屏全屏显示FLAG*/
public static final int FLAG_NOTCH_SUPPORT = 0x00000100; // 开启配置
public static final int FLAG_NOTCH_PORTRAIT = 0x00000200; // 竖屏配置
public static final int FLAG_NOTCH_HORIZONTAL = 0x00000400; // 横屏配置
/**
* 设置应用窗口在刘海屏手机使用刘海区
* <p>
* 经过添加窗口FLAG的方式设置页面使用刘海区显示
*
* @param window 应用页面window对象
*/
public static void setFullScreenWindowLayoutInDisplayCutout(Window window) {
// 竖屏绘制到耳朵区
int flag = FLAG_NOTCH_SUPPORT | FLAG_NOTCH_PORTRAIT;
try {
Method method = Window.class.getMethod("addExtraFlags",
int.class);
method.invoke(window, flag);
} catch (Exception e) {
Log.e("test", "addExtraFlags not found.");
}
}
复制代码
官方提供的全部方法我已经放到了工具类XiaomiNotchUtils里,能够根据需求来使用。 这里说一下个人测试状况,我是用小米8测试的,系统版本已经升到了Android P,利用小米官方提供的适配方法没有效果,只能用谷歌官方针对Android P的适配方案,这一点小米的官方文档也提到了。
至于Android P如下版本的小米手机,我并无测试,若是有哪位大佬测试过了发现有问题能够反馈一下。
Vivo官方提供的适配文档:Vivo全面屏应用适配指南 Oppo官方提供的适配文档:Oppo凹形屏适配指南 这里把Vivo和Oppo放在一块儿说,官方提供的资料不像华为和小米那么详细,只是提供了判断是否有刘海屏的方法。 Vivo判断是否有刘海屏
public static final int VIVO_NOTCH = 0x00000020; // 是否有刘海
public static final int VIVO_FILLET = 0x00000008; // 是否有圆角
/**
* 判断是否有刘海屏
*
* @param context
* @return true:有刘海屏;false:没有刘海屏
*/
public static boolean hasNotch(Context context) {
boolean ret = false;
try {
ClassLoader classLoader = context.getClassLoader();
Class FtFeature = classLoader.loadClass("android.util.FtFeature");
Method method = FtFeature.getMethod("isFeatureSupport", int.class);
ret = (boolean) method.invoke(FtFeature, VIVO_NOTCH);
} catch (ClassNotFoundException e) {
Log.e("Notch", "hasNotchAtVivo ClassNotFoundException");
} catch (NoSuchMethodException e) {
Log.e("Notch", "hasNotchAtVivo NoSuchMethodException");
} catch (Exception e) {
Log.e("Notch", "hasNotchAtVivo Exception");
} finally {
return ret;
}
}
复制代码
Oppo判断是否有刘海屏
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
// Android P利用官方提供的API适配
WindowManager.LayoutParams lp = getWindow().getAttributes();
// 始终容许窗口延伸到屏幕短边上的缺口区域
lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
getWindow().setAttributes(lp);
} else {
// Android P如下根据手机厂商的适配方案进行适配
if (RomUtils.isHuawei() && HwNotchUtils.hasNotch(this)) {
HwNotchUtils.setFullScreenWindowLayoutInDisplayCutout(getWindow());
} else if (RomUtils.isXiaomi() && XiaomiNotchUtils.hasNotch(this)) {
XiaomiNotchUtils.setFullScreenWindowLayoutInDisplayCutout(getWindow());
}
}
}
复制代码
至于全屏显示的适配方案,经过阅读官方文档和网上的其余适配文章,我我的总结一下就是这两种品牌的手机在设置全屏显示时都无需作任何处理(前提是适配了全面屏,上文中提到过如何配置),也就是不会产生黑边,咱们只须要避免布局中的内容或控件不被刘海区域所遮挡就能够了。具体的作法和沉浸式状态栏的适配相同,基本原理仍是将窗口布局下移,预留出状态栏的高度。 注:因为手头没有这两种厂商的手机,所以并无验证,这一点确实是我作得不够严谨,有好心的大佬验证以后欢迎指正。 其实我原本也想列出魅族的适配方案的,可是实在是没找到官方文档。。。若是有知道的大佬能够提供一下,我后面会把适配方案补上。 适配时的基本逻辑就是先判断手机的品牌,这里我利用了一个开源工具类项目AndroidUtilCode,提供了一个获取手机Rom信息的工具类RomUtils,用起来很方便,而后判断是不是刘海屏,针对刘海屏手机添加适配代码。完整的适配代码以下所示:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
// Android P利用官方提供的API适配
WindowManager.LayoutParams lp = getWindow().getAttributes();
// 始终容许窗口延伸到屏幕短边上的缺口区域
lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
getWindow().setAttributes(lp);
} else {
// Android P如下根据手机厂商的适配方案进行适配
if (RomUtils.isHuawei() && HwNotchUtils.hasNotch(this)) {
HwNotchUtils.setFullScreenWindowLayoutInDisplayCutout(getWindow());
} else if (RomUtils.isXiaomi() && XiaomiNotchUtils.hasNotch(this)) {
XiaomiNotchUtils.setFullScreenWindowLayoutInDisplayCutout(getWindow());
}
}
复制代码
虽然文中介绍了不少适配的内容,但其实在开发中须要咱们适配刘海屏的状况并很少,只有两种状况须要咱们进行考虑: 1.沉浸式状态栏,窗口布局延伸到了状态栏中,是否会遮挡必要的内容或控件(处在危险区域)。适配方案就是将窗口布局下移,预留出状态栏的空间。 2.全屏显示模式,不作适配的话状态栏会呈现一条黑边。适配方案是首先判断系统版本,是Android P及以上就按照官方的API来适配,不然根据手机厂商的适配方案进行适配。鉴于目前市面上Android P尚未普及,为了带来更好的用户体验,咱们仍是须要多花一些精力来适配各个手机厂商的刘海屏手机。 最后提示一下,本文只列出了四个当下主流手机厂商的适配方案,我本身验证过的只有华为和小米(只验证了Android P)的方案,对于Vivo和Oppo的一些结论我可能说得不对,欢迎你们指正。固然,若是你们还须要其余厂商的适配方案,也欢迎提出,我会尽力补上。 相关的代码和工具类我已经上传到了github,能够下载Demo来查看,你们一块儿交流