-
2020-07-24 13:59:11
Android开发中,经常需要实现下图状态栏的效果,类似于沉浸式状态栏,但这里仅仅是讨论设置状态栏的颜色和状态栏上面文字、图标的颜色的方法。
Android 4.4(API 19)之后,就提供了修改状态栏颜色的方法,但是在 Android 6.0(API 23)之后,才支持修改状态栏上面的文字和图标颜色,默认是白色的
。所以会导致一个问题,在 4.4 到 6.0 之间的系统,状态栏设置为浅色的话,状态栏上面白色的文字和图标会看不清,像下面这样:
有一些第三方的系统提供了设置状态栏和状态栏文字、图标颜色的方法,比如小米的MIUI和魅族的Flyme,所以考虑了下比较好的实现方式是:- Android 4.4 以上使用 SystemBarTint 修改状态栏颜色;
- Android 6.0 以上使用系统方法修改状态栏字体、图标颜色;
- Android 4.4 到 6.0 之间使用第三方系统提供的方法修改状态栏字体、图标颜色(目前只有 MIUI 和 Flyme)。
当然,这里面也会有坑,比如 MIUI 提供的修改状态栏字体颜色方法会跟 Android 系统自带的方法冲突,官方说明如下: 关于MIUI状态栏字符颜色逻辑调整说明
经过网上的资料和自己的尝试,MIUI 系统还是同时使用 MIUI 提供的方法和 Android 系统自带的方法来修改状态栏字体颜色比较保险。基于上面的思考,封装了设置 Android 4.4 以上系统状态栏颜色和状态栏字体、图标颜色的方法:
- 首先,需要引入 SystemBarTint 库:
dependencies { compile 'com.readystatesoftware.systembartint:systembartint:1.0.3' }
- 隐藏标题栏
<!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- Customize your theme here. --> <!--标题栏的颜色--> <item name="colorPrimary">@color/colorPrimary</item> <!--状态栏的颜色--> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <!--默认的光标、选中的RadioButton颜色--> <item name="colorAccent">@color/colorAccent</item> <!-- 避免控件会顶到状态栏上 --> <item name="android:fitsSystemWindows">true</item> </style>
要在 Application Theme 加上
<item name="android:fitsSystemWindows">true</item>
,不然页面会顶到状态栏上面,或者在 Activity 的布局里面加上android:fitsSystemWindows="true"
和android:clipToPadding="false"
也可以。- 状态栏工具类 StatusBarUtil.java
package com.jairus; import android.annotation.TargetApi; import android.app.Activity; import android.graphics.Color; import android.os.Build; import android.view.View; import android.view.Window; import android.view.WindowManager; import com.readystatesoftware.systembartint.SystemBarTintManager; import java.lang.reflect.Field; import java.lang.reflect.Method; /** * 状态栏工具类 * 作者: JairusTse * 日期: 17/12/19 */ public class StatusBarUtil { /** * 设置状态栏为透明 * @param activity */ @TargetApi(19) public static void setTranslucentStatus(Activity activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Window window = activity.getWindow(); window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(Color.TRANSPARENT); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { Window window = activity.getWindow(); window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); } } /** * 修改状态栏颜色,支持4.4以上版本 * @param activity * @param colorId */ public static void setStatusBarColor(Activity activity, int colorId) { //Android6.0(API 23)以上,系统方法 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Window window = activity.getWindow(); window.setStatusBarColor(activity.getResources().getColor(colorId)); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //使用SystemBarTint库使4.4版本状态栏变色,需要先将状态栏设置为透明 setTranslucentStatus(activity); //设置状态栏颜色 SystemBarTintManager tintManager = new SystemBarTintManager(activity); tintManager.setStatusBarTintEnabled(true); tintManager.setStatusBarTintResource(colorId); } } /** * 设置状态栏模式 * @param activity * @param isTextDark 文字、图标是否为黑色 (false为默认的白色) * @param colorId 状态栏颜色 * @return */ public static void setStatusBarMode(Activity activity, boolean isTextDark, int colorId) { if(!isTextDark) { //文字、图标颜色不变,只修改状态栏颜色 setStatusBarColor(activity, colorId); } else { //修改状态栏颜色和文字图标颜色 setStatusBarColor(activity, colorId); //4.4以上才可以改文字图标颜色 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { if(OSUtil.isMIUI()) { //小米MIUI系统 setMIUIStatusBarTextMode(activity, isTextDark); } else if(OSUtil.isFlyme()) { //魅族flyme系统 setFlymeStatusBarTextMode(activity, isTextDark); } else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { //6.0以上,调用系统方法 Window window = activity.getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); } else { //4.4以上6.0以下的其他系统,暂时没有修改状态栏的文字图标颜色的方法,有可以加上 } } } } /** * 设置Flyme系统状态栏的文字图标颜色 * @param activity * @param isDark 状态栏文字及图标是否为深色 * @return */ public static boolean setFlymeStatusBarTextMode(Activity activity, boolean isDark) { Window window = activity.getWindow(); 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 (isDark) { value |= bit; } else { value &= ~bit; } meizuFlags.setInt(lp, value); window.setAttributes(lp); result = true; } catch (Exception e) { } } return result; } /** * 设置MIUI系统状态栏的文字图标颜色(MIUIV6以上) * @param activity * @param isDark 状态栏文字及图标是否为深色 * @return */ public static boolean setMIUIStatusBarTextMode(Activity activity, boolean isDark) { boolean result = false; Window window = activity.getWindow(); 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 (isDark) { extraFlagField.invoke(window, darkModeFlag, darkModeFlag);//状态栏透明且黑色字体 } else { extraFlagField.invoke(window, 0, darkModeFlag);//清除黑色字体 } result = true; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { //开发版 7.7.13 及以后版本采用了系统API,旧方法无效但不会报错,所以两个方式都要加上 if (isDark) { 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_VISIBLE); } } } catch (Exception e) { } } return result; } }
- 设备系统工具类 OSUtil.java
package com.jairus; import android.os.Environment; import android.text.TextUtils; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.Collection; import java.util.Enumeration; import java.util.Map; import java.util.Properties; import java.util.Set; /** * 设备系统工具类 * 作者: JairusTse * 日期: 17/12/19 */ public class OSUtil { //MIUI标识 private static final String KEY_MIUI_VERSION_CODE = "ro.miui.ui.version.code"; private static final String KEY_MIUI_VERSION_NAME = "ro.miui.ui.version.name"; private static final String KEY_MIUI_INTERNAL_STORAGE = "ro.miui.internal.storage"; //EMUI标识 private static final String KEY_EMUI_VERSION_CODE = "ro.build.version.emui"; private static final String KEY_EMUI_API_LEVEL = "ro.build.hw_emui_api_level"; private static final String KEY_EMUI_CONFIG_HW_SYS_VERSION = "ro.confg.hw_systemversion"; //Flyme标识 private static final String KEY_FLYME_ID_FALG_KEY = "ro.build.display.id"; private static final String KEY_FLYME_ID_FALG_VALUE_KEYWORD = "Flyme"; private static final String KEY_FLYME_ICON_FALG = "persist.sys.use.flyme.icon"; private static final String KEY_FLYME_SETUP_FALG = "ro.meizu.setupwizard.flyme"; private static final String KEY_FLYME_PUBLISH_FALG = "ro.flyme.published"; /** * 是否是Flyme系统 * @return */ public static boolean isFlyme() { if(isPropertiesExist(KEY_FLYME_ICON_FALG, KEY_FLYME_SETUP_FALG, KEY_FLYME_PUBLISH_FALG)) { return true; } try { BuildProperties buildProperties = BuildProperties.newInstance(); if(buildProperties.containsKey(KEY_FLYME_ID_FALG_KEY)) { String romName = buildProperties.getProperty(KEY_FLYME_ID_FALG_KEY); if (!TextUtils.isEmpty(romName) && romName.contains(KEY_FLYME_ID_FALG_VALUE_KEYWORD)) { return true; } } } catch (IOException e) { e.printStackTrace(); } return false; } /** * 是否是EMUI系统 * @return */ public static boolean isEMUI() { return isPropertiesExist(KEY_EMUI_VERSION_CODE, KEY_EMUI_API_LEVEL, KEY_EMUI_CONFIG_HW_SYS_VERSION); } /** * 是否是MIUI系统 * @return */ public static boolean isMIUI() { return isPropertiesExist(KEY_MIUI_VERSION_CODE, KEY_MIUI_VERSION_NAME, KEY_MIUI_INTERNAL_STORAGE); } private static boolean isPropertiesExist(String... keys) { if (keys == null || keys.length == 0) { return false; } try { BuildProperties properties = BuildProperties.newInstance(); for (String key : keys) { String value = properties.getProperty(key); if (value != null) return true; } return false; } catch (IOException e) { return false; } } private static final class BuildProperties { private final Properties properties; private BuildProperties() throws IOException { properties = new Properties(); // 读取系统配置信息build.prop类 properties.load(new FileInputStream(new File(Environment.getRootDirectory(), "build" + ".prop"))); } public boolean containsKey(final Object key) { return properties.containsKey(key); } public boolean containsValue(final Object value) { return properties.containsValue(value); } public Set<Map.Entry<Object, Object>> entrySet() { return properties.entrySet(); } public String getProperty(final String name) { return properties.getProperty(name); } public String getProperty(final String name, final String defaultValue) { return properties.getProperty(name, defaultValue); } public boolean isEmpty() { return properties.isEmpty(); } public Enumeration<Object> keys() { return properties.keys(); } public Set<Object> keySet() { return properties.keySet(); } public int size() { return properties.size(); } public Collection<Object> values() { return properties.values(); } public static BuildProperties newInstance() throws IOException { return new BuildProperties(); } } }
- 在 Activity 里面调用:
//根据状态栏颜色来决定状态栏文字用黑色还是白色 StatusBarUtil.setStatusBarMode(this, true, R.color.c_ffffff);
最终实现的效果如下:
更多相关内容 -
android沉浸状态栏和顶部状态栏背景色的设置
2016-07-26 10:20:48android沉浸状态栏和顶部状态栏背景色的设置,现在很多应用都引用了沉浸式状态栏这样的效果,这样的效果很酷炫。 -
Android内容覆盖透明状态栏下实现全屏模式下带状态栏的效果
2016-07-26 16:26:48做过一段状态栏颜色适配,纯色的基本在style直接设置就可以了, 不过最近效果图有一种效果要求 是在状态栏部分也显示内容部分, 像下面这样 -
android沉浸式状态栏、变色状态栏、透明状态栏、修改状态栏颜色及透明
2016-03-28 15:56:49实现4.4以后的状态栏与app的导航栏颜色相同,android沉浸式状态栏、变色状态栏、透明状态栏、修改状态栏颜色及透明。 -
(沉浸式)在Activity中或Fragment中任意改变状态栏的颜色
2017-01-20 10:27:20在Activity中或Fragment中任意改变状态栏的颜色,通过反射机制来获得状态栏的高度,通过点位View来叠加颜色 -
Android沉浸式状态栏/状态栏设置背景图片/自定义渐变式状态栏
2019-08-23 15:48:212.状态栏为背景图片效果 状态栏为图片效果.jpg 3.侧滑页面效果 侧滑页面效果.jpg 4.自定义渐变效果 自定义渐变效果.jpg 5.含ActionBar效果 含ActionBar效果.jpg 工具类 package 包名; import ...先图为敬。
1.普通效果
普通效果.jpg
2.状态栏为背景图片效果
状态栏为图片效果.jpg
3.侧滑页面效果
侧滑页面效果.jpg
4.自定义渐变效果
自定义渐变效果.jpg
5.含ActionBar效果
含ActionBar效果.jpg
工具类
package 包名; import android.app.Activity; import android.content.Context; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.os.Build; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.widget.LinearLayout; public class StatusBarUtils { private Activity mActivity; //状态栏颜色 private int mColor = -1; //状态栏drawble private Drawable mDrawable; //是否是最外层布局是 DrawerLayout 的侧滑菜单 private boolean mIsDrawerLayout; //是否包含 ActionBar private boolean mIsActionBar; //侧滑菜单页面的内容视图 private int mContentResourseIdInDrawer; public StatusBarUtils(Activity activity) { mActivity = activity; } public static StatusBarUtils with(Activity activity) { return new StatusBarUtils(activity); } public int getColor() { return mColor; } public StatusBarUtils setColor(int color) { mColor = color; return this; } public Drawable getDrawable() { return mDrawable; } public StatusBarUtils setDrawable(Drawable drawable) { mDrawable = drawable; return this; } public boolean isDrawerLayout() { return mIsDrawerLayout; } public boolean isActionBar() { return mIsActionBar; } public StatusBarUtils setIsActionBar(boolean actionBar) { mIsActionBar = actionBar; return this; } /** * 是否是最外层布局为 DrawerLayout 的侧滑菜单 * * @param drawerLayout 是否最外层布局为 DrawerLayout * @param contentId 内容视图的 id * @return */ public StatusBarUtils setDrawerLayoutContentId(boolean drawerLayout, int contentId) { mIsDrawerLayout = drawerLayout; mContentResourseIdInDrawer = contentId; return this; } public void init() { fullScreen(mActivity); if (mColor != -1) { //设置了状态栏颜色 addStatusViewWithColor(mActivity, mColor); } if (mDrawable != null) { //设置了状态栏 drawble,例如渐变色 addStatusViewWithDrawble(mActivity, mDrawable); } if (isDrawerLayout()) { //未设置 fitsSystemWindows 且是侧滑菜单,需要设置 fitsSystemWindows 以解决 4.4 上侧滑菜单上方白条问题 fitsSystemWindows(mActivity); } if (isActionBar()) { //要增加内容视图的 paddingTop,否则内容被 ActionBar 遮盖 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { ViewGroup rootView = (ViewGroup) mActivity.getWindow().getDecorView().findViewById(android.R.id.content); rootView.setPadding(0, getStatusBarHeight(mActivity) + getActionBarHeight(mActivity), 0, 0); } } } /** * 去除 ActionBar 阴影 */ public StatusBarUtils clearActionBarShadow() { if (Build.VERSION.SDK_INT >= 21) { ActionBar supportActionBar = ((AppCompatActivity) mActivity).getSupportActionBar(); if (supportActionBar != null) { supportActionBar.setElevation(0); } } return this; } /** * 设置页面最外层布局 FitsSystemWindows 属性 * * @param activity */ private void fitsSystemWindows(Activity activity) { ViewGroup contentFrameLayout = (ViewGroup) activity.findViewById(android.R.id.content); View parentView = contentFrameLayout.getChildAt(0); if (parentView != null && Build.VERSION.SDK_INT >= 14) { parentView.setFitsSystemWindows(true); //布局预留状态栏高度的 padding if (parentView instanceof DrawerLayout) { DrawerLayout drawer = (DrawerLayout) parentView; //将主页面顶部延伸至status bar;虽默认为false,但经测试,DrawerLayout需显示设置 drawer.setClipToPadding(false); } } } /** * 利用反射获取状态栏高度 * * @return */ public static int getStatusBarHeight(Activity activity) { int result = 0; //获取状态栏高度的资源id int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android"); if (resourceId > 0) { result = activity.getResources().getDimensionPixelSize(resourceId); } Log.e("getStatusBarHeight", result + ""); return result; } /** * 获得 ActionBar 的高度 * * @param context * @return */ public static int getActionBarHeight(Context context) { int result = 0; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { TypedValue tv = new TypedValue(); context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true); result = TypedValue.complexToDimensionPixelSize(tv.data, context.getResources().getDisplayMetrics()); } return result; } /** * 添加状态栏占位视图 * * @param activity */ private void addStatusViewWithColor(Activity activity, int color) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { if (isDrawerLayout()) { //要在内容布局增加状态栏,否则会盖在侧滑菜单上 ViewGroup rootView = (ViewGroup) activity.findViewById(android.R.id.content); //DrawerLayout 则需要在第一个子视图即内容试图中添加padding View parentView = rootView.getChildAt(0); LinearLayout linearLayout = new LinearLayout(activity); linearLayout.setOrientation(LinearLayout.VERTICAL); View statusBarView = new View(activity); ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity)); statusBarView.setBackgroundColor(color); //添加占位状态栏到线性布局中 linearLayout.addView(statusBarView, lp); //侧滑菜单 DrawerLayout drawer = (DrawerLayout) parentView; //内容视图 View content = activity.findViewById(mContentResourseIdInDrawer); //将内容视图从 DrawerLayout 中移除 drawer.removeView(content); //添加内容视图 linearLayout.addView(content, content.getLayoutParams()); //将带有占位状态栏的新的内容视图设置给 DrawerLayout drawer.addView(linearLayout, 0); } else { //设置 paddingTop ViewGroup rootView = (ViewGroup) mActivity.getWindow().getDecorView().findViewById(android.R.id.content); rootView.setPadding(0, getStatusBarHeight(mActivity), 0, 0); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { //直接设置状态栏颜色 activity.getWindow().setStatusBarColor(color); } else { //增加占位状态栏 ViewGroup decorView = (ViewGroup) mActivity.getWindow().getDecorView(); View statusBarView = new View(activity); ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity)); statusBarView.setBackgroundColor(color); decorView.addView(statusBarView, lp); } } } } /** * 添加状态栏占位视图 * * @param activity */ private void addStatusViewWithDrawble(Activity activity, Drawable drawable) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //占位状态栏 View statusBarView = new View(activity); ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { statusBarView.setBackground(drawable); } else { statusBarView.setBackgroundDrawable(drawable); } if (isDrawerLayout()) { //要在内容布局增加状态栏,否则会盖在侧滑菜单上 ViewGroup rootView = (ViewGroup) activity.findViewById(android.R.id.content); //DrawerLayout 则需要在第一个子视图即内容试图中添加padding View parentView = rootView.getChildAt(0); LinearLayout linearLayout = new LinearLayout(activity); linearLayout.setOrientation(LinearLayout.VERTICAL); //添加占位状态栏到线性布局中 linearLayout.addView(statusBarView, lp); //侧滑菜单 DrawerLayout drawer = (DrawerLayout) parentView; //内容视图 View content = activity.findViewById(mContentResourseIdInDrawer); //将内容视图从 DrawerLayout 中移除 drawer.removeView(content); //添加内容视图 linearLayout.addView(content, content.getLayoutParams()); //将带有占位状态栏的新的内容视图设置给 DrawerLayout drawer.addView(linearLayout, 0); } else { //增加占位状态栏,并增加状态栏高度的 paddingTop ViewGroup decorView = (ViewGroup) mActivity.getWindow().getDecorView(); decorView.addView(statusBarView, lp); //设置 paddingTop ViewGroup rootView = (ViewGroup) mActivity.getWindow().getDecorView().findViewById(android.R.id.content); rootView.setPadding(0, getStatusBarHeight(mActivity), 0, 0); } } } /** * 通过设置全屏,设置状态栏透明 * * @param activity */ private void fullScreen(Activity activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { //5.x开始需要把颜色设置透明,否则导航栏会呈现系统默认的浅灰色 Window window = activity.getWindow(); View decorView = window.getDecorView(); //两个 flag 要结合使用,表示让应用的主体内容占用系统状态栏的空间 int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); decorView.setSystemUiVisibility(option); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(Color.TRANSPARENT); //导航栏颜色也可以正常设置 // window.setNavigationBarColor(Color.TRANSPARENT); } else { Window window = activity.getWindow(); WindowManager.LayoutParams attributes = window.getAttributes(); int flagTranslucentStatus = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; int flagTranslucentNavigation = WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; attributes.flags |= flagTranslucentStatus; // attributes.flags |= flagTranslucentNavigation; window.setAttributes(attributes); } } } /** * 通过设置全屏,设置状态栏透明 导航栏黑色 * * @param activity */ public static void setStatusTransparent(Activity activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Window window = activity.getWindow(); WindowManager.LayoutParams attributes = window.getAttributes(); int flagTranslucentStatus = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; int flagTranslucentNavigation = WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; // attributes.flags |= flagTranslucentStatus; attributes.flags |= flagTranslucentNavigation; window.setAttributes(attributes); 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 { Window window = activity.getWindow(); WindowManager.LayoutParams attributes = window.getAttributes(); int flagTranslucentStatus = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; int flagTranslucentNavigation = WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; attributes.flags |= flagTranslucentStatus; attributes.flags |= flagTranslucentNavigation; window.setAttributes(attributes); } } } }
用法
1.普通效果
//清单文件中要设置主题为NoActionBar StatusBarUtils.with(this) .setColor(getResources().getColor(R.color.blue)) .init();
2.图片背景效果
//清单文件中要设置主题为NoActionBar //在布局文件中设置的第一个view的背景图片则为当前状态栏背景图片 StatusBarUtils.with(this) .init();
3.侧滑页面状态栏效果
//清单文件中要设置主题为NoActionBar //R.id.rl_content为主页面布局 //代码中设置的为主页面状态栏 //在侧滑页面布局文件中设置的第一个view的背景图片则为侧滑状态栏背景图片 StatusBarUtils.with(this) .setDrawerLayoutContentId(true, R.id.rl_content) .setColor(getResources().getColor(R.color.blue)) .init();
4.自定义渐变状态栏
//当前案例含ActionBar StatusBarUtils.with(this) .setIsActionBar(true) .clearActionBarShadow() .setDrawable(getResources().getDrawable(R.drawable.shape)) .init();
自定义渐变shape
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <gradient android:angle="0" android:centerX="0.7" android:endColor="@color/shape2" android:startColor="@color/shape1" android:centerColor="@color/shape3" android:type="linear" /> </shape>
-
Android 沉浸式状态栏的实现方法、状态栏透明的实现方法
2016-06-07 16:26:09Android 沉浸式状态栏的实现方法、状态栏透明的实现方法 -
Android 沉浸式状态栏和全面屏遇到刘海屏
2021-11-22 20:09:08在做项目的时候图片全屏预览时发现使用全面屏后,图片会闪动一下,带着问题来梳理一下Android 全面屏、沉浸式状态栏和刘海屏的概念和方法。 一、全面屏 1.1 向后倾斜 1.2 沉浸模式 1.3 粘性沉浸模式 二、沉浸式状态...文章目录
在做项目的时候图片全屏预览时发现使用全面屏后,图片会闪动一下( 总结部分有答案),带着问题来梳理一下Android 全面屏、沉浸式状态栏和刘海屏的概念和方法。普通的手机:
刘海屏的手机:
一、沉浸式状态栏
沉浸式状态栏就是ContentView中的内容占用系统状态栏的空间,状态栏和导航栏依然可见。
沉浸式状态栏对 Android 版本要求不一样,所以我们需要通过不同版本来进行判定区分,在 Android 4.4 一下,可以对 StatusBar 和 NavigationBar 进行显示和隐藏操作。直到 Android 4.4 ,才真正实现沉浸式状态栏。从 Android 4.4 (API 19)到 Android 12 (API 31),可以分为3个阶段:1.1 Android 4.4(API 19)- Android 5.0( API 21)
这个阶段实现沉浸式是通过 WindowManager 的
FLAG_TRANSLUCENT_STATUS
,这个窗口标志:允许窗口内容扩展到屏幕的顶部区域。
这种情况也是分两种情况:- 其一: 允许窗口内容扩展到屏幕的顶部区域,只需要设置
FLAG_TRANSLUCENT_STATUS
标志; - 其二: 如果不想让内容窗口扩展到系统状态栏上,也就是说不让系统状态栏遮挡住内容。就设置
FLAG_TRANSLUCENT_STATUS
标志而且添加一个与状态栏一样大小的 View ,将 View 的 background 设置我们需要的颜色;从而来实现沉浸式。
/** * 窗口标志:请求具有最少系统提供的背景保护的半透明状态栏。 * * 可以通过 {@link android.R.attrwindowTranslucentStatus} 属性在您的主题中控制此标志; * 此属性会在标准的半透明装饰主题中为您自动设置,例如 * {@link android.R.style#Theme_Holo_NoActionBar_TranslucentDecor}, * {@link android.R.style#Theme_Holo_Light_NoActionBar_TranslucentDecor}, * {@link android.R.style#Theme_DeviceDefault_NoActionBar_TranslucentDecor}, and * {@link android.R.style#Theme_DeviceDefault_Light_NoActionBar_TranslucentDecor}. * * 当为窗口启用此标志时,它会自动设置系统 UI 可见性标志 view.setSystemUiVisibility(int visibility) * {@link View.SYSTEM_UI_FLAG_LAYOUT_STABLE} 和 {@link View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}。 * * 使用带有半透明颜色的 {@link WindowsetStatusBarColor(int)} 代替 */ @Deprecated public static final int FLAG_TRANSLUCENT_STATUS = 0x04000000;
上面的注释的比较清楚,当在在 theme 中设置属性 windowTranslucentStatus 为 true 的时候生效:
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <!-- API 19 以上生效--> <item name="android:windowTranslucentStatus" tools:targetApi="kitkat">true</item> </style>
当我们在代码里设置
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
还有一点值得注意的是:设置这个标志会自动调用系统的 systemUiVisibility 来设置全屏模式
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
效果如下:
当我们不希望ContentView 中的内容被遮挡,我们可以添加一个和状态栏高度一样的 View,来控制这个 View 的颜色,以此来实现沉浸式。
private fun addStatusViewWithColor19(activity: Activity, color: Int) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // 设置 paddingTop val rootView = activity.window.decorView.findViewById<View>(android.R.id.content) as ViewGroup rootView.setPadding(0, "状态栏高度", 0, 0) // 增加占位状态栏 val decorView = activity.window.decorView as ViewGroup val statusBarView = View(activity) val lp = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, "状态栏高度") statusBarView.setBackgroundColor(color) decorView.addView(statusBarView, lp) } }
效果如下:
沉浸式在Android4.4 - Android5.0 之间 的状态栏顶部有个渐变,会显示黑色阴影,效果不是很好。1.2 Android 5.0(API 21)以上版本
在Android 5.0 的时候加入了一个重要的属性和方法,
android:statusBarColor (对应方法为 setStatusBarColor)
,通过设置这个属性或方法,可以实现我们想要的任何状态栏的颜色。/** * Sets the color of the status bar to {@code color}. * * For this to take effect, * the window must be drawing the system bar backgrounds with * {@link android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS} and * {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_STATUS} must not be set. * * If {@code color} is not opaque, consider setting * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}. * <p> * The transitionName for the view background will be "android:status:background". * </p> */ public abstract void setStatusBarColor(@ColorInt int color);
注释的意思是:想要这个方法生效,必须还要配合设置
FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
,并且不能设置FLAG_TRANSLUCENT_STATUS
(Android 4.4才用这个)
这个比 Android 4.4(API 19)- Android 5.0( API 21) 的阶段省去了自己创建 View 并给该 View 上颜色的步骤,并且去掉了黑色的渐变阴影。当需要让 ContentView 的内容占据状态栏的时候是和原来一样的直接设置 WindowManager 的FLAG_TRANSLUCENT_STATUS
标志
也可以配合 systemUiVisibility(int) 一起使用1.3 Android 6.0(API 23)以上版本
Android 6.0 以上的的实现方式是和 Android 5.0 一样的,但是从 6.0 开始,提供了状态栏的绘制模式,可以显示白色或黑色的内容和图标,除了部分族开放平台-状态栏变色和小米开放平台-MIUI 9 & 10“状态栏黑色字符”实现方法变更通知,Android 6.0 新添加了一个属性
SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
/** * Flag for {@link #setSystemUiVisibility(int)}: Requests the status bar to draw in a mode that * is compatible with light status bar backgrounds. * * <p>For this to take effect, the window must request * {@link android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS * FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS} but not * {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_STATUS * FLAG_TRANSLUCENT_STATUS}. * * @see android.R.attr#windowLightStatusBar * @deprecated Use {@link WindowInsetsController#APPEARANCE_LIGHT_STATUS_BARS} instead. */ @Deprecated public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 0x00002000;
注释的意思:可以设置 setSystemUiVisibility(int) 进行状态栏绘制模式,可以兼容亮色模式的状态栏。想要这个方法生效,必须还要配合设置
FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
,并且不能设置FLAG_TRANSLUCENT_STATUS
private fun transparentStatusBar23(activity: Activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { activity.window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) activity.window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) activity.window.statusBarColor = Color.TRANSPARENT val decorView = window.decorView if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // 状态栏图标为浅色 val option = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR decorView.systemUiVisibility = option } } }
设置状态栏字色和图标前:
设置状态栏字色和图标后:
综上所述沉浸式状态栏的两种模式,方法如下:
/** * 使 contentView 中的内容占据到状态栏的沉浸式 * * * @param activity */ fun setContentAdjustToStatusBar(activity: Activity, isLight: Boolean) { val window = activity.window if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) window.statusBarColor = Color.TRANSPARENT // 部分手机上设置此标识无效 // window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN // or View.SYSTEM_UI_FLAG_LAYOUT_STABLE) window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) if (isLight) { setStatusLight(activity) } } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) } } /** * 设置状态栏颜色,contentView 的内容在状态栏的下方的沉浸式 * * @param activity * @param color * @param isLight */ fun setStatusColor(activity: Activity, color: Int, isLight: Boolean) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { val window = activity.window window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) window.statusBarColor = color if (isLight) { setStatusLight(activity) } } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // 设置 paddingTop val rootView = activity.window.decorView.findViewById<View>(android.R.id.content) as ViewGroup rootView.setPadding(0, getStatusBarHeight(activity), 0, 0) // 增加占位状态栏 val decorView = activity.window.decorView as ViewGroup val statusBarView = View(activity) val lp = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity)) statusBarView.setBackgroundColor(color) statusBarView.layoutParams = lp decorView.addView(statusBarView) } } /** * 状态栏的图标和文字颜色为暗色 */ fun setStatusLight(activity: Activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { val option = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR activity.window.decorView.systemUiVisibility = option } } /** * 获取状态栏高度 * * @param activity * @return */ fun getStatusBarHeight(activity: Activity): Int { var result = 0 //获取状态栏高度的资源id val resourceId = activity.resources.getIdentifier("status_bar_height", "dimen", "android") if (resourceId > 0) { result = activity.resources.getDimensionPixelSize(resourceId) } Log.d("getStatusBarHeight", result.toString() + "") return result }
二、全屏模式
Android 手机屏幕一般是有状态栏和导航栏的,当需要全屏模式的时候,需要把状态栏和导航栏都隐藏起来。
Android 提供了三个用于将应用设为全屏模式选项:向后倾斜模式、沉浸模式和粘性沉浸模式。在所有三种方法中,系统栏都是隐藏的,您的 Activity 会持续收到所有轻触事件。 它们之间的区别在于用户让系统栏重新显示出来的方式。2.1 向后倾斜
向后倾斜模式适用于用户不会于屏幕进行大量互动的全屏体验,例如,在观看视频的时候。
当用户希望调出系统栏时,只需要点击屏幕上的任意位置即可调出系统栏。
开启向后倾斜模式,代码如下:val window = activity.window window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN)
2.2 沉浸模式
沉浸模式使用于用户将与屏幕有大量互动的应用,例如,游戏、查看图库中的图片或分页阅读。
当用户需要调出系统栏时,可以从状态栏或者导航栏的侧边来滑动调起系统栏,其他方式不会退出全面屏。val window = activity.window window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
2.3 粘性沉浸模式
在粘连沉浸模式下,可以从状态栏或者导航栏的侧边来滑动调起系统栏,但他们是半透明的效果浮在应用视图(ContentView内容)之上,当用户点击应用实体后无互动几秒之后,系统栏自动消失,恢复全面屏。这种模式比较适合游戏、绘图类应用。
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
2.4 状态栏和导航栏的隐藏
/** * 状态栏隐藏 * * @param activity */ fun setStatusBarHide(activity: Activity) { // 允许窗口延伸到屏幕短边上的刘海区域 // supportDisplayCutouts(activity) val window = activity.window window.decorView.systemUiVisibility = ( // 预留控制内容到状态栏的距离 View.SYSTEM_UI_FLAG_LAYOUT_STABLE // 隐藏状态栏 or View.SYSTEM_UI_FLAG_FULLSCREEN // 将 contentView的内容延伸到状态栏。 // 设置此属性时,要考虑是否使用 fitSystemWindows 来填充状态栏的距离; // 还要考虑刘海屏的情况,使用 layoutInDisplayCutoutMode 设置 or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN ) } /** * 导航栏隐藏 * * @param activity */ fun setNavigationBarHide(activity: Activity) { val window = activity.window window.decorView.systemUiVisibility = ( // 预留控制内容到导航栏的距离 View.SYSTEM_UI_FLAG_LAYOUT_STABLE // 隐藏导航栏 or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // 将 contentView的内容延伸到导航栏。 // 设置此属性时,要考虑是否使用 fitSystemWindows 来填充导航栏的距离; or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION ) }
2.5 view.setFitsSystemWindows()方法
- 1.设置系统是否需要考虑System bar(status bar和Navigation bar的统称)占据的区域来显示。如果需要的话就会执行
fitSystemWindows(Rect)
方法。即设置为true的时候系统会适应System bar 的区域,不内容不被遮住。fitSystemWindows(Rect)(api level 14)
:用来调整自身的内容来适应System Bar(不让被System Bar遮住)。 这里其实不止Status Bar和Navigation Bar,只是目前只考虑Status Bar、Navigation Bar、IME。 - 2.
onApplyWindowInsets(WindowInsets)(api level 20)
:同fitSystemWindows(Rect)的作用是一样的,更加方便扩展,对以后增加新的系统控件便于扩展。 - 3.使用
android:fitsSystemWindows="true"
,系统会自动的调整显示区域来实现详情的控件不会被遮住。
2.6 API 30 及以后的方式
在Android 30 API 上 SystemUIvisibility已弃用,推荐使用 WindowInsetsController类来进行状态栏的显示和隐藏以及其他操作。这种方式更简单、易操作。也可以使用 WindowInsetsControllerCompat 进行兼容。
private fun hideSystemBars30s(activity: Activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { val controller = activity.window.decorView.windowInsetsController // 隐藏状态栏 controller?.hide(WindowInsets.Type.statusBars()) // 显示状态栏 controller?.show(WindowInsets.Type.statusBars()) // 隐藏导航栏 controller?.hide(WindowInsets.Type.navigationBars()) // 显示导航栏 controller?.show(WindowInsets.Type.navigationBars()) // 同时隐藏状态栏和导航栏 controller?.hide(WindowInsets.Type.systemBars()) // 同时显示状态栏和导航栏 controller?.show(WindowInsets.Type.systemBars()) // 向后模式 val behavior1 = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH // 沉浸模式 val behavior2 = WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE // 粘性沉浸模式 val behavior3 = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE controller?.systemBarsBehavior = behavior1 } }
三、刘海屏
3.1 刘海屏的要求
在Android 9(API 28)及更高版本的设备上正式支持刘海屏。部分制造商也有在低版本上支持刘海屏。
在带刘海屏的设备上为确保一致性和兼容性,有五点强制要求:- 一条边缘最多只能包含一个刘海
- 一台设备不能有两个以上的刘海
- 设备的两条较长边缘上不能有刘海
- 在未设置特殊标志的竖屏模式下,状态栏的高度必须至少与刘海屏的高度一致
- 在默认情况下,在全屏模式或横屏模式下,整个刘海屏必须显示黑边
第五条比较重要,这里设置全面屏时必须要考虑是否有刘海屏的这个性质
3.2 刘海模式
Android 允许控制是否在刘海区域显示内容。窗口布局属性
layoutInDisplayCutoutMode
控制内容将如何呈现在刘海区域中。共分为3种模式,可以通过编程或设置 Activity 的样式来控制刘海屏模式:<style name="ActivityTheme"> <item name="android:windowLayoutInDisplayCutoutMode"> shortEdges <!-- default, shortEdges, never --> </item> </style>
fun supportDisplayCutouts(activity: Activity) { val window = activity.window val lp = window.attributes if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { // 仅当缺口区域完全包含在状态栏之中时,才允许窗口延伸到刘海区域显示 // lp.layoutInDisplayCutoutMode = // WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT // 永远不允许窗口延伸到刘海区域 // lp.layoutInDisplayCutoutMode = // WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER // 始终允许窗口延伸到屏幕短边上的刘海区域 lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES window.attributes = lp } }
下面就更详细的介绍不同的刘海模式。
3.2.1 默认行为
默认情况下,在未设置特殊标志的竖屏模式下,在带刘海屏的设备上,状态栏的大小会调整为至少与刘海一样高,而您的内容会显示在下方区域。在横屏模式或全屏模式下,您的应用窗口会显示黑边,因此您的任何内容都不会显示在刘海区域中。
3.2.2 将内容呈现在短边刘海屏区域中
在竖屏模式和横屏模式下,内容都会呈现到刘海区域中,而不管系统栏处于隐藏还是可见状态栏。需要注意的是:Android 一般是不允许内容视图与系统栏重叠,如果要强制视图内容延伸到刘海区域,需要和设置上述介绍的沉浸式状态栏或全面屏的方式结合使用。
3.2.3 从不将内容呈现在刘海区域中
内容从不呈现到刘海区域中。此模式应该用于暂时设置 View.SYSTEM_UI_FLAG_FULLSCREEN 或 View.SYSTEM_UI_FLAG_HIDE_NAVIGATION 的窗口,以避免在设置或清除了该标志时执行另一种窗口布局。
四、总结
开头说的全屏预览时发现使用全面屏后,图片会闪动一下,这是因为,我当时使用的刘海屏的手机,当使用全屏模式时,刘海屏的手机默认会显示黑边,我们还要设置刘海屏的模式,上面提到的刘海屏的3中模式,可以设置**将内容呈现在短边刘海屏区域中**从而在刘海屏中达到全屏的效果。沉浸式状态栏主要是分两个模式,一种是控制内容呈现在状态栏中,一种是控制状态栏的颜色。而且沉浸式状态栏还要根据 Android API 的不同使用的方式也不同,这块相对来说在日常开发中用的比较多,概念理清后就比较简单。
全面屏在 Android API 30 之前和之后也有所不同,在 Android API 30 及以后方式简单明了。
刘海屏是在 Android API 28 及以后支持的,要和全面屏配合使用,不然只考虑全面屏,在刘海屏上的效果是不进人不如意的。站在巨人的肩膀上:
1.MIUI 9 & 10“状态栏黑色字符”实现方法变更通知
2.魅族开放平台-状态栏变色
3.启用全屏模式
4.支持刘海屏
5.Android关于沉浸式状态栏总结 - 其一: 允许窗口内容扩展到屏幕的顶部区域,只需要设置
-
Android沉浸式状态栏 一体化状态栏信号栏
2015-12-02 15:09:36Android 沉浸式状态栏 一体化状态栏 信号栏 -
Android底部导航栏、沉浸式状态栏
2015-11-11 14:30:28Android底部导航栏(也可称为顶部导航栏)、 Android沉浸式状态栏 -
Android沉浸式状态栏(透明系统状态栏)
2021-02-01 14:30:34Android沉浸式状态栏(透明系统状态栏)的目的:顶部系统状态栏和App的导航栏一体化,不给用户突兀的感觉,使用户把更多的视角留在我们的App上。 沉浸式状态栏的兼容情况 Android版本 透明状态栏 <4.4 ...Android沉浸式状态栏(透明系统状态栏)的目的:顶部系统状态栏和App的导航栏一体化,不给用户突兀的感觉,使用户把更多的视角留在我们的App上。
沉浸式状态栏的兼容情况
Android版本 透明状态栏 <4.4 × 4.4-5.0 ✓ >=5.0 ✓ Android版本 黑白字符状态栏 <6.0 × >=6.0 ✓ fitsSystemWindows
fitsSystemWindows,这个属性在沉浸式状态中扮演着非常重要的角色:
该属性只作用在sdk>=19的系统上就是高于4.4的系统,android:fitsSystemWindows默认值为false。基于系统窗口(如status bar)调整视图布局。只有在设置了透明状态栏(StatusBar)或者导航栏(NavigationBar)此属性才会生效,为true,将调整视图padding为系统窗口预留出空间。
当设置了透明状态栏(StatusBar)时:布局会扩展到StatusBar的位置,同时所有设置了android:fitsSystemWindows="true"属性的view会自动添加一个值等于状态栏高度的paddingTop
<item name="android:windowTranslucentStatus">true</item> 或者 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); }
当设置了透明导航栏(NavigationBar)时:布局会扩展到NavigationBar的位置,同时所有设置了android:fitsSystemWindows="true"属性的view会自动添加一个值等于导航栏高度的paddingBottom。
<item name="android:windowTranslucentNavigation">true</item> 或者 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); }
沉浸式状态栏实现的一般思路
-
4.4以下版本: 我们可以对StatusBar和 NavigationBar进行显示和隐藏操作,但无法实现沉浸式状态栏。
-
Android4.4(API 19) - Android 5.0(API 21): 通过FLAG_TRANSLUCENT_STATUS设置状态栏为透明并且为全屏模式,然后通过添加一个与StatusBar 一样大小的View,将View 的 background 设置为我们想要的颜色,从而来实现沉浸式。
-
Android 5.0(API 21)以上版本: 在Android 5.0的时候,加入了一个重要的属性和方法 android:statusBarColor (对应方法为 setStatusBarColor),通过这个方法我们就可以轻松实现沉浸式。也就是说,从Android5.0开始,系统才真正的支持沉浸式。
-
Android 6.0(API 23)以上版本: Android6.0以上的实现方式和Android 5.0 +是一样,但从Android 6.0(API 23)开始,我们可以改状态栏的绘制模式,可以显示白色或浅黑色的内容和图标(除了魅族手机,魅族自家有做源码更改,6.0以下就能实现)
Android4.4(API 19) - Android 5.0(API 21)实现沉浸式的方式
可以在代码中设置,如下:
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
也可以在theme 中设置属性windowTranslucentStatus,如下:
android:windowTranslucentStatus
效果如下:
效果如上图,可以看出,沉浸式的效果是出来了,但是也有一个问题,我们的标题栏和状态栏重叠了,相当于整个布局上移了StatusBar 的高度。为了让标题栏回到原来的位置,我们在标题栏的上方添加一个大小和StatusBar大小一样的View,View 的BackgroundColor 为标题栏一样的颜色,这个View起到一个占位的作用。这个时候,标题栏就会下移StatusBar的高度,回到正常的位置。/** * 设置状态栏颜色 * * @param activity 需要设置的activity * @param color 状态栏颜色值 * @param statusBarAlpha 状态栏透明度 */ public static void setColor(Activity activity, @ColorInt int color, int statusBarAlpha) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); activity.getWindow().setStatusBarColor(calculateStatusColor(color, statusBarAlpha)); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); int count = decorView.getChildCount(); if (count > 0 && decorView.getChildAt(count - 1) instanceof StatusBarView) { decorView.getChildAt(count - 1).setBackgroundColor(calculateStatusColor(color, statusBarAlpha)); } else { StatusBarView statusView = createStatusBarView(activity, color, statusBarAlpha); decorView.addView(statusView); } setRootView(activity); } } /** * 设置根布局参数 */ private static void setRootView(Activity activity) { ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0); rootView.setFitsSystemWindows(true); rootView.setClipToPadding(true); } /** * 计算状态栏颜色 * * @param color color值 * @param alpha alpha值 * @return 最终的状态栏颜色 */ private static int calculateStatusColor(@ColorInt int color, int alpha) { float a = 1 - alpha / 255f; int red = color >> 16 & 0xff; int green = color >> 8 & 0xff; int blue = color & 0xff; red = (int) (red * a + 0.5); green = (int) (green * a + 0.5); blue = (int) (blue * a + 0.5); return 0xff << 24 | red << 16 | green << 8 | blue; } /** * 生成一个和状态栏大小相同的半透明矩形条 * * @param activity 需要设置的activity * @param color 状态栏颜色值 * @param alpha 透明值 * @return 状态栏矩形条 */ private static StatusBarView createStatusBarView(Activity activity, @ColorInt int color, int alpha) { // 绘制一个和状态栏一样高的矩形 StatusBarView statusBarView = new StatusBarView(activity); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity)); statusBarView.setLayoutParams(params); statusBarView.setBackgroundColor(calculateStatusColor(color, alpha)); return statusBarView; }
其中StatusBarView 就是一个普通的View。
添加上述代码后,效果如下:
通过以上就可以实现Android 4.4 上的沉浸式状态栏。另外,如果是一张图片延伸到状态栏的话,直接设置FLAG_TRANSLUCENT_STATUS就可以了,如下:
小结:Android4.4上实现沉浸式状态栏的套路是:为window添加FLAG_TRANSLUCENT_STATUS Flag,然后添加一个和status bar 一样大小的View 站位,从而让让标题栏不会与status bar重叠。而图片延伸到状态栏只需要设置FLAG_TRANSLUCENT_STATUS就OK。
沉浸式在Android4.4 - Android5.0 之间的版本表现得不是很好,从上面贴的几张图就可以看出,状态栏的顶部有一个渐变,会显示出黑色的阴影(底部的导航栏也是一样的效果),在Android 5.0 版本已经被修复。
Android 5.0(API 21)以上实现沉浸式的方式
Android 5.0 是一个里程碑式的版本,从Android 5.0开始,Google 推出了全新的设计规范 Material Design,并且原生控件就可以实现一些炫酷的UI动效。从这个版本开始,google 加入了一个比较重要的方法setStatusBarColor (对应属性:android:statusBarColor),通过这个方法,可以很轻松地实现沉浸式状态栏。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); activity.getWindow().setStatusBarColor(calculateStatusColor(color, statusBarAlpha)); }
想要这个方法生效,必须还要配合一个Flag一起使用,必须设置FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS ,并且不能设置FLAG_TRANSLUCENT_STATUS(Android 4.4才用这个)
效果如下:
当然也可以直接在Theme中使用,在values-v21文件夹下添加如下主题:
<style name="MDTheme" parent="Theme.Design.Light.NoActionBar"> <item name="android:windowTranslucentStatus">false</item> <item name="android:windowDrawsSystemBarBackgrounds">true</item> <item name="android:statusBarColor">@android:color/holo_red_light</item> </style>
效果和上面代码中添加的效果一样。
图片延伸到状态栏
在Android 5.0使图片延伸到状态栏,只需设置windowTranslucentStatus,将 statusBarColor 设置为透明即可:
<style name="ImageTranslucentTheme" parent="Theme.AppCompat.DayNight.NoActionBar"> <item name="android:windowTranslucentNavigation">true</item> <item name="android:windowTranslucentStatus">true</item> <!-- 设置statusBarColor 为透明--> <item name="android:statusBarColor">@android:color/transparent</item> </style>
效果如下:
Android 6.0 + 实现状态栏字色和图标浅黑色
使用沉浸式的时候会遇到一个问题,那就是Android 系统状态栏的字色和图标颜色为白色,当我的主题色或者图片接近白色或者为浅色的时候,状态栏上的内容就看不清了。 这个问题在Android 6.0的时候得到了解决。Android 6.0 新添加了一个属性SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
添加如下代码:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); }
除了在代码中添加以外,还可以直接在主题中使用属性:
<style name="MDTheme" parent="Theme.Design.Light.NoActionBar"> <item name="android:windowTranslucentStatus">false</item> <item name="android:windowDrawsSystemBarBackgrounds">true</item> <item name="android:statusBarColor">@android:color/holo_red_light</item> <!-- Android 6.0以上 状态栏字色和图标为浅黑色--> <item name="android:windowLightStatusBar">true</item> </style>
注意:主题要放在values-v23文件夹下
参考以下文章
Android关于沉浸式状态栏总结沉浸式状态栏工具类:https://github.com/laobie/StatusBarUtil
-
-
Android开发 透明状态栏 沉浸式状态栏的实现
2021-02-08 22:09:23Android 透明状态栏Android 透明状态栏实现平台设置所需要的颜色填满屏幕状态栏透明化Padding调整状态栏颜色文字颜色设置(可选)颜色填充任务完成! 最近在做毕业设计,想要实现沉浸式状态栏,在网上搜索了好多的... -
Android悬浮窗口及状态栏显示应用图标
2013-06-03 11:42:51Android,悬浮窗口,状态栏显示应用图标 -
iOS 准确获取 iPhone 状态栏、导航栏、TabBar高度,看这篇就够了
2022-04-09 01:59:56最近在项目开发的时候,发现项目提供的获取 「状态栏+导航栏高度」的方法,在 iPhone 12 系列机型上是错误的。解决了这个问题之后,决定做个简单的总结,于是有了这篇文章。 -
android状态栏(沉浸式状态栏,改变状态栏字体颜色,背景颜色)
2018-12-03 22:12:01通过主题设置状态栏 在API21(android 5.0)之后,设置状态栏透明效果为半透明,并且为了保证在API19(android 4.4)之前的正常使用,所以需要3份不同的style文件。即values-v19(android 4.4之前使用),values-... -
Android 系统状态栏一体化
2015-03-09 16:23:50把系统状态栏集成到app当中,使得界面美观大方。 有这方面需求的朋友可以下载试试,具体情况可进博客一观。 博客地址:http://blog.csdn.net/aiyuan0204 注:这是android4.4新特性,只支持4.4以上版本 -
状态栏总结(沉浸式状态栏+状态栏颜色+状态栏字体的颜色)
2018-12-12 22:37:17今天总结一下状态栏的使用,当然也是参考别人的。但是总归自己得试试,然后把常用的几种情况记下来,因为这些东西是死的,下次拿过来就可以用。 一. 沉浸式状态栏 :Android 5.0以上才会支持沉浸式状态栏效果。 ... -
三星s9状态栏隐藏方法,三星s9状态栏隐藏小技巧
2021-03-07 10:18:28最近很多朋友都在问三星s9状态栏怎么隐藏,很多人不喜欢三星S9的手机状态栏,无奈不知道方法。小编马上就给大家分享三星s9手机隐藏状态栏的方法。第一步安装三星手机驱动(如果已经安装了驱动或系统自动安装的则可... -
Android 最新状态栏处理
2021-12-15 15:00:40Android 中状态栏的处理无非两种,一种是显示隐藏状态栏,另外一种是状态栏字体颜色的修改,之前的写法都已经废弃了,来看看最新的版本中应该如何处理吧。 -
Android 设置状态栏沉浸式
2022-03-23 11:17:19/** * 状态栏设置颜色(默认透明度) * * @param activity 需要设置的activity * @param color 状态栏颜色值 */ public static void setColor(Activity activity, @ColorInt int color) { setColor(activity, color, ... -
Android Jetpack Compose 沉浸式/透明状态栏 ProvideWindowInsets SystemUiController
2021-08-03 11:07:28Android Jetpack Compose 沉浸式/透明状态栏 ProvideWindowInsets SystemUiController前言代码解析添加依赖FitsSystemWindows透明状态栏内容不挡住状态栏完事 前言 从现在开始疯狂学习Jetpack Compose,经过一早上的... -
oppo状态栏美化
2021-06-07 05:44:53爱美化oppo状态栏美化是一款免费的手机状态栏美化软件,这款软件可以帮你美化oppo手机的状态栏。oppo状态栏美化可在手机上制作小米/华为/金立/VIVO/OPPO主题、字体等美化资源。如果你嫌弃手机原生系统的状态栏不好看... -
Android 动态获取状态栏高度并设置沉浸式状态栏(万能方法)
2018-12-24 15:17:50随着苹果刘海屏的出现,越来越多的android手机制造商开始出刘海屏,水滴屏,而手机不同的状态栏高度,导致UI设计的图,状态栏部分也会在不同手机上显示不一样,这个时候就需要适配了。 本文主要是动态获取手机的... -
Android状态栏微技巧,带你真正理解沉浸式模式
2016-08-23 07:32:55记得之前有朋友在留言里让我写一篇关于沉浸式状态栏的文章,正巧我确实有这个打算,那么本篇就给大家带来一次沉浸式状态栏的微技巧讲解。 其实说到沉浸式状态栏这个名字我也是感到很无奈,真不知道这种叫法是谁先... -
uni-app导航栏和状态栏配置
2020-11-25 11:22:04参考:官网uni-app导航栏开发指南。 1.1 原生导航栏-通用配置 (1) 原生导航优点 原生导航的体验更好,渲染新页面时,原生导航栏的渲染无需等待新页面dom加载,可以在新页面进入动画开始时就渲染。 原生导航还可以... -
隐藏状态栏 | Android 开发者 | Android Developers
2021-05-26 19:16:06本节课介绍如何在不同版本的 Android 上隐藏状态栏。隐藏状态栏(也可选择隐藏导航栏)可为内容腾出更多显示空间,从而提供更加身临其境的用户体验。图 1 显示了具有可见状态栏的应用:图 1. 状态栏可见。图 2 显示了... -
flutter 修改状态栏的颜色
2022-03-22 09:57:28在flutter载体页面,修改flutter页面的状态栏背景颜色,这两个方法就可以实现: /** * 6.0级以上的沉浸式布局 * */ public static void fitSystemBar(Activity activity) { if (Build.VERSION.SDK_INT < ... -
Android 沉浸式状态栏完美解决方案
2018-07-26 17:10:08} } 好了,这个类 支持了 设置状态栏透明, 设置状态栏颜色, 支持了状态栏深色浅色切换(则状态栏上的文字图标颜色) 怕搞了一堆在别的文章的配置,所以我还是要说下以下代码不能出现: 全局搜索你的代码里 是否有 ... -
Android沉浸式状态栏demo代码
2015-06-11 15:14:42主要介绍了Android沉浸式状态栏实现示例,小编觉得挺不错的,现在分享给大家下载,也给大家做个参考 -
Android 透明状态栏
2019-03-04 14:50:04最近公司产品提出透明状态栏的要求,将一张背景填充满屏幕,自己记录一下: Android 透明状态栏:有两种,背景是图片还是纯色,下面分开讲: 1.当背景为图片时,布局可以这么写: 方法1,在代码onCreate()方法...