精华内容
下载资源
问答
  • Android 设置状态栏颜色和状态栏文字、图标颜色
    千次阅读
    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沉浸状态栏和顶部状态栏背景色的设置,现在很多应用都引用了沉浸式状态栏这样的效果,这样的效果很酷炫。
  • 做过一段状态栏颜色适配,纯色的基本在style直接设置就可以了, 不过最近效果图有一种效果要求 是在状态栏部分也显示内容部分, 像下面这样
  • 实现4.4以后的状态栏与app的导航栏颜色相同,android沉浸式状态栏、变色状态栏、透明状态栏、修改状态栏颜色及透明。
  • 在Activity中或Fragment中任意改变状态栏的颜色,通过反射机制来获得状态栏的高度,通过点位View来叠加颜色
  • 2.状态栏为背景图片效果 状态栏为图片效果.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 沉浸式状态栏的实现方法、状态栏透明的实现方法
  • 在做项目的时候图片全屏预览时发现使用全面屏后,图片会闪动一下,带着问题来梳理一下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)及更高版本的设备上正式支持刘海屏。部分制造商也有在低版本上支持刘海屏。
    在带刘海屏的设备上为确保一致性和兼容性,有五点强制要求:

    1. 一条边缘最多只能包含一个刘海
    2. 一台设备不能有两个以上的刘海
    3. 设备的两条较长边缘上不能有刘海
    4. 在未设置特殊标志的竖屏模式下,状态栏的高度必须至少与刘海屏的高度一致
    5. 在默认情况下,在全屏模式或横屏模式下,整个刘海屏必须显示黑边

    第五条比较重要,这里设置全面屏时必须要考虑是否有刘海屏的这个性质

    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 及以后支持的,要和全面屏配合使用,不然只考虑全面屏,在刘海屏上的效果是不进人不如意的。

    Demo 在 系统栏里面

    站在巨人的肩膀上:
    1.MIUI 9 & 10“状态栏黑色字符”实现方法变更通知
    2.魅族开放平台-状态栏变色
    3.启用全屏模式
    4.支持刘海屏
    5.Android关于沉浸式状态栏总结

    展开全文
  • Android 沉浸式状态栏 一体化状态栏 信号栏
  • Android底部导航栏(也可称为顶部导航栏)、 Android沉浸式状态栏
  • Android沉浸式状态栏(透明系统状态栏)的目的:顶部系统状态栏和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

    效果如下:
    Alt
    效果如上图,可以看出,沉浸式的效果是出来了,但是也有一个问题,我们的标题栏和状态栏重叠了,相当于整个布局上移了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。
    添加上述代码后,效果如下:
    Alt
    通过以上就可以实现Android 4.4 上的沉浸式状态栏

    另外,如果是一张图片延伸到状态栏的话,直接设置FLAG_TRANSLUCENT_STATUS就可以了,如下:
    Alt

    小结: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才用这个)

    效果如下:
    Alt

    当然也可以直接在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>

    效果如下:
    Alt

    Android 6.0 + 实现状态栏字色和图标浅黑色

    使用沉浸式的时候会遇到一个问题,那就是Android 系统状态栏的字色和图标颜色为白色,当我的主题色或者图片接近白色或者为浅色的时候,状态栏上的内容就看不清了。 这个问题在Android 6.0的时候得到了解决。Android 6.0 新添加了一个属性SYSTEM_UI_FLAG_LIGHT_STATUS_BAR

    Alt

    添加如下代码:

    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 透明状态栏Android 透明状态栏实现平台设置所需要的颜色填满屏幕状态栏透明化Padding调整状态栏颜色文字颜色设置(可选)颜色填充任务完成! 最近在做毕业设计,想要实现沉浸式状态栏,在网上搜索了好多的...
  • Android,悬浮窗口,状态栏显示应用图标
  • 最近在项目开发的时候,发现项目提供的获取 「状态栏+导航栏高度」的方法,在 iPhone 12 系列机型上是错误的。解决了这个问题之后,决定做个简单的总结,于是有了这篇文章。
  • 通过主题设置状态栏 在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以上版本
  • 今天总结一下状态栏的使用,当然也是参考别人的。但是总归自己得试试,然后把常用的几种情况记下来,因为这些东西是死的,下次拿过来就可以用。 一. 沉浸式状态栏 :Android 5.0以上才会支持沉浸式状态栏效果。 ...
  • 最近很多朋友都在问三星s9状态栏怎么隐藏,很多人不喜欢三星S9的手机状态栏,无奈不知道方法。小编马上就给大家分享三星s9手机隐藏状态栏的方法。第一步安装三星手机驱动(如果已经安装了驱动或系统自动安装的则可...
  • Android 最新状态栏处理

    千次阅读 2021-12-15 15:00:40
    Android 中状态栏的处理无非两种,一种是显示隐藏状态栏,另外一种是状态栏字体颜色的修改,之前的写法都已经废弃了,来看看最新的版本中应该如何处理吧。
  • 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前言代码解析添加依赖FitsSystemWindows透明状态栏内容不挡住状态栏完事 前言 从现在开始疯狂学习Jetpack Compose,经过一早上的...
  • oppo状态栏美化

    千次阅读 2021-06-07 05:44:53
    爱美化oppo状态栏美化是一款免费的手机状态栏美化软件,这款软件可以帮你美化oppo手机的状态栏。oppo状态栏美化可在手机上制作小米/华为/金立/VIVO/OPPO主题、字体等美化资源。如果你嫌弃手机原生系统的状态栏不好看...
  • 随着苹果刘海屏的出现,越来越多的android手机制造商开始出刘海屏,水滴屏,而手机不同的状态栏高度,导致UI设计的图,状态栏部分也会在不同手机上显示不一样,这个时候就需要适配了。 本文主要是动态获取手机的...
  • Android状态栏微技巧,带你真正理解沉浸式模式

    万次阅读 多人点赞 2016-08-23 07:32:55
    记得之前有朋友在留言里让我写一篇关于沉浸式状态栏的文章,正巧我确实有这个打算,那么本篇就给大家带来一次沉浸式状态栏的微技巧讲解。 其实说到沉浸式状态栏这个名字我也是感到很无奈,真不知道这种叫法是谁先...
  • uni-app导航栏和状态栏配置

    千次阅读 2020-11-25 11:22:04
    参考:官网uni-app导航开发指南。 1.1 原生导航-通用配置 (1) 原生导航优点 原生导航的体验更好,渲染新页面时,原生导航的渲染无需等待新页面dom加载,可以在新页面进入动画开始时就渲染。 原生导航还可以...
  • 本节课介绍如何在不同版本的 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()方法...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 494,990
精华内容 197,996
关键字:

状态栏

友情链接: Nyx-master.zip