前言:关于这个状态栏变色到底叫透明状态栏、沉浸状态栏、变色状态栏,可能大家在看网上文章时也是各抒己见,概念乱七八糟,把透明栏叫成沉浸栏,沉浸栏当做透明栏的,最后也是一头雾水。今天这篇文章带大家弄懂【Android状态栏】的几种形式。知道每个是什么样子的,也就懂他们的区别了。
一、默认状态栏
如果软件没有对状态栏做处理,状态栏自然状态是颜色不透明(全黑)、字体为白色
二、关于【沉浸】
沉浸模式Immersive Mode是Android 4.4的特性
用简单一句话是它呈现的形式是全屏状态, 隐藏状态栏与导航栏。让应用程序内容可以在最大显示范围呈现,增加大屏体验,而当需要查看通知的时候只需要从顶部向下滑动就能呼出通知栏。也就是全屏拓展。
三、透明状态栏
关于【透明】、和【沉浸】一样、是Google在Android4.4提出,在 Android 4.4 的 API 描述页面里提到了“Translucent system UI styling”,即半透明化的系统UI风格。这个“半透明化”包括了状态栏和通知栏,当开发者让应用支持这个新特性的时候,状态栏和导航栏可以单独/同时变为渐变的半透明样式。状态栏也会根据系统的主题是深色还是浅色有一点不同,但大概如下图,其实我们可以看出,这样并不是很好看。
- 之所以提到透明状态栏,是我们通常所需要的给状态栏设置颜色的原理就是这里的透明概念。(Android4.4)API 19没有提供单独设置“状态栏”颜色的方法,如果要设置,就需要把View的最上端设置出一定高度的颜色(或者图案),具体高度只要大于等于“状态栏”高度即可,那么已经“透明”了的“状态栏”自然而然就会变成这个颜色(或者图案)。从而达到设置状态栏有颜色的样子。所以可以说是有色状态栏为透明状态栏的一种。因为是以透明状态栏为基础。
- 但这限于Android5.0以下设置颜色的原理,在5.0之后就提供API可以直接设置颜色了。
四、给状态栏设置颜色
经常会看到这些词:着色状态栏、变色状态栏、单色状态栏
放几张图片,以下就是我们常用状态栏的样式。
大家通常又通常把状态栏和导航栏颜色不一样称作变色状态栏,一样称作单色状态栏、😁、这些名称大家都是自己随意命名的,只要知道是怎么回事就好、说白了就是给状态栏设置颜色!
【给状态栏设置颜色】我们要分为两种情况
-
1.Android4.4以上&&Android5.0以下(API大于等于19小于21)
记得我们上面讲过(Android4.4)API 19没有提供单独设置“状态栏”颜色的方法,如果要设置,就需要把View的最上端设置出一定高度的颜色(或者图案),具体高度只要大于等于“状态栏”高度即可,那么已经“透明”了的“状态栏”自然而然就会变成这个颜色(或者图案)。从而达到设置状态栏有颜色的样子。原理就是4.4提出的透明概念
-
2.Android5.0以上(API大于等于21)
在5.0我们都知道提出了MaterialDesign的UI设计,导航栏也变成了Toolbar、google默认都会把我们的两栏设置为一深一浅的颜色,你可以说这是变色状态栏,但不是沉浸!!我们可以直接通过系统的colorPrimaryDark等颜色值直接修改状态栏的颜色。
-
概括一下就是4.4的变色原理是“透明”。5.0及以上版本的变色原理是“分别单独设置颜色”,但同时,4.4那种基于“透明”的方法的变色在5.0上依旧适用。
五、关于全屏
我所理解的全屏也是分为两种
-
一种带状态栏,内容View和状态栏像放在一个FrameLayout一样,是层叠关系并且状态栏是透明,内容View可以延伸到状态栏,我们称之为全屏模式。比如抖音、小米天气
- 或者隐藏状态栏。而隐藏状态栏也分为两种。
- 沉浸式全屏(我们在前面提到的):比如我们看小说、玩游戏时,在沉浸式全屏状态下,对屏幕的操作并不会唤出系统栏。想要唤出系统栏,你必须从屏幕的上/下边缘向屏幕内划入。
-
普通全屏:还有一种是类似看视频,通常我们的手指不会碰触屏幕,此时点击屏幕,状态栏就会出现。
知道了这些状态栏的区别,最后我们封装一个状态栏的工具类,以便我们在使用中方便调用。应对各种需求。
封装StatusBarUtil工具类
一、设置透明状态栏
重点 :只有4.4以后才可以设置,4.4以下只会显示默认状态栏。
4.4以后之所以可以设置,是因为API新添属性windowTranslucentStatus.
关于设置透明栏我们有两种方式,第一种是通过xml设置,第二种是使用代码设置。推荐代码方式。
-
通过xml:新建values-v19文件夹,同时新建styles.xml,为activity的Style 添加一个属性,然后在配置文件给相应的activity设置android:theme
<style name="AppTheme" parent="BaseTheme">
<item name="android:windowTranslucentStatus">true</item>
</style>
-
通过代码设置:
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
Window window = activity.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
我们看一下效果
【关于fitsSystemWindows属性】
我们应该很明显的看出,该属性实现了透明状态栏效果,但是使用该属性设置主题后,内容布局向上延伸至状态栏,出现了重叠
,并且在不同版本的系统中呈现效果也有所区别,所以当我们的顶部带有标题栏actionbar或toorbar,为了避免重叠的问题,我们需要在布局layout文件里加入android:fitsSystemWindows="true"这个属性,或是在theme的style中加入<item name="android:fitsSystemWindows">true</item>。这样既可解决视图延伸问题。使用效果如下:
二、有色状态栏
正常标题栏是黑底白字,但有的时候我们的主题背景是浅色,那肯定希望字体颜色为黑色比较好,这时我们需要根据浅色主题做适配,目前可以应用于4.4以上版本MIUIV、Flyme和6.0(API 23)以上版本其他Android 。
//有色状态栏,想要和导航栏颜色不一样就设置不一样的颜色即可
public static void setWindowStatusBarColor(Activity activity, int color, boolean isLightMode) {
Log.d("StatusBar", "Build.VERSION.SDK_INT:" + Build.VERSION.SDK_INT);
int statusColor = color;
//如果是浅色主题 可将状态栏图标和文字内容改为黑色样式 实现浅色主题调背景的状态栏效果
//只应用于4.4以上版本MIUIV、Flyme和6.0(API 23)以上版本其他Android
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (isLightMode) {
int mode = StatusBarLightMode(activity);
//0代表浅色主题没有设置成功
if (mode == 0) {
statusColor = activity.getResources().getColor(R.color.status_bar_dark);
}
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//使用setStatusBarColor的前提条件 取消FLAG_TRANSLUCENT_STATUS
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
activity.getWindow().setStatusBarColor(statusColor);
return;
}
//4.4以上,5.0以下
//原理:只要在根布局去设置一个与状态栏等高的View,设置背景色为我们期望的颜色就可以
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content);
View statusBarView = new View(activity);
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
getStatusBarHeight(activity));
statusBarView.setBackgroundColor(statusColor);
contentView.addView(statusBarView, lp);
}
}
三、全屏模式
1.全屏模式之带状态栏(内容可伸展到状态栏,比如抖音)
页面顶部的图片内容延伸至状态栏中,这种做法其实就是单独使用 <item name="android:windowTranslucentStatus">true</item> 样式,不在 layout 布局文件中添加 android:fitsSystemWindows="true" 属性。
通过设置style方式:
<item name="android:windowTranslucentStatus">true</item>
//如果要取消标题栏
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
通过代码
//带状态栏的全屏--内容可伸展到状态栏
public static void setFullScreenWithStatusBar(Activity activity){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// 设置状态栏透明
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
// 设置根布局的参数
//将根rootView直接顶上去,和状态栏的顶部对齐。
ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
ViewCompat.setFitsSystemWindows(rootView,false);
rootView.setClipToPadding(true);
}
}
2.全屏模式之无状态栏
之一:沉浸式状态栏实现
⚠️:切结给状态栏改个颜色不代表就是沉浸!
我们想要的沉浸效果:开启沉浸模式后,状态栏消失,也就是全屏拓展,当从顶部向下滑动,状态栏出现,退出沉浸模式,状态栏也出现了。
因为这个也不是通用的,就没有封装进去,如果有这个需求,参考这篇文章自行设置Android 实现沉浸式状态栏
之二:普通全屏
1.设置style
- 只是全屏:指定theme为android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
- 全屏并透明:指定theme为android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen"
2.或代码实现
//普通全屏
public static void setFullScreenNoStatusBar(Activity activity){
activity.getWindow().
setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
最后放上全部代码
public class StatusBarUtils {
// 使Activity布局填充状态栏
public static void fillStatusBar(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
View decorView = activity.getWindow().getDecorView();
int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
decorView.setSystemUiVisibility(option);
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
//透明状态栏
public static void setTranslucentStatusbar(Activity activity) {
Window window = activity.getWindow();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//使用setStatusBarColor的前提条件 取消FLAG_TRANSLUCENT_STATUS
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
window.setNavigationBarColor(Color.TRANSPARENT);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// 透明状态栏
//必须要有windowTranslucentStatus这个属性
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
// 透明导航栏
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
ViewCompat.setFitsSystemWindows(rootView, true);
}
}
/**
* @param activity
* @param color
* @param isLightMode
*/
//有色状态栏,想要和导航栏颜色不一样就设置不一样的颜色即可
public static void setWindowStatusBarColor(Activity activity, int color, boolean isLightMode) {
Log.d("StatusBar", "Build.VERSION.SDK_INT:" + Build.VERSION.SDK_INT);
int statusColor = color;
//如果是浅色主题 可将状态栏图标和文字内容改为黑色样式 实现浅色主题调背景的状态栏效果
//只应用于4.4以上版本MIUIV、Flyme和6.0(API 23)以上版本其他Android
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (isLightMode) {
int mode = StatusBarLightMode(activity);
//0代表浅色主题没有设置成功
if (mode == 0) {
statusColor = activity.getResources().getColor(R.color.status_bar_dark);
}
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//使用setStatusBarColor的前提条件 取消FLAG_TRANSLUCENT_STATUS
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
activity.getWindow().setStatusBarColor(statusColor);
return;
}
//4.4以上,5.0以下
//原理:只要在根布局去设置一个与状态栏等高的View,设置背景色为我们期望的颜色就可以
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content);
View statusBarView = new View(activity);
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
getStatusBarHeight(activity));
statusBarView.setBackgroundColor(statusColor);
contentView.addView(statusBarView, lp);
}
}
//得到系统statusbar的高度
public static int getStatusBarHeight(Context context) {
int result = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
//全屏布局实现方式1
//带状态栏的全屏--内容可伸展到状态栏
public static void setFullScreenWithStatusBar(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// 设置状态栏透明
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
// 设置根布局的参数
//将根rootView直接顶上去,和状态栏的顶部对齐。
ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
ViewCompat.setFitsSystemWindows(rootView, false);
rootView.setClipToPadding(true);
}
}
//不带状态栏的全屏
public static void setFullScreenNoStatusBar(Activity activity) {
activity.getWindow().
setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
//全屏布局实现方式2
public static void setFullScreen(Activity activity, boolean isHavaStatusBar) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (isHavaStatusBar) {
//带状态栏的全屏
activity.getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
} else {
//不带状态栏的全屏
activity.getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
}
}
}
/**
* 设置状态栏黑色字体图标,
* 适配4.4以上版本MIUIV、Flyme和6.0以上版本其他Android
*
* @param activity
* @return 1:MIUUI 2:Flyme 3:android6.0 0:为没有设置浅色主题成功
*/
public static int StatusBarLightMode(Activity activity) {
int result = 0;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (DeviceUtils.isMiui() && MIUISetStatusBarLightMode(activity.getWindow(), true)) {
result = 1;
//miui android 7.0无法通过反射修改
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
result = 3;
Log.d("statusbar1", "api method");
}
} else if (FlymeSetStatusBarLightMode(activity.getWindow(), true)) {
result = 2;
} else 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);
result = 3;
}
}
return result;
}
/**
* 设置状态栏图标为深色和魅族特定的文字风格
* 可以用来判断是否为Flyme用户
*
* @param window 需要设置的窗口
* @param dark 是否把状态栏字体及图标颜色设置为深色
* @return boolean 成功执行返回true
*/
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;
}
/**
* 设置状态栏字体图标为深色,需要MIUIV6以上
*
* @param window 需要设置的窗口
* @param dark 是否把状态栏字体及图标颜色设置为深色
* @return boolean 成功执行返回true
*/
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);//状态栏透明且黑色字体
Log.d("statusbar1", "miui method: 1");
} else {
extraFlagField.invoke(window, 0, darkModeFlag);//清除黑色字体
Log.d("statusbar1", "miui method: 2");
}
result = true;
} catch (Exception e) {
Log.d("statusbar1", "miui method: error");
}
}
return result;
}
}