关于适配一直是Android开发者头疼的问题,恰好这段时间遇到了这个问题,决定仔细的学习一下(疯狂百度学习)。android
关于沉浸式,其实在安卓里面并非沉浸式,只是对状态栏和虚拟导航栏的透明化操做而已,可是市面上你们都这么叫,因此就成为了沉浸式导航栏了。(本文测试皆为GoogleAPI的版本模拟器,所有亲身测试过)bash
看了许多的博客和代码,发现这个东西还真是够复杂的,由于安卓太多的厂商有太多的ROM,以致于适配不过来。可是总的来讲,沉浸式分为四个部分:app
FLAG_TRANSLUCENT_STATUS
将状态栏设置为透明而且设置为全屏,而后添加一个和StatusBar 同样大小的View,将View 的 background 设置为咱们想要的颜色,从而来实现沉浸式。window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
复制代码
Android 5.0 (API 21)以上版本:在这个阶段,系统加入了一个重要的属性和方法:android:statusBarColor
(对应的方法为setStatusBarColor),经过这个方法咱们就能够实现沉浸式了。工具
Android 6.0 (API 23) 以上版本:为啥要加这个阶段咧,由于这个阶段开始,咱们能够改状态栏的绘制模式,能够显示白色或浅黑色的内容和图标。(深色或者浅色)布局
这个阶段没有真正意义上的沉浸式,咱们只是将状态栏透明化了而已。学习
没有设置沉浸式时: 测试
咱们设置状态栏为透明,而且设置一个Status大小的view:ui
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
复制代码
这里咱们须要解释一下:spa
当window的这个属性有效的时候,会自动设置 system ui visibility的标志
SYSTEM_UI_FLAG_LAYOUT_STABLE和SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
复制代码
咱们来看下效果:3d
能够看到,状态栏已经透明化了,而且全屏显示了,可是这样子啊,咱们的组件就直接显示到最上面去了。咱们还须要设置一个和状态栏同样大小的View去填充,那么,咱们能够这么实现
在布局文件中填充一个View比做状态栏
设置根布局的paddingTop 属性来填充状态栏
当界面为底部导航栏拥有多个Fragment时,咱们来看看适配的效果
没有适配时
我门使用上面的Activity的适配方法适配后。
能够看到,适配时成功的,可是,咱们须要把顶部填充一下。由于这个适配时针对顶部透明的。
没有适配时
使用如下方法适配时
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
//将侧边栏顶部延伸至status bar
drawer.setFitsSystemWindows(true);
//将主页面顶部延伸至status bar;虽默认为false,但经测试,DrawerLayout需显示设置
drawer.setClipToPadding(false);
复制代码
实现的效果
完美实现
那么,针对安卓4.4到5.0的适配方法,咱们能够封装为以下:
public class SimpleStatusBarAdapter {
/*
适配安卓4.4--5.0 测试手机 谷歌安卓模拟器
*/
public static void setAdapter(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
/*
适配安卓4.4--5.0 测试手机 谷歌安卓模拟器 将跟布局的paddingTop设置为状态栏的高度
*/
public static void setAdapter(Fragment fragment, View rootView, boolean headImage) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
if (!headImage) {
int resourceId = fragment.getActivity().getResources().getIdentifier("status_bar_height", "dimen", "android");
int height = fragment.getActivity().getResources().getDimensionPixelSize(resourceId);
rootView.setPadding(0, height, 0, 0);
}
}
}
/*
适配安卓4.4--5.0 测试手机 谷歌安卓模拟器 侧边栏为DrawerLayout时,设置这个属性实现沉浸式
*/
public static void setAdapter(DrawerLayout drawer) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
//将侧边栏顶部延伸至status bar
drawer.setFitsSystemWindows(true);
//将主页面顶部延伸至status bar;虽默认为false,但经测试,DrawerLayout需显示设置
drawer.setClipToPadding(false);
}
}
}
复制代码
从Android 5.0开始,安卓系统加入了一个比较重要的方法setStatusBarColor
(对应属性:android:statusBarColor
),经过这个方法,能够很轻松地实现沉浸式状态栏。
未适配时
使用Android4.4时的适配方法的界面
能够看到,设置以后只是透明化了一点。可是仍是没有到理想的状态。
咱们开始使用API21以后的方法来实验一下:
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
//注意要清除 FLAG_TRANSLUCENT_STATUS flag
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
activity.getWindow().setStatusBarColor(getResources().getColor(R.color.status_color));
复制代码
这里须要解释一下的是:
想要这个方法生效,必须还要配合一个Flag一块儿使用,必须设置FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS ,
而且不能设置FLAG_TRANSLUCENT_STATUS(Android 4.4才用这个)。
复制代码
再来运行一下看下效果:
完美实现
也能够在Theme中使用
<style name="MDTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowTranslucentStatus">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@color/status_color</item>
</style>
复制代码
若是想将状态栏直接所有隐藏掉咱们能够这么设置
<style name="ImageTranslucentTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowTranslucentNavigation">true</item>
<item name="android:windowTranslucentStatus">true</item>
<!-- 设置statusBarColor 为透明-->
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
还有
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
activity.getWindow().setStatusBarColor(getResources().getColor(R.color.status_translution));
复制代码
咱们看看效果:
这里,咱们看到,底部的虚拟导航栏也被透明化了。
<item name="android:windowTranslucentNavigation">false</item>
复制代码
运行,,,结果和咱们想象的彻底不一样,状态栏也失效了。
可是,我找到了解决方法
activity.getWindow().getDecorView().
setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
复制代码
添加这个便可不透明化底部虚拟导航栏
咱们先来看看,彻底没有适配的时候
能够看到,状态栏彻底挡住了咱们的视图,咱们来设置上面的方式试试
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
//注意要清除 FLAG_TRANSLUCENT_STATUS flag
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
activity.getWindow().setStatusBarColor(getResources().getColor(R.color.status_color));
复制代码
而后咱们会看到,emmm
咱们把状态栏的颜色写死了,那么要适配动态的Fragment,咱们只能将状态栏直接所有隐藏掉,直接这么写
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
//注意要清除 FLAG_TRANSLUCENT_STATUS flag
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
activity.getWindow().setStatusBarColor(getResources().getColor(R.color.status_translution));
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
复制代码
而后,运行看看
能够看到,确实是实现了。可是,这里须要咱们适配顶部的高度
if (!headImage) {
int resourceId = fragment.getActivity().getResources().getIdentifier("status_bar_height", "dimen", "android");
int height = fragment.getActivity().getResources().getDimensionPixelSize(resourceId);
rootView.setPadding(0, height, 0, 0);
}
复制代码
咱们只须要实现一下方法便可完美实现沉浸式
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
// 注意要清除 FLAG_TRANSLUCENT_STATUS flag
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
activity.getWindow().setStatusBarColor(activity.getResources().getColor(R.color.status_translution));
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
toolbar.setFitsSystemWindows(true);
复制代码
如图:
今天发现上面的适配方法有个问题,遇到水滴屏就会变形。而后,我又试出了新的适配方法。
首先,在NavigationView中加入
app:insetForeground="@null"
复制代码
其余的照上面适配 而后加上
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
int height = context.getResources().getDimensionPixelSize(resourceId);
View headerView = navigationView.getHeaderView(0);
navigationView.setForeground(new BitmapDrawable());
coordinatorLayout.setPadding(0, height, 0, 0);
headerView.setPadding(headerView.getPaddingLeft(),headerView.getPaddingTop()+height,headerView.getPaddingRight(),headerView.getPaddingBottom());
coordinatorLayout.setBackground(toolbar.getBackground());
复制代码
这样子,就完美适配了,尝试过水滴屏(小米),刘海屏(小米)都没有啥问题
在这个阶段,其实和5.0时差很少,只不过多了对状态栏字色和图标的颜色的操做API,由于以前的沉浸式不能设置状态栏为浅色调,否则就会看的很别扭。
咱们来看看浅色时的界面
是否是以为很别扭,这种状况在6.0以前是没法解决的,在这以后,咱们就能够完美解决了。
咱们只须要在5.0的基础上加上
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
activity.getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
复制代码
看看效果
能够看到,完美实现。
另外,还能够在style中使用
<!-- Android 6.0以上 状态栏字色和图标为浅黑色-->
<item name="android:windowLightStatusBar">true</item>
复制代码
6.0版本之上跟5.0之上同样的,因此就不用区分各类状况了。
咱们常常看到
View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
复制代码
那么其中的View.SYSTEM_UI_FLAG_HIDE_NAVIGATION都是些啥咧?每次都是网上查一下,而后直接粘贴复制使用,也没多想,今天就来总结一下。
参数 | 意义 |
---|---|
SYSTEM_UI_FLAG_FULLSCREEN | 隐藏状态栏 |
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | 半透明状态栏 |
SYSTEM_UI_FLAG_HIDE_NAVIGATION | 隐藏虚拟导航栏 |
SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | 半透明虚拟导航栏,会自动设置半透明状态栏 |
SYSTEM_UI_FLAG_IMMERSIVE | 自动隐藏状态栏和虚拟导航栏 |
SYSTEM_UI_FLAG_IMMERSIVE_STIKY | 自动隐藏状态栏和虚拟导航栏 |
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
复制代码
类型 | 参数 | 意义 |
---|---|---|
int | FLAG_BLUR_BEHIND | 让该window后全部东西都模糊(blur) |
int | FLAG_DIM_BEHIND | 让该window后全部的东西都成暗淡(dim) |
int | FLAG_DITHER | 开启抖动(dithering) |
int | FLAG_FORCE_NOT_FULLSCREEN | 恢复window非全屏显示 |
int | FLAG_FULLSCREEN | 让window进行全屏显示 |
int | FLAG_KEEP_SCREEN_ON | 当该window对用户可见时,让设备屏幕处于高亮(bright)状态。 |
int | FLAG_LAYOUT_IN_SCREEN | 让window占满整个手机屏幕,不留任何边界(border) |
int | FLAG_LAYOUT_NO_LIMITS | window大小再也不不受手机屏幕大小限制,即window可能超出屏幕以外,这时部份内容在屏幕以外。 |
int | FLAG_SECURE | 当该window在进行显示的时候,不容许截屏。 |
咱们只须要将启动时的背景设置为透明,而后其余的正常适配便可,这样子的作法是适配瞒天过海的启动页
<item name="android:windowIsTranslucent">true</item>
复制代码
咱们从前到后,测试了许多效果,那么可不能够封装为一个工具类,针对性的适配,咱们来尝试下。
public static void setActivityAdapter(Activity activity,boolean isSetLightStatusBar) {
/*
适配安卓4.4--5.0
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
/*
适配安卓5.0以上
*/
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
// //注意要清除 FLAG_TRANSLUCENT_STATUS flag
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
/*
适配6.0以上状态栏文字颜色,设置为深色
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (isSetLightStatusBar)
activity.getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
}
}
复制代码
public static void setFragmentAdapter(Fragment fragment, View rootView, boolean headerIsImage) {
/*
适配安卓4.4--5.0
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
if (!headerIsImage) {
int resourceId = fragment.getActivity().getResources().getIdentifier("status_bar_height", "dimen", "android");
int height = fragment.getActivity().getResources().getDimensionPixelSize(resourceId);
rootView.setPadding(0, height, 0, 0);
}
}
/*
适配安卓5.0以上
*/
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
if (!headerIsImage) {
int resourceId = fragment.getActivity().getResources().getIdentifier("status_bar_height", "dimen", "android");
int height = fragment.getActivity().getResources().getDimensionPixelSize(resourceId);
rootView.setPadding(0, height, 0, 0);
}
}
}
复制代码
/*
toolbar.getBackground()
*/
public static void setNavigationViewAdapter(DrawerLayout drawer, Context context, NavigationView navigationView, View rootLayout, Drawable bg) {
/*
适配安卓4.4--5.0
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
//将侧边栏顶部延伸至status bar
drawer.setFitsSystemWindows(true);
//将主页面顶部延伸至status bar;虽默认为false,但经测试,DrawerLayout需显示设置
drawer.setClipToPadding(false);
}
/*
适配安卓5.0以上
*/
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
int height = context.getResources().getDimensionPixelSize(resourceId);
View headerView = navigationView.getHeaderView(0);
navigationView.setForeground(new BitmapDrawable());
rootLayout.setPadding(0, height, 0, 0);
headerView.setPadding(headerView.getPaddingLeft(),headerView.getPaddingTop()+height,headerView.getPaddingRight(),headerView.getPaddingBottom());
rootLayout.setBackground(bg);
}
}
复制代码
最终,我总结的是,咱们可使用工具类,可是,最终仍是须要本身应对部分适配不成功的问题,因此仍是须要本身去理一理,轮子虽好,可不要贪杯哦。
好了,这篇文章先到这里,后期有新的的再补充(客套话)。