其实这篇主要是讲解适配状态栏的,在这其中可能有些读者对状态栏(StatusBar)、ActionBar、ToolBar、TitleBar有点混淆或者感觉很混乱,所以就有了这其中的内容。
关于 TitleBar、ActionBar、ToolBar、StatusBar
首先强调一点 StatusBar 和前面这几种 Bar 是完全的两回事,其实 statusbar 应该写成 status bar ,也就是我们的状态栏。没错就表示我们 Android 中最上面显示时间、通知的那一栏。
说起 TitlBar、ActionBar、ToolBar,要和 Android 的发展历史有关了。
在 Android 3.0 之前,设计上没有美感之说,知识为了完成功能,这个时候最上面样式就是显示当前页面的 Title,也就是 TitleBar,这个时代的 Android 机都是有物理返回键的。这个时候就是充当一个 Title 的作用,这也就是 TitleBar。它的样子是这样的
titlebar.png
上面显示MyStudyDemo
的一栏就是 TitleBar 了。
然后在 Android 3.0 API 11 开始注重美感了,引进了 HOLO 样式,这个时候用 ActionBar代替了 TitleBar 了。这个时候的 Bar 的功能不再仅仅限于展示标题了,有了很多的新功能。
actionbar.png
Fragment 也是在 Android 3.0 的时候引入的,所以 Android 3.0 对于 Android 的发展史还是很重要的。
最简单的 ActionBar
简单ActionBar.png
值得注意的是 TitleBar 和 ActionBar 在本质上是一样的,他们都不是我们常用的控件的形式,而是绑定在 DecorView
中的,可以通过 getWindow().setFeatureInt()
暴露出的几个方法进行修改功能和样式。
正是因为 ActionBar 是嵌套在 DecorView 中的,它不是一个独立的控件,而且由于国内的 Android 应用开发环境,一般都不遵循 Google 的那一套设计,所以运用起来很不灵活。随后在 Android 5.0 推出了 ToolBar 控件,这是一个完全独立的控件,你可以尽情的运用了。
2019-04-25_20-44-48.png
好了,到这里来应该彻底分清楚这几个 bar 了吧,这是 Android 的一个历史发展形成的,可以结合我的这篇文章 https://blog.csdn.net/sydMobile/article/details/80164916 来详细的看一下 Android 在样式上的发展。
状态栏的历史以及更正错误叫法
因为上面也说了 ActionBar 是在 Android 3.0 以后才引用的,但是真正的规范是在 Android 4.1 以及以上。所以在 Android 4.1 以下版本是不支持 ActionBar 的。这里针对 ActionBar 的操作全是在 Android 4.1 以上运行的,不适用于 Android 4.1 以下版本。
说明:在 Android 4.4 之前状态栏一直就是黑色的,在 Android 4.4 中带来了 windowTranslucentStatus
这一特性,这个时候才可以给状态栏设置颜色。在国内将这种状态栏变色叫做沉浸式状态栏,其实这种叫法是错误的,但是时间久了,大家都这么叫了,就不追究了,就把这种状态栏变色叫做沉浸式状态栏了(这也是由国内互联网发展太过迅速,忽略了很多细节点,相关方面的人才缺乏,另一个方面是国内的 Android 开发环境造成的,碎片化太过严重,不同的手机厂商关于这些又有不同的叫法)。
关于沉浸式和透明式概念说明
在谷歌官方中:
在 Android 4.4 Google 引入了可以在阅读电子书、玩游戏、看电影时支持全屏模式(Immersive Mode 沉浸模式),同时也支持更改修改状态栏的颜色。
可以知道在官方是根本没有 沉浸式状态栏 这种说法的。只有 沉浸模式 就是其实就是出于全屏状态。所谓的 ”沉浸式状态栏“,类似于下面图的样子:
上面也说了这种错误的叫法是不对的,没有沉浸式状态栏 ,这种样式只是将内容 UI 设置成了全屏,把状态栏设置成了透明。所以这种是叫做 状态栏透明模式
设置透明状态栏
先来几组效果图,从效果 1 开始逐渐递进演示,这里面的几个重要参数会在后面具体说明,这里先说明现象
1.内容布局全屏
在 Android 4.1 以上设置去除状态栏或者认为是状态栏被内容布局遮挡了(全屏)和去除 ActionBar
代码语言:javascript复制View decorView = getWindow().getDecorView();
int option = View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(option);
ActionBar actionBar = getSupportActionBar();
actionBar.hide();
// 情况说明:采用这种方式全屏,如果你切换到别的应用程序,或者在通知栏下拉一下,状态栏就会下来,我们的 UI 布局就会跑到状态栏下面,和没有设置的时候效果是一样的。
效果图:
2.内容布局全屏(被状态栏遮住顶部)
代码语言:javascript复制View decorView = getWindow().getDecorView();
int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
decorView.setSystemUiVisibility(option);
ActionBar actionBar = getSupportActionBar();
actionBar.hide();
// 可以看到我们在 option 中添加了一个 STABLE 属性,这个属性是用阿里保持我们的 UI 视图稳定的,使得它不会因为系统 UI 的变化而重新 layout 。记住这个属性长和 FLAG_LAYOUT_XXX 这种属性在一起使用。STABLE 就是会始终给系统 UI 保留一个空间(不管系统 UI 有没有消失,并且悬浮在我们自己的 UI 视图上面 )
// 可以看到这种效果,状态栏仍然还在,只是你仔细发现,原布局有一部分被状态栏给覆盖了,也就是说这种方式的话,布局延伸到了状态栏。这个时候我们只要设置状态栏为透明。就是我们想要的透明状态栏的那种效果了。
效果图:
layout_fullscreen.png
3.由效果 2 变成透明状态栏模式
代码语言:javascript复制
4.属性 STABLE 的作用
代码语言:javascript复制// 主要是为了验证 STABLE 的作用,在 1 的案例中,我们只用 FLAG_FULLSCREEN 属性,会发现在切换应用或者触碰系统 UI 的时候,这个时候我们的状态栏就会出来,然后我们的 UI 布局就会“被压在”状态栏的下面(实际上是把我们的布局重新 layout 了)然后如果添加 STABLE 属性呢?
if (Build.VERSION.SDK_INT >= 21){
View decorView = getWindow().getDecorView();
int option = View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
decorView.setSystemUiVisibility(option);
getWindow().setStatusBarColor(Color.TRANSPARENT);
}
ActionBar actionBar = getSupportActionBar();
actionBar.hide();
// 结果就是 提前预留了一块系统 UI 的控件,下拉的时候,状态栏内容就会显示出来。(这样我们的 UI 布局是没有被重新 layout 的)
screnn_stable.png
5.沉浸模式
代码语言:javascript复制// 所谓沉浸模式就是一开始我们的 UI 布局是全屏的,状态栏和虚拟导航键也是隐藏的,当我们需要的系统 UI 的时候,从状态栏的位置下拉就可以出现系统 UI,这个时候就需要一个新的属性了 View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY 需要在 Android 4.4 及其以上版本
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus && Build.VERSION.SDK_INT >=19){
View decorView = getWindow().getDecorView();
decorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
);
}
}
// 当触摸底部或者上部状态栏的时候,导航栏和状态栏就会出来,过一会就会消失,类似于游戏中那样。
immersive_sticky_all.png
最后,总结这几个 FLAG 的作用
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN:作用就是使我们的 UI 布局可以延伸到状态栏,状态栏悬浮会遮挡住我们的 UI 布局,和 View.SYSTEM_UI_FLAG_LAYOUT_STABLE 一起使用
- View.SYSTEM_UI_FLAG_FULLSCREEN:作用使 UI 布局延伸到状态栏,全屏显示,状态栏消失,下拉的时候,状态栏依然会出现,并且不再消失。
- View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY:沉浸模式,配合 View.SYSTEM_UI_FLAG_FULLSCREEN 和 View.SYSTEM_UI_FLAG_HIDE_NAVIGATION 来实现
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION :作用就是使我们的 UI 布局可以延伸到导航栏,导航栏悬浮会遮挡住我们的 UI 布局。
- View.SYSTEM_UI_FLAG_HIDE_NAVIGATION:作用使 UI 布局延伸到导航栏,全屏显示,导航栏消失。单独使用的话,触碰 UI 任何地方,系统都会将这个属性设置去除,不再生效因此需要和 View.SYSTEM_UI_FLAG_IMMERSIVE 配合使用
- View.SYSTEM_UI_FLAG_IMMERSIVE:单独使用是没有任何意义的,需要和 View.SYSTEM_UI_FLAG_FULLSCREEN、View.SYSTEM_UI_FLAG_HIDE_NAVIGATION 配合使用,当出现隐藏的系统栏的时候,之前的所有设置就会被清除。
修改状态栏颜色
Android 5.0 (API 21) 后支持直接修改状态栏的颜色,在 Android 4.4(API 19)之前是不允许操作状态栏的,也就是说在 Android 4.4 之前,我们是没法对状态栏进行任何操作的。
Android 4.4 修改状态栏颜色
代码语言:javascript复制// 将状态栏设置为透明(需要 API 19) 设置成这种模式后,状态栏会变成透明,我们的内容布局(只是我们 Activity 对应的布局,不包含 Window 中的 ActionBar)会占据系统栏。类似于 getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | LAYOUT_STABLE); getWindow().setStatusBarColor(Color.TRANSPARENT);
// 首先设置状态栏为透明
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
// 这个时候,我们的布局内容中占据系统栏了,这个时候,我们只需要自己来创建一个系统栏背景就可以了。
View mStatusBarView = new View(context);
int screenWidth = 屏幕宽度;
int statusBarHeight = 状态栏高度(是可以获取的);
ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(screenWidth,statusBarHeight);
mStatusBarView.setLayoutParams(params);
mStatusBarView.requestLayout();
// 获取系统默认的根布局
ViewGroup systemContent = findViewById(android.R.id.content);
// 这个就是我们自己的布局文件
ViewGroup userContent = systemContent.getChildAt(0);
// 添加到我们的布局中
userContent.addView(mStatusBarView,0);
Android 5.0 修改状态栏颜色
代码语言:javascript复制// 方法一
getWindow().setStatusBarColor(int color );
// 方法二
样式中: <item name="colorPrimaryDark">@color/colorAccent</item> 对应状态栏颜色
修改状态栏文字颜色
关于状态栏的文字颜色,是在 Android 6.0 才开始可以支持修改的
代码语言:javascript复制// 修改成 黑色字体
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
setFitsSystemWindows
代码语言:javascript复制setFitsSystemWindows(boolean is);
在直接调用这个方法的时候,只有我们自己的根布局(在 mContentParentParent 中,titleBar 的位置是固定的),调用才起作用。表示是否保留系统栏的空间。可以在布局属性中设置。作用和给控件添加 "fitsSystemWindows" 属性相同。
如果设置了这个属性为 true,那么则是保留系统 UI 的位置(实际上是固定了我们的 UI 的高度,我们 UI 的高度就是屏幕去掉系统栏高度后的高度),那么这个时候你如何设置了 FLAG_LAYOUT_HIDE_NAVIGATION 是不起作用的,因为我们的布局高度已经确定了,不可能延伸到系统栏。
设置布局完全全屏的方式:
代码语言:javascript复制// 方式一
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
// 方式二
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);
// 方式三 style.xml 中配置
<style name = "fullScreen" parent="Theme">
<item name="android.windowFullscreen">true</item>
</style>
// 总结:
关于 addFlags 和改变 statusbar 颜色的方法在 getWindow 里面
页面布局
为了说明页面的布局关系,这里引入一张图片(来自网上)
Android页面来自网络.png
每个 Activity 对应一个页面,是不包括 status bar 的,不过可设置 status bar 显示还是不显示,可以设置 Activity 是否延伸到 status bar 的位置(确切的来说是 Window 窗口)
DecorView 是继承自 FrameLayout 的。
其中 mContentParent 的 id 是 com.android.internal.R.id.content
mContentParent 通过这个 id 就可以获取到我们自己的布局的根布局了。
titleBar 也有一个 id
相关文章:http://yifeng.studio/2017/02/19/android-statusbar/