沉浸式系列总结

前言

关于适配一直是Android开发者头疼的问题,恰好这段时间遇到了这个问题,决定仔细的学习一下(疯狂百度学习)。android

关于沉浸式

简介

关于沉浸式,其实在安卓里面并非沉浸式,只是对状态栏和虚拟导航栏的透明化操做而已,可是市面上你们都这么叫,因此就成为了沉浸式导航栏了。(本文测试皆为GoogleAPI的版本模拟器,所有亲身测试过)bash

看了许多的博客和代码,发现这个东西还真是够复杂的,由于安卓太多的厂商有太多的ROM,以致于适配不过来。可是总的来讲,沉浸式分为四个部分:app

  • Android 4.4 (API 19) - Android 5.0 (API 21):这个阶段能够实现沉浸式,可是效果不是很好,主要的实现方式为:设置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) 以上版本:为啥要加这个阶段咧,由于这个阶段开始,咱们能够改状态栏的绘制模式,能够显示白色或浅黑色的内容和图标。(深色或者浅色)布局

关于状态栏的操做

Android 4.4 - Android 5.0 (API 19 -21)

这个阶段没有真正意义上的沉浸式,咱们只是将状态栏透明化了而已。学习

Activity时

没有设置沉浸式时: 测试

咱们设置状态栏为透明,而且设置一个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时

当界面为底部导航栏拥有多个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 (API 21)以上

从Android 5.0开始,安卓系统加入了一个比较重要的方法setStatusBarColor (对应属性:android:statusBarColor),经过这个方法,能够很轻松地实现沉浸式状态栏。

Activity时

未适配时

使用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);
复制代码

添加这个便可不透明化底部虚拟导航栏

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_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());
复制代码

这样子,就完美适配了,尝试过水滴屏(小米),刘海屏(小米)都没有啥问题

Android 6.0 (API 23) 以上

在这个阶段,其实和5.0时差很少,只不过多了对状态栏字色和图标的颜色的操做API,由于以前的沉浸式不能设置状态栏为浅色调,否则就会看的很别扭。

Activity时

咱们来看看浅色时的界面

是否是以为很别扭,这种状况在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之上同样的,因此就不用区分各类状况了。

关于setSystemUiVisibility方法中参数的意义

示例

咱们常常看到

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 自动隐藏状态栏和虚拟导航栏

关于setFlag方法中参数的意义

示例

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>
复制代码

最终封装

咱们从前到后,测试了许多效果,那么可不能够封装为一个工具类,针对性的适配,咱们来尝试下。

第一步:适配单个Activity

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);
 }
​
 }
 }
复制代码

第二步:适配Fragment

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);
 }
 }
复制代码

最终,我总结的是,咱们可使用工具类,可是,最终仍是须要本身应对部分适配不成功的问题,因此仍是须要本身去理一理,轮子虽好,可不要贪杯哦。

好了,这篇文章先到这里,后期有新的的再补充(客套话)。

相关文章
相关标签/搜索