概要
Metarial Design是2014年Google IO的一个重点,在过去的两年时光里,越来越多的公司已经开始认可MD设计规范。在dribbble上可以越来越多的设计师开始投入到MD设计实践中,MD设计规范终于有底气可以和IOS的设计规范对抗啦Android程序员可以很叫嚣滴告诉设计师这就是Android的设计规范。
很少写Material Design的东西,今天趁着手热,在Material化财经APP的时候,看了些透明状态栏/沉浸式状态栏的东西,觉得自己可能还有很多不足之处只是希望能分享出来,一方面是自己的学习成果,另一方面是希望大家指正自己在理解的不到位或者错误。
先放一张我的五儿子手机原生的短信截图:
从上到下,一次是状态栏,内容View,导航栏(有些机器上不一定有导航栏这个虚拟栏),可以看到状态栏是彩色的,不是以前那种黑乎乎的状态栏,现在你也可以定制自己的状态栏了。
现在我们就来看下我手机上装的APP的一些截图:
<span id="pic2">
</span>
这个五个应用分别是网易新闻、豌豆荚、微信、小米天气、小气天气。
关于这个状态栏变色到底叫「Immersive Mode」/「Translucent Bars」有兴趣可以去 为什么在国内会有很多用户把 「透明栏」(Translucent Bars)称作 「沉浸式顶栏」?以及何为沉浸模式,沉浸式顶栏,变色龙状态栏,这个历史原因我们就没法去说大家错误,所有就错说错有了沉浸式状态栏一说。
在5.0之后我们是可以通过v7包下的Theme.AppCompat
一些列主题为APP的页面设置Activity的的状态栏,但是为了兼容低版本的我们放弃这种方式。为了能定制自己的状态栏,我们可以手动设置状态栏的颜色,可以随时改变状态栏的透明度等,所以我们需要自己来定制一套可行的方案。
实现
准备
分类
全屏模式和着色模式
根据内容延伸的角度可以分为两类:全屏模式和着色模式。其中,图2 中前三个页面都是状态栏固定在上方,无论下面怎么滑动,内部View都是那一块固定的大小区域滑动,由于状态栏是固定在一定位置且有着色,我们称之为着色模式,而图2中后两个页面截图中,内容View和状态栏像放在一个FrameLayout
一样,是层叠关系并且状态栏是透明,内容View可以延伸到状态栏,我们称之为全屏模式。透明状态栏和彩色状态栏
根据状态栏的颜色,可以分为状态栏透明和不透明(网易新闻、小米天气都是透明,而豌豆荚、wechat都是不透明)。
以上两种分为是不同维度的,从不同维度来看可以组合如下图:
setFitsSystemWindows
在android doc是这么描述的:
void setFitsSystemWindows (boolean fitSystemWindows)
Sets whether or not this view should account for system screen decorations such as the status bar and inset its content; that is, controlling whether the default implementation of fitSystemWindows(Rect) will be executed. See that method for more details.
Note that if you are providing your own implementation of fitSystemWindows(Rect), then there is no need to set this flag to true -- your implementation will be overriding the default implementation that checks this flag.
大概意思是:setFitsSystemWindows
用来设置影响系统的工具栏如状态栏,决定了这个view是否插入到它的ContentView。当您设置了fitSystemWindows(Rect)而没有将setFitsSystemWindows
设置为true,你对fitSystemWindows(Rect)设置是无效的.
该属性可以设置是否为系统 View 预留出空间, 当设置为 true 时,会预留出状态栏的空间。
由于这个实在API14(4.0)之后的函数,为了兼容低版本,才有V4里面的
ViewCompat.setFitsSystemWindows(rootView,false);
透明状态栏
由于Android 4.4才加入透明状态栏,Android 5.0之后可以直接设置状态栏和导航栏,而不是之前的黑乎乎的状态栏,但是,在4.4以下的系统上,想自己定义状态栏就无能为力了。所以,我们将Android 4.4和Android5.0视为边界。
Android 4.4设置透明状态栏
设置方法有两种:
代码
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// 设置状态栏透明
activity.getWindow()
.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
XML
在values-v19文件夹下,为activity的Style 添加一个属性:
<resources>
<style name="AppTheme" parent="@style/BaseAppTheme">
<item name="android:windowTranslucentStatus">true</item>
</style>
</resources>
5.0+透明状态栏
不过对于5.0系统,上面的设置后的结果可能不是透明哦(在原生机器是不透明的,但是在小米的是透明的,估计MIUI做了一些优化工作)
若是要完全透明,就需要看额外处理,在内容延伸到状态栏一节有介绍。
当然了你也可以用5.0的setStatusBarColor
<span id="5status">全屏模式的透明状态栏</span>
Window window = activity.getWindow();
//设置透明状态栏,这样才能让 ContentView 向上
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
//需要设置这个 flag 才能调用 setStatusBarColor 来设置状态栏颜色
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
//设置状态栏颜色
window.setStatusBarColor(statusColor);
//为了设置全屏
ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT);
View mChildView = mContentView.getChildAt(0);
if (mChildView != null) {
//注意不是设置 ContentView 的 FitsSystemWindows, 而是设置 ContentView 的第一个子 View . 使其不为系统 View 预留空间.
ViewCompat.setFitsSystemWindows(mChildView, false);
}
ViewCompat.setFitsSystemWindows(mChildView, false)中的第二个参数设置为false就是全屏模式,而设置成true。像上述实例中, ViewCompat.setFitsSystemWindows(mChildView, false)就是说mChildView可以直接延伸到phoneWindow的顶部,相当于小米天气的那种效果。
彩色状态栏
有透明状态栏,就有彩色状态栏。
在5.0+设置状态栏很简单就参照全屏模式的透明状态栏中的代码做修改window.setStatusBarColor(int color)
,而对于Android 4.4--5.0的怎么办呢,Android4.4是提供setStatusBarColor
这个方法的。
我们就想到了在上面提到的全屏透明状态栏的基础上加一个和状态栏一样高度的空白View放到顶部。
好了说干就干:
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
//添加一个空白的view到手机屏幕的顶部
addStatusBarBehind(activity, color, statusBarAlpha);
public static void addStatusBarBehind(Activity activity, int color, int statusBarAlpha) {
ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
int count = decorView.getChildCount();
if (count > 0 && decorView.getChildAt(count - 1) instanceof StatusBarView) {
decorView.getChildAt(count - 1).setBackgroundColor(calculateStatusColor(color, statusBarAlpha));
} else {
StatusBarView statusView = createStatusBarView(activity, color, statusBarAlpha);
decorView.addView(statusView);
}
setRootView(activity);
}
全屏模式
全屏模式,内容延伸到状态栏类似与小米天气的APP,先考虑下怎么做?
答案:将内容移到状态栏下,并且状态栏背景透明.
4.4--5.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// 设置状态栏透明
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
// 设置根布局的参数
ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
ViewCompat.setFitsSystemWindows(rootView,false);
rootView.setClipToPadding(true);
}
其实就先将状态栏设置了透明activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
,而后可以直接设置内容的根View来直接设置ViewCompat.setFitsSystemWindows(rootView,false)
,这样就可以直接讲根rootView直接顶上去,和状态栏的顶部对齐。
5.0+
在Android 4.4上设置透明状态栏,在5.0上依然可以正常显示,利用5.0实现的全屏模式的透明状态栏不过状态栏实际上并不完全为透明色,会有些许灰色。一般情况下,这个使我们能接受的,如微信、QQ等都是状态栏颜色暗与下面的Toolbar的。
若是能通过Activity的Theme的colorPrimaryDark
设状态栏颜色颜色设置,也是可行的,但是若你must实现透明额状态栏,也只能出狠招了。
Window window = activity.getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS|
| WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN| View.SYSTEM_UI_FLAG_LAYOUT_STABLE); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
setSystemUiVisibility
这个可以参考Using Immersive Full-Screen Mode。
这样的组合,让我们可以按照自己的需求来定制自己的状态栏,随后会开源自己封装的开源库工供大家参考。