很多时候我们会有改变状态栏颜色或者用背景图侵入状态栏的这种需求,所谓侵入式布局实际上就是将状态栏背景色改为透明然后将背景图从状态栏的顶部开始绘制,比如下图
废话不多说,我们从新建Activity开始一步一步来实现
Lollipop以上的设备
window.apply {
clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
statusBarColor = Color.TRANSPARENT
}
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
window到底是什么
当你打开一个StandardApp的时候,从上到下你会依次看到手机状态栏(StatusBar)、导航栏(NavigationBar)、然后是Activity的实际页面。它们每一个组件都有一个不同的Window用于它们自身的绘制。Activity的Window负责显示我们设计的视图,导航栏的Window负责绘制我们的菜单、返回键等等。状态栏的Window负责绘制我们的时间、电池信息、通知图标等等。所有这些Window都在同一个屏幕内被一个叫做WindowManager的东西管理着
window设置的那些Flag有什么用
FLAG_TRANSLUCENT_STATUS:如果这个被设置,状态栏会被设置为透明,然而Activity还是按照原来的情况从状态栏下方开始绘制,所以我们会看到一个灰色的状态栏被显示出来,这不是我们想要的,所以我们先移除了这个Flag。
FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS:如果这个被设置,系统会把Activity的Window用来绘制状态栏,所以我们需要添加这个Flag。
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN:这个Flag帮助我们在窗口进入\退出全屏模式而引起状态栏可见\隐藏而导致的窗口Resizing的时候保持元素位置。
setStatusBarColor():这个无需多解释,Android自带Api改变状态栏颜色,然而只能在Api21以上使用。
Api23以上的问题
在Api23以上的设备中状态栏文字、图标的默认颜色是白色,所以如果你的背景图也是亮色的话会导致看不清楚的问题。
解决方法:
设置SYSTEM_UI_FLAG_LIGHT_STATUS_BAR:它会让系统状态栏的颜色、图标适配亮色背景
适配页面元素
现在我们终于可以在绝大多数Android设备上实现侵入式背景布局了,然而现在还剩下一个问题:我们的页面元素也跟着背景一起进入到了状态栏里。所以我们应该把页面元素整体下移一个状态栏的高度,那么如何个下移呢?比如设一个MarginTop。那么,在各种类型的Android设备里,状态栏的分布和样式是千奇百怪的,那么如何获取这个高度就变成了我们要探讨的主要问题。
使用Window inset适配
Window insets 会给我们返回我们需要移动的距离,在这里也就是指状态栏高度。所以我们要移动的距离就是topWindowInset这个字段的值。
如何获取当前的Window inset
ViewCompat.setOnApplyWindowInsetsListener(
findViewById(R.id.content_container)) { _, insets ->
Log.d("topInset", insets.systemWindowInsetTop)
insets.consumeSystemWindowInsets()
}
ViewCompat.setOnApplyWindowInsetsListener(container, new OnApplyWindowInsetsListener() {
@Override
public WindowInsetsCompat onApplyWindowInsets(View view, WindowInsetsCompat windowInsetsCompat) {
Log.d("topInset", windowInsetsCompat.getSystemWindowInsetTop());
return windowInsetsCompat.consumeSystemWindowInsets();
}
});
拿到TopInset的值之后,把页面元素整体下移就可以了,下移的方法也贴出来
fun View.setMarginTop(marginTop: Int) {
val menuLayoutParams = this.layoutParams as ViewGroup.MarginLayoutParams
menuLayoutParams.setMargins(0, marginTop, 0, 0)
this.layoutParams = menuLayoutParams
}
public static void setMarginTop(View view, int marginTop){
ViewGroup.MarginLayoutParams menuLayoutParams =
(ViewGroup.MarginLayoutParams)view.getLayoutParams();
menuLayoutParams.setMargins(0, marginTop, 0, 0);
view.setLayoutParams(menuLayoutParams);
}
如此,一个侵入式的页面布局就适配完成了,我们把这些方法封装起来以供日后使用
/**
* Created by Buzz on 2019/10/21.
* Email :[email protected]
*/
object ScreenUtil {
/**
* 背景侵入式布局,状态栏透明
* @param container 页面容器View
* @param content 整体下移的View
*/
fun AppCompatActivity.makeStatusBarTransparent(container:View, content:View) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.apply {
clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
} else {
decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
}
statusBarColor = Color.TRANSPARENT
}
ViewCompat.setOnApplyWindowInsetsListener(container){
_, insets ->
content.setMarginTop(insets.systemWindowInsetTop)
insets.consumeSystemWindowInsets()
}
}
}
/**
* 设定View的上边距
* @param marginTop 上边距的值
*/
private fun View.setMarginTop(marginTop: Int) {
val menuLayoutParams = this.layoutParams as ViewGroup.MarginLayoutParams
menuLayoutParams.setMargins(0, marginTop, 0, 0)
this.layoutParams = menuLayoutParams
}
}
/**
* Created by Buzz on 2019/10/21.
* Email :[email protected]
*/
class ScreenUtil {
static void makeStatusBarTransparent(AppCompatActivity activity, View container, final View content){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
} else {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
}
ViewCompat.setOnApplyWindowInsetsListener(container, new OnApplyWindowInsetsListener() {
@Override
public WindowInsetsCompat onApplyWindowInsets(View view, WindowInsetsCompat windowInsetsCompat) {
setMarginTop(content, windowInsetsCompat.getSystemWindowInsetTop());
return windowInsetsCompat.consumeSystemWindowInsets();
}
});
}
private static void setMarginTop(View view, int marginTop){
ViewGroup.MarginLayoutParams menuLayoutParams =
(ViewGroup.MarginLayoutParams)view.getLayoutParams();
menuLayoutParams.setMargins(0, marginTop, 0, 0);
view.setLayoutParams(menuLayoutParams);
}
}
封装完毕,在Activity里使用只需要一行代码即可
//rlContainer为Activity的根布局id,llContent为需要下移的内容布局的id
makeStatusBarTransparent(findViewById(R.id.rlContainer), findViewById(R.id.llContent))
//rlContainer为Activity根布局的id, llContent是需要下移的布局的id
StatusBarUtil.makeStatusBarTransparent(this, findViewById(R.id.rlContainer), findViewById(R.id.llContent));