vivo设置全屏后状态栏黑色_玩转Android状态栏

本文介绍了在Android系统中如何设置状态栏颜色,包括不同版本Android的状态栏特性,以及如何处理各种定制ROM的适配问题。文章详细阐述了4.4到6.0版本的实现原理,并推荐了兼容库status-bar-compat。同时,文中还讨论了全屏切换时的页面移动问题、白色状态栏与图标颜色的匹配,以及多个tab页面状态栏颜色的处理策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

前段时间,突然收到一个状态栏颜色优化设计的任务,将原本应用整体的黑色状态栏修改为根据标题栏颜色进行沉浸式设计,显示效果如下:

2c6f36c035846aa55926d137e9dde5eb.png

经过分析及踩过N多坑,终于完成了APP全局的修改。现将一些需要注意的问题及踩过的坑进行梳理总结,主要从系统版本区别、各大厂商的ROM区别及具体的设置进行分析,期间也参考了很多资料,会在文末附上对应的链接

Android各版本状态栏区别

首先我们需要注意,Android不是各个版本都支持设置状态栏的颜色,只有在5.0以上才支持。另外6.0以上才支持设置状态栏黑色图标(避免白色状态栏及白色图标导致看不清电量 时间等问题)

b7175b50d154e90c705bc196399caa9e.png
系统版本是否支持设置状态栏颜色是否允许设置状态栏黑色图标
4.4
5.0
6.0+


是不是设置了状态栏透明就真透明了?

这个问题一开始也困扰了我,后面分析,在原生的系统虽然设置了状态栏透明,但是状态栏区域也会有一层半透明的遮罩(估计就是考虑到白色状态栏引起的问题),但是测试发现部分国产ROM设置穿透栏透明则会完全透明(例如MIUI)

原生系统效果如下:

e3ce83dea86e8757442edc8037b784b3.png

MIUI系统效果如下:

60882f4dd5ef8d5f7e3699591fa4174e.png

是不是6.0都支持系统状态栏黑条图标?

原生6.0以上有API支持,但是国产各ROM经过定制,有的需要特定的设置才能实现

原生系统设置:

public void setLightStatusBar(Window window, boolean lightStatusBar) {
// 设置浅色状态栏时的界面显示
View decor = window.getDecorView();
int ui = decor.getSystemUiVisibility();
if (lightStatusBar) {
ui |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
} else {
ui &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
}
decor.setSystemUiVisibility(ui);

}

小米:

public static boolean MIUISetStatusBarLightMode(Window window, boolean dark) {
boolean result = false;
if (window != null) {
Class clazz = window.getClass();
try {
int darkModeFlag = 0;
Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
darkModeFlag = field.getInt(layoutParams);
Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
if (dark) {
extraFlagField.invoke(window, darkModeFlag, darkModeFlag);//状态栏透明且黑色字体
} else {
extraFlagField.invoke(window, 0, darkModeFlag);//清除黑色字体
}
result = true;
} catch (Exception e) {

}
}
return result;
}

魅族:

public static boolean FlymeSetStatusBarLightMode(Window window, boolean dark) {
boolean result = false;
if (window != null) {
try {
WindowManager.LayoutParams lp = window.getAttributes();
Field darkFlag = WindowManager.LayoutParams.class
.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
Field meizuFlags = WindowManager.LayoutParams.class
.getDeclaredField("meizuFlags");
darkFlag.setAccessible(true);
meizuFlags.setAccessible(true);
int bit = darkFlag.getInt(null);
int value = meizuFlags.getInt(lp);
if (dark) {
value |= bit;
} else {
value &= ~bit;
}
meizuFlags.setInt(lp, value);
window.setAttributes(lp);
result = true;
} catch (Exception e) {

}
}
return result;
}

华为手机:部分测试发现华为的EMUI手机状态栏会跟系统桌面的状态栏一样,设置了没用,这里如果要特殊设置状态栏颜色,只能参考4.4的处理方式(后续介绍)

83d37aec37774caf152098cfd26707c4.png

如何进行定制状态栏

原理分析

  • 4.4

通过上述的版本及分析,可见完善的的状态栏兼容是一个大工程,需要综合考虑系统版本及各个厂商ROM等因素。5.0以上有系统API进行支持,这里我们主要来分析一些4.4的实现原理。简单来说,4.4的实现方式就是使用透明的状态栏,然后做一个和状态栏一样高度的View,加入到Windows的DecorView,然后给这个View设置背景色,达到实现状态栏颜色。

window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

ViewGroup decorViewGroup = (ViewGroup) window.getDecorView();
View statusBarView = decorViewGroup.findViewWithTag(STATUS_BAR_VIEW_TAG);
if (statusBarView == null) {
statusBarView = new StatusBarView(window.getContext());
statusBarView.setTag(STATUS_BAR_VIEW_TAG);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.TOP;
statusBarView.setLayoutParams(params);
decorViewGroup.addView(statusBarView);
}
statusBarView.setBackgroundColor(color);
StatusBarCompat.internalSetFitsSystemWindows(window, true);
8e5a8466d2891fa483d207fbc2fdb954.png
  • 5.0

注意5.0一般不用使用白色的状态栏(因为不能设置状态栏灰色图标),可在资源文件定义一个rgb,区分版本,5.0使用白灰色

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void setStatusBarColor(Window window, int color) {
//取消设置透明状态栏,使 ContentView 内容不再覆盖状态栏
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
//需要设置这个 flag 才能调用 setStatusBarColor 来设置状态栏颜色
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
//设置状态栏颜色
window.setStatusBarColor(color);
}
9e92b14196b911b3832d552781aa9203.png
  • 6.0

@TargetApi(Build.VERSION_CODES.M)
@Override
public void setStatusBarColor(Window window, int color) {
//取消设置透明状态栏,使 ContentView 内容不再覆盖状态栏
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
//需要设置这个 flag 才能调用 setStatusBarColor 来设置状态栏颜色
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
//设置状态栏颜色
window.setStatusBarColor(color);

// 去掉系统状态栏下的windowContentOverlay
View v = window.findViewById(android.R.id.content);
if (v != null) {
v.setForeground(null);
}
}
f9ccb67ea6ca7d6a57a3c4e71dd109d4.png

实现方案

这里不重复造轮子,先提供一下github上比较完善的处理方案

status-bar-compat

StatusBarCompat是一个用于设置系统状态栏颜色的兼容库,兼容Android 4.4.2(API 19)以上,使用简单,仅需要一行代码的调用。

SystemBarTint

支持4.4以上的,主要使用透明状态栏的方式实现

推荐使用status-bar-compat,已考虑到整体的版本兼容机及各厂ROM,调用简单。

在Activity的setContentView()方法调用之后,调用以下方法即可。

StatusBarCompat.setStatusBarColor(this, color, lightStatusBar);
或者是

StatusBarCompat.setStatusBarColor(this, color);

本文主要源码使用status-bar-compat中的代码进行说明

关于全屏及非全屏界面切换导致页面移动问题

例如在应用中有全屏的看图页面,点击返回为非全屏(带状态栏)页面,非全屏页面由于现实状态,会出现页面抖动。目前暂无完善的处理方案,项目中暂时使用的方式是延迟全屏页面的finish,先显示状态栏后再关闭

复写onBackPressed

getActivity().getWindow().clearFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN);
if(getView()!=null){
getView().postDelayed(new Runnable() {
@Override
public void run() {
getActivity().finish();
}
},10);
}

状态栏一片白色

上面已有分析,要注意如果状态栏为白色,需要设置状态栏的图标颜色。status-bar-compat中会把颜色转换成灰度值,然后自己控制状态栏图标颜色

public static void setStatusBarColor(Activity activity, @ColorInt int color) {
boolean isLightColor = toGrey(color) > 225;
setStatusBarColor(activity, color, isLightColor);
}

/**
* 把颜色转换成灰度值。
* 代码来自 Flyme 示例代码
*/
public static int toGrey(@ColorInt int color) {
int blue = Color.blue(color);
int green = Color.green(color);
int red = Color.red(color);
return (red * 38 + green * 75 + blue * 15) >> 7;
}

部分第三方组件的弹层和白色状态栏显得比较突兀

f2fd7837033bafea462c35559cedfc88.png

这个目前也尚无方法,考虑可以在弹层出现时,动态修改状态的颜色,但是工作量比较大,可先适当调整弹层的rgb,减低透明度

一个界面有几个tab卡,其中一个需要沉浸式,另外3个需要设置白色状态栏

aea7f72a6bb98058857bc0cf17b86146.png

这个问题,一开始感觉很简单,切换到沉浸式时将界面设置为全屏,切换到非全屏时则不全屏则可,但实际发生在同一个界面,如果全屏和非全屏切换页面会产生位移。最后方案使用Paletted对背景图进行顶部取色,然后设置状态栏颜色。(注意改方案当背景图为全白或者全黑时,顺便也解决了状态栏图标颜色问题)

Palette.Builder builder = Palette.from(bitmap);
builder.generate(new Palette.PaletteAsyncListener() {
@Override
public void onGenerated(Palette palette) {
//获取到充满活力的这种色调
Palette.Swatch vibrant = palette.getVibrantSwatch();
if (vibrant == null) {
vibrant = palette.getLightVibrantSwatch();
}
if (vibrant == null) {
vibrant = palette.getDarkVibrantSwatch();
}
for (Palette.Swatch targer : palette.getSwatches()) {
if (vibrant == null) {
vibrant = targer;
}
}
if (vibrant != null) {
rgb = vibrant.getRgb();
setStatusBarTintColor(vibrant.getRgb());
} else {
setStatusBarTintColor(getResources().getColor(R.color.status_back_color));
}
bitmap.recycle();
}
});

项目中如何使用状态栏颜色

  • 普通界面

af53526fb6d033d019692cf9cf5aee1c.png

普通界面默认都是白色的状态栏颜色,无须特殊设置

  • 特殊颜色标题栏

d7f5bb4289bc6b9ba7c654a0036a9f6e.png

@Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); setStatusBarTintColor(getResources().getColor(R.color.blue_status_bar)); }

  • 沉浸式状态栏

257cc8c1b083949d7c219b09f556a743.png
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
setImmersionStatusBar();//沉浸式
setViewHeightWithStatusBar(toolbar);//将状态栏距离顶部一个状态栏的高度
}
  • 混合型的状态栏

d5ef0709187062b22c70babefc5cfce7.png

HomePersonWebActivity

scrollLayout.setOnScrollListener((translationY, maxY) -> {
float ratio = (float) (maxY - translationY) / maxY;
if (ratio == 0) {
setStatusBarTintColor(getResources().getColor(R.color.status_back_color));
setTooBarMargin(0);
toolbar.setBackgroundResource(R.drawable.home_title_back_ground_of_drawable);
setTitle(userName);
toolbar_close.setTextColor(getResources().getColor(R.color.common_blue_color));
showQrMenu = false;
if (qrmenu != null) {
qrmenu.setVisible(false);
}
menu_star.setVisibility(!self ? View.VISIBLE : View.GONE);
more_menu.setIcon(R.mipmap.nav_bar_more);
super.updateArrowTheme();
} else {
setImmersionStatusBar();
setTooBarMargin(-1);
toolbar.setBackgroundResource(R.drawable.transparent);
setTitle(" ");
toolbar_close.setTextColor(getResources().getColor(R.color.white));
showQrMenu = true;
if (qrmenu != null) {
qrmenu.setVisible(true);
}
menu_star.setVisibility(View.GONE);
more_menu.setIcon(R.mipmap.ic_menu_action_more_white);
this.updateArrowTheme();
}
});

参考资料

Android 6.0 以上实现状态栏白底黑字

status-bar-compat

关于

欢迎关注我的个人公众号

微信搜索:一码一浮生,或者搜索公众号ID:life2code

34f13043a47ff0d03466c4be0be40fbc.png
  • 作者:黄俊彬

  • 博客:junbin.tech

  • GitHub: junbin1011

  • 知乎: @JunBin

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值