Android 状态栏与主题探究
前不久在写一个功能的时候,遇到一个添加引导页的需求,直接照搬了之前的引导页的实现方式,使用了 NewbieGuide 来实现,发现在 Activity 中引导页弹出时无法遮盖状态栏,除了状态栏外的部分才能正常被遮盖,所以探究下这个是什么原因导致的
项目中其他地方的遮罩可以完整的盖住整个手机屏幕,连同状态栏一起,所以先去看看这些地方是如何实现的,一共有两种:
- 布局中设置 android:fitsSystemWindows=”true” 的同时,使用了自定义的 TransLinearLayout
- 这个 TranslinearLayout 中的 onMeasure 方法中,加上了 WindowInsets 的 getSystemWindowInsetTop 取得的高度
而 WindowInsets 这里的 inset 的直译是插入物,可以理解为特定屏幕区域,WindowInsets 的三个成员变量 mSystemWindowInsets,mWindowDecorInsets,mStableInsets 表示了三种屏幕区域,而 SystemWindowInsets 系统窗口区域代表着整个屏幕窗口上,状态栏,导航栏,输入法等系统窗口占用的区域
这里的top表示的是顶部占位区域的宽度,即状态栏的宽度,长度默认是屏宽
- 这个 TranslinearLayout 中的 onMeasure 方法中,加上了 WindowInsets 的 getSystemWindowInsetTop 取得的高度
- 某些 Activity 的 Theme 中加上 windowTranslucentStatus=true 属性,或者在活动的 onCreate 代码中设置
1
<item name="android:windowTranslucentStatus">true</item>
至此明白了需要怎样去实现,解决了问题,但是从这里可以看出我对主题,状态栏相关的知识不够扎实,故对相关内容重新整理归纳一下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
}
setContentView(R.layout.ai_kline_act_kline_master)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
val titleBar = findViewById<ConstraintLayout>(R.id.ai_kline_master_title_bar)
var statusBarHeight = 60
if (ScreenUtil.getStatusBarHeight(this) != 0) {
statusBarHeight = ScreenUtil.getStatusBarHeight(this)
}
titleBar.setPadding(titleBar.paddingLeft, titleBar.paddingTop + statusBarHeight, titleBar.paddingRight, titleBar.paddingBottom)
val titleBarLayoutParams = titleBar.layoutParams
titleBarLayoutParams.height = titleBarLayoutParams.height + statusBarHeight
titleBar.layoutParams = titleBarLayoutParams
}
首先是主题相关的内容,Android 5.0 发布后,support-v7-appcompat 也更新到V21,增加了 ToolBar、recyclerview、cardview 等控件。Android 从 5.0 开始支持 Material Design 应用,可以通过修改各项参数来达到不同的主题效果,以下是各项参数的作用
- textColorPrimary 应用的主要文字颜色,ActionBar 的标题文字默认使用该颜色
- colorPrimary 应用的主要色调,actionBar 默认使用该颜色,Toolbar 导航栏的底色
- statusBarColor 状态栏颜色,默认使用 colorPrimaryDark
- colorPrimaryDark 应用的主要暗色调,statusBarColor 默认使用该颜色
- windowBackground 窗口背景颜色
- navigationBarColor 底部栏颜色
- colorAccent CheckBox,RadioButton,SwitchCompat 等一般控件的选中效果默认采用该颜色
- colorForeground 应用的前景色,ListView的分割线,switch 滑动区默认使用该颜色
- colorBackground 应用的背景色,popMenu 的背景默认使用该颜色
- colorControlNormal CheckBox,RadioButton,SwitchCompat 等默认状态的颜色。
- colorControlHighlight 控件按压时的色调
- colorControlActivated 控件选中时的颜色,默认使用 colorAccent
- colorButtonNormal 默认按钮的背景颜色
- editTextColor :默认 EditView 输入框字体的颜色。
- textColor Button,textView 的文字颜色
- textColorPrimaryDisableOnly RadioButton checkbox 等控件的文字
- colorSwitchThumbNormal switch thumbs 默认状态的颜色. (switch off)
1 |
|
又发现到 AppCompatActivity 中出现了 windowNoTitle 属性,前边不需要加 “android:”,那这两个有什么区别吗?
其实带 android 的是系统的,不带 android 是兼容样式的,就是 Theme.AppCompat 样式的,因为是 support 包里重新定义了这个属性,即 windowNoTitle 是 appcompat-v7 中的属性,在 appcompat-v7\res\values\values.xml 中定义了,可以打开 appcompat-v7\res\values\values.xml 搜索 AppCompatTheme,可以找到定义的 “windowNoTitle”、”windowActionBar” 等属性。
“windowNoTitle” 属性在代码中可以使用 R.attr.windowNoTitle 访问,
“android:windowNoTitle” 则需要使用 android.R.attr.windowNoTitle 访问。
使用 AppCompatActivity 时(Activity 必须使用 Theme.AppCompat 主题及其子主题),测试发现:
“android:windowActionBar” 属性在 AppCompatActivity 中不起作用
windowNoTitle = false 并且 android:windowNoTitle = false 时,会出现两个标题,位于下方的是 AppCompatActivity的标题栏
当windowNoTitle = false,windowActionBar = false 时,会报错: AppCompat does not support the current theme features
windowNoTitle = true 并且 android:windowNoTitle = true时 ,无标题
然后是状态栏的沉浸式实现的转变:
Android4.4(API 19)- Android 5.0(API 21): 这个阶段可以实现沉浸式,但是表现得还不是很好,实现方式为: 通过 FLAG_TRANSLUCENT_STATUS 设置状态栏为透明并且为全屏模式,然后通过添加一个与 StatusBar 一样大小的 View,将 View 的 background 设置为我们想要的颜色,或者是像上文那样动态设置一个 padding,从而来实现沉浸式
这里的 FLAG_TRANSLUCENT_STATUS 的含义是:设置状态栏透明,并且变为全屏模式,从注释中看到,当 window 的这个属性有效的时候,会自动设置 system ui visibility 的标志 SYSTEM_UI_FLAG_LAYOUT_STABLE 和 SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
Android 5.0(API 21)以上版本: 在Android 5.0 的时候,加入了一个重要的属性和方法 android:statusBarColor(对应方法为 setStatusBarColor),通过这个方法我们就可以轻松实现沉浸式
Android 6.0(API 23)以上版本:Android 6.0 以上的实现方式和 Android 5.0 +是一样,但是从 Android 6.0(API 23)开始,我们可以改状态栏的绘制模式,可以显示白色或浅黑色的内容和图标(除了魅族手机,魅族自家有做源码更改,6.0 以下就能实现)示例
1
2//6.0 以上可以设置状态栏的字体为黑色,使用下面代码可以打开亮色状态栏模式,实现黑色字体,白底的需求用这句 setStatusBarColor(Color.WHITE)
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR更新
同样的操作,在 Android 8.0 以上似乎并不能很好地实现,而是更符合 flag 原本的定义,FLAG_TRANSLUCENT_STATUS 其中的 TRANSLUCENT 的意思是半透明的意思,所以在 8.0 以上的时候,设置这个 FLAG 会让状态栏半透明,同时 SYSTEM_UI_FLAG_LAYOUT_STABLE(让应用的主体内容占用系统状态栏的空间)和SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN(全屏)也会生效,造成顶部和状态栏重叠的情况,同时有一条半透明的状态栏在上面,这样子的话,在 Android7.0 中可以完美实现的沉浸式,在 8.0 的时候还是会显示一条半透明状态栏,所以如果要实现沉浸式,设置 FLAG_TRANSLUCENT_STATUS 和 android:windowTranslucentStatus = true 其实并不是太可取,应该要动态改变状态栏的颜色来实现真正的沉浸式,可以设置状态栏颜色与下方控件颜色一致,或者在没有设置 FLAG_TRANSLUCENT_STATUS 而设置了 FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS 的同时,设置状态栏颜色为透明,因为设置 FLAG_TRANSLUCENT_STATUS 会使得状态栏默认为半透明颜色