「Android」沉浸式、透明状态栏全解析

总有一条蜿蜒在童话镇里梦幻的河 分隔了理想分隔现实
又在前方的山口汇合 川流不息扬起水花
又卷入一帘时光入水 让所有很久很久以前 都走到幸福结局的时刻又陌生
程序员的生活不止有代码,其实还有音乐~~~

前言

Androd4.4.4之后我们可以改变状态栏,去掉黑色的状态栏,使用户可以专注于App的界面。 “沉浸式状态栏”和 “透明状态栏”,“沉浸式”是目前国内最普遍的叫法,但是其本质并不是沉浸式,有兴趣的小伙伴可以自行去研究,这里我们姑且叫它 “沉浸式状态栏” 吧~

   “沉浸式”状态栏 和 “透明”状态栏并不是一个概念
   沉浸式状态栏是整个屏幕只有App的界面,状态栏是浮在上面的,无论它是否透明。
   透明状态栏是App的界面在状态栏的下面,但是状态栏是透明

下面是你需要了解的一些常识。

版本 API 代号 备注
Android 4.4.4 19 KITKAT (MIUI 6 和 FlyMe 4 属于该版本)
Android 5.0 21 Lollipop 棒棒糖
Android 6.0 23 M 。。

//获得当前手机的Android系统版本

Build.VERSION.SDK_INT

//获得Android系统各个版本

Build.VERSION_CODES.KITKAT

Build.VERSION_CODES.LOLLIPOP

Build.VERSION_CODES.M

三个阶段

1. Android 4.4.4 ~ Andoird 5.0阶段


Android4.4.4之后我们终于可以对状态做手脚了,嘿嘿~

可以通过FLAG_TRANSLUCENT_STATUS这个flag实现沉浸式代码如下:

WindowManager.LayoutParams winParams = window.getAttributes();
final int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
if (transparentStatusBar) {    
      winParams.flags |= bits;
} else {    
      winParams.flags &= ~bits;
}
window.setAttributes(winParams);

它实现的效果是一条从上到下的半透明渐变层,App界面占据整个屏幕
不同系统可能略有不同,后面在详述

「Android」沉浸式、透明状态栏全解析_第1张图片
Android4.4.4效果图.png
2. Android 5.0 ~ Android 6.0


Android 5.0 之后我们可以对状态栏进行着色,通过设置flag代码如下:

int flag = window.getDecorView().getSystemUiVisibility();
flag |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;    
window.setStatusBarColor(statusBarColor)
window.getDecorView().setSystemUiVisibility(flag);
「Android」沉浸式、透明状态栏全解析_第2张图片
Android5.0效果图.png

相比第一阶段,我们在Api21实现沉浸式状态栏的同时,可以自定义状态栏的颜色了
如果要实现透明效果只需要让 statusBarColor = Color.TRANSPARENT

Tip:

在Android 5.0 之后我们除了可以在代码中改变状态栏的颜色,还可以在XML中设置主题色,这种方式我们的App不属于沉浸式,在状态栏的下面



「Android」沉浸式、透明状态栏全解析_第3张图片
ThemeColors.png
2. Android 6.0以后


Api 23之后我们除了可以实现第一阶段和第二阶段的特性外,还可以改变状态栏图标颜色为黑色,这个效果对于沉浸式背景色为白色的页面是很有用的,状态的图标不至于看不清楚了,代码如下:

int flag = window.getDecorView().getSystemUiVisibility();
flag |=(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS 
| View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
window.setStatusBarColor(Color.TRANSPARENT);
window.getDecorView().setSystemUiVisibility(flag);
「Android」沉浸式、透明状态栏全解析_第4张图片
Android6.0效果图.png
总结

通过上面对三个阶段的分析,在Api 19 我们可以实现沉浸式, 在Api 21我们可以对状态栏进行着色,在Api 23之后我们可以对改变状态栏的颜色为黑色。有个规律可以方便我们记忆,每个阶段都比上一个阶段增加一个特性,我们对状态栏的可操作性越来越强了。

这里有一点需要提一下,阶段三的特性虽然在Android 6.0 之后才提供,
但是在MIUI 6 和 FlyMe 4 中就可以实现了,而且你用阶段三的
代码去实现亮Icon在小米和魅族手机中是没有效果的,所以在开发中我们需要单独去处理这两个系统,
贴一下处理代码:
/**
 * 改变小米的状态栏字体颜色为黑色, 要求MIUI6以上
*/
private void processMIUI(boolean lightStatusBar) throws Exception {    
    Class clazz = window.getClass();    
    int darkModeFlag;    
    Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");    
    Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");    
    darkModeFlag = field.getInt(layoutParams);    
    Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);    
    extraFlagField.invoke(window, lightStatusBar ? darkModeFlag : 0, darkModeFlag);
}
/** 
* 改变魅族的状态栏字体为黑色,要求FlyMe4以上 
*/
private void processFlyMe(boolean isLightStatusBar) throws Exception {    
    WindowManager.LayoutParams lp = window.getAttributes();    
    Class instance = Class.forName("android.view.WindowManager$LayoutParams");   
    int value = instance.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON").getInt(lp);   
    Field field = instance.getDeclaredField("meizuFlags");    
    field.setAccessible(true);    
    int origin = field.getInt(lp);    
    if (isLightStatusBar) {        
        field.set(lp, origin | value);    
    } else {        
        field.set(lp, (~value) & origin);    
    }
}

使用情景分析


在上文中我们分析了三个阶段的Api特性,那在我们项目中,我们为什么会用到沉浸式呢?在什么情况下会用到沉浸式呢?我总结了一下几点,同时结合我的demo给大家进行演示,请大家结合我的代码来看 「戳这里」

  • 改变状态栏颜色,使其与App主题色一致
  • 随页面颜色改变状态栏颜色,并且改变状态栏图标icon的颜色

以上的使用情况是可以结合使用的,下面我来分析下正确的使用姿势,我们只考虑Android4.4.4之后的系统,之前的我们是改变不了的

(1)第一种,也是我们在开发中首先要设置的就是在xml中设置各种主题色,这样我们解决了5.0以上的全屏效果,然后我们要单独处理4.4.4 ~5.0 之间的Android系统,需要实现沉浸式,我们可以有两种解决方案,方式A:动态改变Title的paddingTop为状态栏的高度

//详细代码请查看github上demo这里就不贴了
tvTitle = (TextView) findViewById(R.id.tv_title);
AsaSystemBar.from(this)
    //版本低于5.0时执行
    .setUseBelow(Build.VERSION_CODES.LOLLIPOP)      
    .setTransparentStatusBar(true)        
    .setActionbarView(tvTitle)        
    .setActionbarPadding(true)       
    .process();

方式B:添加一个View在状态栏的下面,通过设置View的颜色改变状态栏的颜色


「Android」沉浸式、透明状态栏全解析_第5张图片
添加View.png

但是这种方式,因为实现了沉浸,所以App界面占全屏,设置完View,Title被View挡住了,我们需要在xml中设置根布局的这个属性android:fitsSystemWindows="true"
至此这样我们解决了最基本的4.4.4~5.0的沉浸式全屏,5.0以上的假沉浸式全屏

「Android」沉浸式、透明状态栏全解析_第6张图片
Android4.4.4以上效果一致.gif

(2)接下来我们分析第二种使用情况,先看效果吧

Android6.0上的效果.gif

我们可以看到随页面变化状态栏图标变黑了,在白色的页面看的很清楚,再来一张Android5.0上的效果图

Android5.0上效果图.gif

上图的效果与Android4.4.4的效果一样就不贴图了

之前我们分析过只有在Android 6.0上才能改变状态栏图标的颜色,我们看到5.0上图标还是白色,如果背景是白色的,状态栏的图标就看清了,造成了很不好的用户体验,如果设计师给你这一需求你该怎么处理呢???全部加一个半透明的背景??
我们大概的思路是,针对不同的系统分别处理,6.0以上的系统还是让他变黑,6.0以下的设备就加一个半透明的蒙层。代码如下:

//需要在xml中设置android:fitsSystemWindows="true"
//处理6.0以上的设备
AsaSystemBar.from(SlideViewTintActivity.this)        
    .setTransparentStatusBar(true)        
    .setLightStatusBar(true)        
    .process();
//处理6.0以下的设备
AsaSystemBar.from(SlideViewTintActivity.this)        
    .setUseBelow(Build.VERSION_CODES.M)  //6.0以下使用        
    .setTransparentStatusBar(true)        
  .setStatusBarColor(ContextCompat.getColor(SlideViewTintActivity.this, R.color.alpha_50))        
  .process();

Android 6.0的效果图上面已经贴过了
来看下6.0以下的(注:一直到Android4.4.4效果一致)

增加半透明蒙层.gif

>

代码如下:

//添加view + padding的方式
//6.0以上依旧不做处理
AsaSystemBar.from(SlideViewTintActivity.this)        
    .setTransparentStatusBar(true)        
    .setLightStatusBar(true)     
    .setActionbarView(llFoo)    
    .setActionbarPadding(true)     
    .process();
//6.0以下的处理
AsaSystemBar.from(SlideViewTintActivity.this)
    .setUseBelow(Build.VERSION_CODES.M)                               
    .setTransparentStatusBar(true)           
    .setActionbarView(llFoo)         
    .addStatusBarView(true)                
    .setStatusBarColor(ContextCompat.getColor(SlideViewTintActivity.this, R.color.alpha))            
    .setActionbarPadding(true)           
    .process();   

细心的同学可能会发现,文字还是被状态栏挡住了,没有实现沉浸式,这里仅仅能对有固定标题的页面有效,因为下面的内容不会滚到标题栏上面去,包括6.0以上也是会被遮挡,因为我们用了android:fitsSystemWindows="true"

好接下来我们来实现不被遮挡的,完全沉浸,而且标题栏icon还是能看清的,以下讨论的依旧是6.0以下的

给布局设置android:fitsSystemWindows="true"还是有他的局限性的,不能全屏。
那么我们换另一种思路,给title设置padding 并且在xml中父布局设置 android:clipToPadding="true"不知道的小伙伴自行查资料~~~
这样我们的文字就可以滚动到状态栏下面啦,沉浸式搞定~

看下效果图(4.4.4上效果一致)

大功告成.gif

注意

我们设置paddingTop一定不要写在xml里,因为我们在代码里是进行了版本判断的,不同版本设置不同,如果你在4.4.4以下的系统安装软件,你会发现,Title上面无端多出来的一块。。。。

 除了设置paddingTop我们还可以设置marginTop,但是也一定要做好版本判断。
 在4.4.4~5.0Android系统上会有一个渐变的半透明蒙层,白色的Icon也是可以看见的,
 但是我们在这里依然为她们增加一个半透明的View的原因是为了兼容MIUI 6 因为在小米上面是透明的
(FlyMe 4上好像也是)至此,心好累。。。。
 -。-感觉Android有无尽的坑,好羡慕开发ios的同学·····

占坑


后续会补充Navigationbar的处理,再次表示心好累......

结束语

研究沉浸式状态的童鞋,相信通过以上的讲解,对沉浸式状态栏有了相对全面的了解,同时也足以应对大多数沉浸式状态栏的开发需求,那么我花时间写本文的目的也就达到了,表谢,嘿嘿~
上面的核心代码并没有贴出来,请童鞋「戳这里」其中AsaSystembar是本文的核心代码。可以直接拿来使用

研究本文最好是结合代码来研究,边实验边了解
还有为我女神做广告=。=


2016/12/1更新

今天到了魅族的测试机,发现魅族手机状态栏图标没有变暗。。。哪里出了问题,在官网的文档上发现,上面的方法是处理非Activity的情况的,下面贴下处理Activity的代码

if("Meizu".equals(android.os.Build.MANUFACTURER)){   
 //魅族手机    
    int oldVis =     window.getDecorView().getSystemUiVisibility();    
    int newVis = oldVis;    
    if (isLightStatusBar) {        
        newVis |= 0x00002000;   
    } else {       
        newVis &= 0x00002000;    
    }   
    if (newVis != oldVis) {        
          window.getDecorView().setSystemUiVisibility(newVis);        
          isUsePrivateApi = true;    
    }
}

「魅族官方文档」

路漫漫其修远兮,明天又该上班啦!!!

你可能感兴趣的:(「Android」沉浸式、透明状态栏全解析)