改变状态栏背景颜色
2016-05-07 22:47
288 查看
Github Demo 链接: StatusBarCompat
参考文章:
由沉浸式状态栏引发的血案Translucent
System Bar 的最佳实践
该使用
fitsSystemWindows 了!
首先强调,对于状态栏的处理有两种不同的方式, 这里从Translucent
System Bar 的最佳实践直接盗了两张图做对比~.
全屏( ContentView 可以进入状态栏) | 非全屏 ( ContentView 与状态栏分离, 状态栏直接着色) |
---|---|
全屏模式: 左边图所示.
着色模式: 右边图所示.
ContentView:
activity.findViewById(Window.ID_ANDROID_CONTENT)获取的
View , 即
setContentView方法所设置的 View, 实质为
FrameLayout.
ContentParent:
ContentView的 parent , 实质为
LinearLayout.
ChildView:
ContentView的第一个子 View ,即布局文件中的 layout .
再介绍一下相关的函数:
fitsSystemWindows, 该属性可以设置是否为系统 View 预留出空间, 当设置为 true 时,会预留出状态栏的空间.
ContentView, 实质为
ContentFrameLayout,
但是重写了
dispatchFitSystemWindows方法, 所以对其设置
fitsSystemWindows无效.
ContentParent, 实质为
FitWindowsLinearLayout,
里面第一个 View 是
ViewStubCompat, 如果主题没有设置 title ,它就不会 inflate .第二个 View 就是
ContentView.
5.0以上的处理:
自5.0引入 Material Design ,状态栏对开发者更加直接,可以直接调用 setStatusBarColor来设置状态栏的颜色.
全屏模式:
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); }
着色模式:
Window window = activity.getWindow(); //取消设置透明状态栏,使 ContentView 内容不再覆盖状态栏 window.clearFlags(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, true); }
4.4-5.0的处理:
4.4-5.0因为没有直接的 API 可以调用,需要自己兼容处理,网上的解决方法基本都是创建一下高度为状态栏的 View ,通过设置这个 View 的背景色来模拟状态栏. 这里我尝试了三种方法来兼容处理.方法1: 向
ContentView添加假 View ,
设置
ChildView的 marginTop 属性来模拟 fitsSystemWindows .
全屏模式:
Window window = activity.getWindow(); ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT); //首先使 ChildView 不预留空间 View mChildView = mContentView.getChildAt(0); if (mChildView != null) { ViewCompat.setFitsSystemWindows(mChildView, false); } int statusBarHeight = getStatusBarHeight(activity); //需要设置这个 flag 才能设置状态栏 window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); //避免多次调用该方法时,多次移除了 View if (mChildView != null && mChildView.getLayoutParams() != null && mChildView.getLayoutParams().height == statusBarHeight) { //移除假的 View. mContentView.removeView(mChildView); mChildView = mContentView.getChildAt(0); } if (mChildView != null) { FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mChildView.getLayoutParams(); //清除 ChildView 的 marginTop 属性 if (lp != null && lp.topMargin >= statusBarHeight) { lp.topMargin -= statusBarHeight; mChildView.setLayoutParams(lp); } }
着色模式:
Window window = activity.getWindow(); ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT); //First translucent status bar. window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); int statusBarHeight = getStatusBarHeight(activity); View mChildView = mContentView.getChildAt(0); if (mChildView != null) { FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mChildView.getLayoutParams(); //如果已经为 ChildView 设置过了 marginTop, 再次调用时直接跳过 if (lp != null && lp.topMargin < statusBarHeight && lp.height != statusBarHeight) { //不预留系统空间 ViewCompat.setFitsSystemWindows(mChildView, false); lp.topMargin += statusBarHeight; mChildView.setLayoutParams(lp); } } View statusBarView = mContentView.getChildAt(0); if (statusBarView != null && statusBarView.getLayoutParams() != null && statusBarView.getLayoutParams().height == statusBarHeight) { //避免重复调用时多次添加 View statusBarView.setBackgroundColor(statusColor); return; } statusBarView = new View(activity); ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, statusBarHeight); statusBarView.setBackgroundColor(statusColor); //向 ContentView 中添加假 View mContentView.addView(statusBarView, 0, lp);
方法2: 向
ContentParent添加假 View
,设置
ContentView和
ChildView的
fitsSystemWindows.
全屏模式:
Window window = activity.getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT); ViewGroup mContentParent = (ViewGroup) mContentView.getParent(); View statusBarView = mContentParent.getChildAt(0); if (statusBarView != null && statusBarView.getLayoutParams() != null && statusBarView.getLayoutParams().height == getStatusBarHeight(activity)) { //移除假的 View mContentParent.removeView(statusBarView); } //ContentView 不预留空间 if (mContentParent.getChildAt(0) != null) { ViewCompat.setFitsSystemWindows(mContentParent.getChildAt(0), false); } //ChildView 不预留空间 View mChildView = mContentView.getChildAt(0); if (mChildView != null) { ViewCompat.setFitsSystemWindows(mChildView, false); }
着色模式(会有一条黑线,无法解决):
Window window = activity.getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT); ViewGroup mContentParent = (ViewGroup) mContentView.getParent(); View statusBarView = mContentParent.getChildAt(0); if (statusBarView != null && statusBarView.getLayoutParams() != null && statusBarView.getLayoutParams().height == getStatusBarHeight(activity)) { //避免重复调用时多次添加 View statusBarView.setBackgroundColor(statusColor); return; } //创建一个假的 View, 并添加到 ContentParent statusBarView = new View(activity); ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity)); statusBarView.setBackgroundColor(statusColor); mContentParent.addView(statusBarView, 0, lp); //ChildView 不需要预留系统空间 View mChildView = mContentView.getChildAt(0); if (mChildView != null) { ViewCompat.setFitsSystemWindows(mChildView, false); }
方法3:向
ContentView添加假 View , 设置
ChildView的
fitsSystemWindows.
全屏模式:
Window window = activity.getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT); View statusBarView = mContentView.getChildAt(0); //移除假的 View if (statusBarView != null && statusBarView.getLayoutParams() != null && statusBarView.getLayoutParams().height == getStatusBarHeight(activity)) { mContentView.removeView(statusBarView); } //不预留空间 if (mContentView.getChildAt(0) != null) { ViewCompat.setFitsSystemWindows(mContentView.getChildAt(0), false); }
着色模式:
Window window = activity.getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); ViewGroup mContentView = (ViewGroup) activity.findViewById(Window.ID_ANDROID_CONTENT); int statusBarHeight = getStatusBarHeight(activity); View mTopView = mContentView.getChildAt(0); if (mTopView != null && mTopView.getLayoutParams() != null && mTopView.getLayoutParams().height == statusBarHeight) { //避免重复添加 View mTopView.setBackgroundColor(statusColor); return; } //使 ChildView 预留空间 if (mTopView != null) { ViewCompat.setFitsSystemWindows(mTopView, true); } //添加假 View mTopView = new View(activity); ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, statusBarHeight); mTopView.setBackgroundColor(statusColor); mContentView.addView(mTopView, 0, lp);
其实全屏模式在三种模式下实现都是一样的,主要是着色模式实现不同.
对比一下三种着色模式实现的方式:
方法1 | 方法2 | 方法3 | |
---|---|---|---|
原理 | 向 ContentView中添加假 View, 然后利用 ChildView的 marginTop 属性来模拟 fitsSystemWindows,主要是通过修改 marginTop 的值可以在全屏模式和着色模式之间切换. | 因为 ParentView的实质是一个 LinearLayout, 可以再其顶部添加 View . | 向 ContentView中添加假 View, 然后利用 ChildView的 fitsSystemWindows属性来控制位置, 但是实现缺陷就是不能随时切换两种模式. |
缺陷 | 改变了 ChildView的 marginTop 值 | 着色模式下,会像由沉浸式状态栏引发的血案中一样出现一条黑线 | 不能在不重启 Activity 的情况下切换模式. |
对应 Github demo 中代码 | StatusBarCompat类 | StatusBarCompat1类 | StatusBarCompat2 类 |
总结
StatusBarCompat2主要问题不能切换.
StatusBarCompat1在4.4上会有一条黑线, 如果可以解决我觉得这是最靠谱的解决方法.
StatusBarCompat类算是我最后给出的解决方案吧, 目前使用效果比较完善.推荐使用
用户可以随时在同一个 Activity 中切换不同的状态栏模式.
就算子 View 重写了
dispatchFitSystemWindows也不会有影响.
相关文章推荐
- C++ Note
- 单链表中重复元素的删除
- 第91讲:sparkStreaming基于kafka的Direct详解
- JavaWeb学习笔记——JavaBean的保存范围和删除
- Java多线程编程4--Lock的实例--实现生产者/消费者模式:一对一、多对多交替打印
- 20150309+Linux+LAMP安装-02
- iOS开发者必看的全球100个最佳博客
- 可控制导航下拉方向的jQuery下拉菜单代码
- (转)vivoxshot 精英版三模转五模模式切换操作方法
- mysql limit分页优化方法
- 工厂模式—选择器
- Codeforces Beta Round #2 A map B dp C 模拟退火
- poj 3461 KMP模式串匹配
- 【2010集训队出题】小Z的袜子
- 从频率到意义:语义向量空间模型(4)(From Frequency to Meaning: Vector Space Models of Semantics)
- fts:路径 XXX 已在工作区 XXX;XXX 中映射
- 03-nginx负载均衡
- poj 1673 EXOCENTER OF A TRIANGLE(求三角形垂心)
- poj2752 扩展kmp
- 88. Merge Sorted Array