状态栏_状态栏设置 - CSDN
  • Android状态栏微技巧,带你真正理解沉浸式模式

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

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/51763825

    本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭霖 即可关注,每天都有文章更新。

    记得之前有朋友在留言里让我写一篇关于沉浸式状态栏的文章,正巧我确实有这个打算,那么本篇就给大家带来一次沉浸式状态栏的微技巧讲解。

    其实说到沉浸式状态栏这个名字我也是感到很无奈,真不知道这种叫法是谁先发起的。因为Android官方从来没有给出过沉浸式状态栏这样的命名,只有沉浸式模式(Immersive Mode)这种说法。而有些人在没有完全了解清楚沉浸模式到底是什么东西的情况下,就张冠李戴地认为一些系统提供的状态栏操作就是沉浸式的,并且还起了一个沉浸式状态栏的名字。

    比如之前就有一个QQ群友问过我,像饿了么这样的沉浸式状态栏效果该如何实现?

    这个效果其实就是让背景图片可以利用系统状态栏的空间,从而能够让背景图和状态栏融为一体。

    本篇文章当中我会教大家如何实现这样的效果,但这个真的不叫沉浸式状态栏。因此,这算是一篇技术+普及的文章吧,讲技术的同时也纠正一下大家之前错误的叫法。

    什么是沉浸式?

    先来分析一下叫错的原因吧,之所以很多人会叫错,是因为根本就不了解沉浸式是什么意思,然后就人云亦云跟着叫了。那么沉浸式到底是什么意思呢?

    根据百度百科上的定义,沉浸式就是要给用户提供完全沉浸的体验,使用户有一种置身于虚拟世界之中的感觉。

    比如说现在大热的VR就是主打的沉浸式体验。

    那么对应到Android操作系统上面,怎样才算是沉浸式体验呢?这个可能在大多数情况下都是用不到的,不过在玩游戏或者看电影的时候就非常重要了。因为游戏或者影视类的应用都希望能让用户完全沉浸在其中,享受它们提供的娱乐内容,但如果这个时候在屏幕的上方还显示一个系统状态栏的话,可能就会让用户分分钟产生跳戏的感觉。

    那么我们来看一下比较好的游戏都是怎么实现的,比如说海岛奇兵:

    海岛奇兵的这种模式就是典型的沉浸式模式,它的整个屏幕中显示都是游戏的内容,没有状态栏也没有导航栏,用户玩游戏的时候就可以完全沉浸在游戏当中,而不会被一些系统的界面元素所打扰。

    然后我们再来看一下爱奇艺的实现:

    同样也是类似的,爱奇艺将整个屏幕作为影视的展示区,用户在看电影的时候眼中就只会有电影的内容,这样就不会被其他一些无关的东西所分心。

    这才是沉浸式模式的真正含义,而所谓的什么沉浸式状态栏纯粹就是在瞎叫,完全都没搞懂“沉浸式” 这三个字是什么意思。

    不过虽然听上去好像是很高大上的沉浸式效果,实际看上去貌似就是将内容全屏化了而已嘛。没错,Android沉浸式模式的本质就是全屏化,不过我们今天的内容并不仅限于此,因为还要实现饿了么那样的状态栏效果。那么下面我们就开始来一步步学习吧。

    隐藏状态栏

    一个Android应用程序的界面上其实是有很多系统元素的,观察下图:

    可以看到,有状态栏、ActionBar、导航栏等。而打造沉浸式模式的用户体验,就是要将这些系统元素全部隐藏,只留下主体内容部分。

    比如说我现在新建了一个空项目,然后修改布局文件中的代码,在里面加入一个ImageView,如下所示:

    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@drawable/bg"
            android:scaleType="centerCrop" />
    
    </RelativeLayout>

    这里将ImageView的宽和高都设置成match_parent,让图片充满屏幕。现在运行一下程序,效果如下图所示。

    如果你将图片理解成游戏或者电影界面的话,那这个体验离沉浸式就差得太远了,至少状态栏和ActionBar得要隐藏起来了吧?没关系,我们一步步进行优化,并且在优化中学习。

    隐藏状态栏和ActionBar的方式在4.1系统之上和4.1系统之下还是不一样的,这里我就不准备考虑4.1系统之下的兼容性了,因为过于老的系统根本就没有提供沉浸式体验的支持。

    修改MainActivity中的代码,如下所示:

    public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            View decorView = getWindow().getDecorView();
            int option = View.SYSTEM_UI_FLAG_FULLSCREEN;
            decorView.setSystemUiVisibility(option);
            ActionBar actionBar = getSupportActionBar();
            actionBar.hide();
        }
    }

    这里先调用getWindow().getDecorView()方法获取到了当前界面的DecorView,然后调用它的setSystemUiVisibility()方法来设置系统UI元素的可见性。其中,SYSTEM_UI_FLAG_FULLSCREEN表示全屏的意思,也就是会将状态栏隐藏。另外,根据Android的设计建议,ActionBar是不应该独立于状态栏而单独显示的,因此状态栏如果隐藏了,我们同时也需要调用ActionBar的hide()方法将ActionBar也进行隐藏。

    现在重新运行一下程序,效果如下图所示。

    这样看上去就有点沉浸式效果的模样了。

    虽说这才是正统的沉浸式含义,但有些朋友可能想实现的就是饿了么那样的状态栏效果,而不是直接把整个系统状态栏给隐藏掉,那么又该如何实现呢?

    其实也很简单,只需要借助另外一种UI Flag就可以了,如下所示:

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if (Build.VERSION.SDK_INT >= 21) {
        View decorView = getWindow().getDecorView();
        int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
        decorView.setSystemUiVisibility(option);
        getWindow().setStatusBarColor(Color.TRANSPARENT);
    }
    ActionBar actionBar = getSupportActionBar();
    actionBar.hide();

    首先需要注意,饿了么这样的效果是只有5.0及以上系统才支持,因此这里先进行了一层if判断,只有系统版本大于或等于5.0的时候才会执行下面的代码。

    接下来我们使用了SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN和SYSTEM_UI_FLAG_LAYOUT_STABLE,注意两个Flag必须要结合在一起使用,表示会让应用的主体内容占用系统状态栏的空间,最后再调用Window的setStatusBarColor()方法将状态栏设置成透明色就可以了。

    现在重新运行一下代码,效果如下图所示。

    可以看到,类似于饿了么的状态栏效果就成功实现了。

    再声明一次,这种效果不叫沉浸式状态栏,也完全没有沉浸式状态栏这种说法,我们估且可以把它叫做透明状态栏效果吧。

    隐藏导航栏

    现在我们已经成功实现隐藏状态栏的效果了,不过屏幕下方的导航栏还比较刺眼,接下来我们就学习一下如何将导航栏也进行隐藏。

    其实实现的原理都是一样的,隐藏导航栏也就是使用了不同的UI Flag而已,修改MainActivity中的代码,如下所示:

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    View decorView = getWindow().getDecorView();
    int option = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
            | View.SYSTEM_UI_FLAG_FULLSCREEN;
    decorView.setSystemUiVisibility(option);
    ActionBar actionBar = getSupportActionBar();
    actionBar.hide();

    这里我们同时使用了SYSTEM_UI_FLAG_HIDE_NAVIGATION和SYSTEM_UI_FLAG_FULLSCREEN,这样就可以将状态栏和导航栏同时隐藏了。现在重新运行一下程序,效果如图所示。

    这次看上去好像终于是完全全屏化了,但其实上这离真正的沉浸式模式还差得比较远,因为在这种模式下,我们触摸屏幕的任意位置都会退出全屏。

    这显然不是我们想要的效果,因此这种模式的使用场景比较有限。

    除了隐藏导航栏之外,我们同样也可以实现和刚才透明状态栏类似的效果,制作一个透明导航栏:

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if (Build.VERSION.SDK_INT >= 21) {
        View decorView = getWindow().getDecorView();
        int option = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
        decorView.setSystemUiVisibility(option);
        getWindow().setNavigationBarColor(Color.TRANSPARENT);
        getWindow().setStatusBarColor(Color.TRANSPARENT);
    }
    ActionBar actionBar = getSupportActionBar();
    actionBar.hide();

    这里使用了SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION,表示会让应用的主体内容占用系统导航栏的空间,然后又调用了setNavigationBarColor()方法将导航栏设置成透明色。现在重新运行一下程序,效果如下图所示。

    真正的沉浸式模式

    虽说沉浸式导航栏这个东西是被很多人误叫的一种称呼,但沉浸式模式的确是存在的。那么我们如何才能实现像海岛奇兵以及爱奇艺那样的沉浸式模式呢?

    首先你应该确定自己是否真的需要这个功能,因为除了像游戏或者视频软件这类特殊的应用,大多数的应用程序都是用不到沉浸式模式的。

    当你确定要使用沉浸式模式,那么只需要重写Activity的onWindowFocusChanged()方法,然后加入如下逻辑即可:

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
        @Override
        public void onWindowFocusChanged(boolean hasFocus) {
            super.onWindowFocusChanged(hasFocus);
            if (hasFocus && Build.VERSION.SDK_INT >= 19) {
                View decorView = getWindow().getDecorView();
                decorView.setSystemUiVisibility(
                    View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
            }
        }
    
    }

    沉浸式模式的UI Flag就这些,也没什么好解释的,如果你需要实现沉浸式模式,直接将上面的代码复制过去就行了。需要注意的是,只有在Android 4.4及以上系统才支持沉浸式模式,因此这里也是加入了if判断。

    另外,为了让我们的界面看上去更像是游戏,这里我将MainActivity设置成了横屏模式:

    <activity android:name=".MainActivity" 
              android:screenOrientation="landscape">
        ...
    </activity>

    这样我们就实现类似于海岛奇兵和爱奇艺的沉浸式模式效果了,如下图所示。

    可以看到,界面默认情况下是全屏的,状态栏和导航栏都不会显示。而当我们需要用到状态栏或导航栏时,只需要在屏幕顶部向下拉,或者在屏幕右侧向左拉,状态栏和导航栏就会显示出来,此时界面上任何元素的显示或大小都不会受影响。过一段时间后如果没有任何操作,状态栏和导航栏又会自动隐藏起来,重新回到全屏状态。

    这就是最标准的沉浸式模式。


    关注我的技术公众号,每天都有优质技术文章推送。关注我的娱乐公众号,工作、学习累了的时候放松一下自己。

    微信扫一扫下方二维码即可关注:

            

    展开全文
  • Android 沉浸式状态栏完美解决方案

    万次阅读 多人点赞 2019-06-03 14:21:57
    国内很多类似的文章, 我只想说一个字, 真tm乱! 我看不懂… 评论里面 全在说无效什么的 (我试了也无效, 好厉害的样子) 不废话,回到正题, 首先贴上一个众所周知的库 SystemBarTint 我只要这个类 ...

    注明下,这里只讲状态栏,导航栏后面看有没有必要再讲
    国内很多类似的文章, 我只想说一个字, 真tm乱! 我看不懂… 评论里面 全在说无效什么的 (我试了也无效, 好厉害的样子)

    不废话,回到正题, 首先贴上一个众所周知的库 SystemBarTint
    我只要这个类
    https://github.com/jgilfelt/SystemBarTint/blob/master/library/src/com/readystatesoftware/systembartint/SystemBarTintManager.java
    然后复制到你的工程
    这里写图片描述
    这个类我就不多说了, 就是兼容4.x以上沉浸透明状态栏的 一个兼容类, 有空可以研究下
    #开始
    先贴工具类, 有部分代码参考自网上并有做改动, 但这,不重要…

     
    public class StatusBarUtil {
        public final static int TYPE_MIUI = 0;
        public final static int TYPE_FLYME = 1;
        public final static int TYPE_M = 3;//6.0
    
        @IntDef({TYPE_MIUI,
                TYPE_FLYME,
                TYPE_M})
        @Retention(RetentionPolicy.SOURCE)
        @interface ViewType {
        }
    
        /**
         * 修改状态栏颜色,支持4.4以上版本
         *
         * @param colorId 颜色
         */
        public static void setStatusBarColor(Activity activity, int colorId) {
    
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                Window window = activity.getWindow();
                window.setStatusBarColor(colorId);
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                //使用SystemBarTintManager,需要先将状态栏设置为透明
                setTranslucentStatus(activity);
                SystemBarTintManager systemBarTintManager = new SystemBarTintManager(activity);
                systemBarTintManager.setStatusBarTintEnabled(true);//显示状态栏
                systemBarTintManager.setStatusBarTintColor(colorId);//设置状态栏颜色
            }
        }
    
        /**
         * 设置状态栏透明
         */
        @TargetApi(19)
        public static void setTranslucentStatus(Activity activity) {
            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;
                decorView.setSystemUiVisibility(option);
                window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
                window.setStatusBarColor(Color.TRANSPARENT);
                //导航栏颜色也可以正常设置
                //window.setNavigationBarColor(Color.TRANSPARENT);
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                Window window = activity.getWindow();
                WindowManager.LayoutParams attributes = window.getAttributes();
                int flagTranslucentStatus = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
                attributes.flags |= flagTranslucentStatus;
                //int flagTranslucentNavigation = WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
                //attributes.flags |= flagTranslucentNavigation;
                window.setAttributes(attributes);
            }
        }
    
    
        /**
         *  代码实现android:fitsSystemWindows
         *
         * @param activity
         */
        public static void setRootViewFitsSystemWindows(Activity activity, boolean fitSystemWindows) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                ViewGroup winContent = (ViewGroup) activity.findViewById(android.R.id.content);
                if (winContent.getChildCount() > 0) {
                    ViewGroup rootView = (ViewGroup) winContent.getChildAt(0);
                    if (rootView != null) {
                        rootView.setFitsSystemWindows(fitSystemWindows);
                    }
                }
            }
    
        }
    
    
        /**
         * 设置状态栏深色浅色切换 
         */
        public static boolean setStatusBarDarkTheme(Activity activity, boolean dark) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    setStatusBarFontIconDark(activity, TYPE_M, dark);
                } else if (OSUtils.isMiui()) {
                    setStatusBarFontIconDark(activity, TYPE_MIUI, dark);
                } else if (OSUtils.isFlyme()) {
                    setStatusBarFontIconDark(activity, TYPE_FLYME, dark);
                } else {//其他情况
                    return false;
                }
    
                return true;
            }
            return false;
        }
    
        /**
         * 设置 状态栏深色浅色切换
         */
        public static boolean setStatusBarFontIconDark(Activity activity, @ViewType int type,boolean dark) {
            switch (type) {
                case TYPE_MIUI:
                    return setMiuiUI(activity, dark);
                case TYPE_FLYME:
                    return setFlymeUI(activity, dark);
                case TYPE_M:
                default:
                    return setCommonUI(activity,dark);
            }
        }
    
        //设置6.0 状态栏深色浅色切换
        public static boolean setCommonUI(Activity activity, boolean dark) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                View decorView = activity.getWindow().getDecorView();
                if (decorView != null) {
                    int vis = decorView.getSystemUiVisibility();
                    if (dark) {
                        vis |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
                    } else {
                        vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
                    }
                    if (decorView.getSystemUiVisibility() != vis) {
                        decorView.setSystemUiVisibility(vis);
                    }
                    return true;
                }
            }
            return false;
    
        }
    
        //设置Flyme 状态栏深色浅色切换
        public static boolean setFlymeUI(Activity activity, boolean dark) {
            try {
                Window window = activity.getWindow();
                WindowManager.LayoutParams lp = window.getAttributes();
                Field darkFlag = WindowManager.LayoutParams.class.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
                Field meizuFlags = WindowManager.LayoutParams.class.getDeclaredField("meizuFlags");
                darkFlag.setAccessible(true);
                meizuFlags.setAccessible(true);
                int bit = darkFlag.getInt(null);
                int value = meizuFlags.getInt(lp);
                if (dark) {
                    value |= bit;
                } else {
                    value &= ~bit;
                }
                meizuFlags.setInt(lp, value);
                window.setAttributes(lp);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
    
        //设置MIUI 状态栏深色浅色切换
        public static boolean setMiuiUI(Activity activity, boolean dark) {
            try {
                Window window = activity.getWindow();
                Class<?> clazz = activity.getWindow().getClass();
                @SuppressLint("PrivateApi") Class<?> layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
                Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
                int darkModeFlag = field.getInt(layoutParams);
                Method extraFlagField = clazz.getDeclaredMethod("setExtraFlags", int.class, int.class);
                extraFlagField.setAccessible(true);
                if (dark) {    //状态栏亮色且黑色字体
                    extraFlagField.invoke(window, darkModeFlag, darkModeFlag);
                } else {
                    extraFlagField.invoke(window, 0, darkModeFlag);
                }
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }
        //获取状态栏高度
        public static int getStatusBarHeight(Context context) {
            int result = 0;
            int resourceId = context.getResources().getIdentifier(
                    "status_bar_height", "dimen", "android");
            if (resourceId > 0) {
                result = context.getResources().getDimensionPixelSize(resourceId);
            }
            return result;
        }
    }
    

    好了,这个类 支持了 设置状态栏透明, 设置状态栏颜色, 支持了状态栏深色浅色切换(则状态栏上的文字图标颜色)

    停!

    如果你在看这篇文章, 先不要看别的文章 (23333…) .因为可能会扰乱你的思路,导致你无法理解和使用, 并且你乱入的代码会干扰这边的代码正常工作, 先删掉你在别的文章的代码修改, 相信我, 我这边啥都不用做, 你绝对能以最简单的方式 让你的项目实现沉浸状态栏兼容~ 包括图片沉浸!

    前期准备:
    我就怕你搞了一堆在别的文章的配置,所以我还是要说下以下代码不能出现:
    全局搜索你的代码里 是否有 android:fitsSystemWindows , 删掉!, 没错 删掉!!!
    检查你的values、values-v19、values-v21等 是否配置了
    如下item标签

    // values-v19。v19 开始有 android:windowTranslucentStatus 这个属性
    <style name="TranslucentTheme" parent="Theme.AppCompat.Light.NoActionBar">
            <item name="android:windowTranslucentStatus">true</item>
            <item name="android:windowTranslucentNavigation">true</item>
    </style>
    
    // values-v21。5.0 以上提供了 setStatusBarColor()  方法设置状态栏颜色。
    <style name="TranslucentTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:windowTranslucentStatus">false</item>
        <item name="android:windowTranslucentNavigation">true</item>
        <!--Android 5.x开始需要把颜色设置透明,否则导航栏会呈现系统默认的浅灰色-->
        <item name="android:statusBarColor">@android:color/transparent</item>
    </style>
    

    凡是在style.xml中 有关 windowTranslucentNavigation、windowTranslucentStatus、statusBarColor 统统删掉,全部删掉~
    因为 我们要通过代码去实现, xml中的各种属性全部不要写, 避免代码出现互相干扰, 会出现各种 为啥啊无效啊的bug

    这里我要吐槽一下, 我浏览过很多文章博客, 关于状态栏适配, 一会儿在java 设置setFitsSystemWindows setStatusBarColor 一会儿又回到布局里设置 android:fitsSystemWindows=“xxx” ,一会儿在style 配置 android:windowTranslucentStatus等 一会儿又使用工具类 设置FLAG_TRANSLUCENT_NAVIGATION …,然后还什么4.4 5.x各一份 style ,甚至还拿colorPrimary来乱入一通, 搞得是真的乱! 不信你看完我写的之后再去看别的, 有的说的不全 比如漏了图片如何沉浸没讲 , 或者是漏了图片沉浸后 布局也跟着沉浸进状态栏如何解决没讲… 唉…

    好了干扰已全部去除,开始适配

    首先在Activity 的setContentView 下一行编写如下代码(一般你可以写到你的BaseActivity里,否则你每个activity都得写一次)

    @Override
    protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.xxx);
       
       //这里注意下 因为在评论区发现有网友调用setRootViewFitsSystemWindows 里面 winContent.getChildCount()=0 导致代码无法继续
       //是因为你需要在setContentView之后才可以调用 setRootViewFitsSystemWindows 
       
       //当FitsSystemWindows设置 true 时,会在屏幕最上方预留出状态栏高度的 padding
       StatusBarUtil.setRootViewFitsSystemWindows(this,true);
       //设置状态栏透明
       StatusBarUtil.setTranslucentStatus(this);
       //一般的手机的状态栏文字和图标都是白色的, 可如果你的应用也是纯白色的, 或导致状态栏文字看不清
       //所以如果你是这种情况,请使用以下代码, 设置状态使用深色文字图标风格, 否则你可以选择性注释掉这个if内容
       if (!StatusBarUtil.setStatusBarDarkTheme(this, true)) {
            //如果不支持设置深色风格 为了兼容总不能让状态栏白白的看不清, 于是设置一个状态栏颜色为半透明,
            //这样半透明+白=灰, 状态栏的文字能看得清
            StatusBarUtil.setStatusBarColor(this,0x55000000);
       } 
    }
    

    上面先这样 由于界面风格很多, 比如同一个app有 的界面是黑色风格的页面, 有的是白色风格的页面,有的是顶部是图片的界面希望沉浸进去 这样更好看, 同时 此时状态栏文字要跟随改变

    其实都不用我解释了 就按自己需求 配置呗,工具类都写好功能了.
    比如我这个 4个不同的fragment,有一个是白色, 另外两个是顶部是图片的
    我是这样切换状态栏文字深浅色的,你们参考下
    0界面设置状态栏黑色图标
    这里写图片描述
    123界面设置状态栏白色图标
    这里写图片描述
    代码:
    这里写图片描述
    你还可以随时使用
    StatusBarUtil.setStatusBarColor(this,颜色值);
    设置不同fragment时 的状态栏颜色

    至此 你明白了设置状态栏颜色 和 随界面切换时 该怎么改状态栏颜色或状态栏文字颜色, 没错,你没有漏看! 用了我这个你不需要在xml中 或style中设置各种属性, 也不用判断什么4.4 啊 5.0 啊 6.0啊怎么处理… 就是这么神奇!~

    现在来一个蛋疼的问题

    我要把图片也沉浸进去!!!

    通常 你使用我刚才的代码时 相同颜色的界面没啥问题,比如:
    这里写图片描述
    但 当你界面顶部是图片界面的时候 或者 标题颜色不一样时
    成了这鬼样子,
    这里写图片描述
    这是因为我前面设置了 setRootViewFitsSystemWindows(this,true); 于是带有 paddingTop=状态栏高度 的效果

    首先 你可以选择两种应对办法
    如果顶部不是图片布局 , 可以直接使用 setStatusBarColor 设置相同颜色即可
    如果顶部是图片布局, 那么问题来了
    这里注意了

    想要图片沉浸, 必须设置fitsSystemWindows=false, 以去掉padding效果, 然后想办法 把图片上层的 其他View 整体 paddingTop=状态栏高 让其他View向下挪动

    这句话一定要理解
    ,现在试试在当前带图片的activity 重新设置setRootViewFitsSystemWindows(this,false);
    效果如下(你会发现图标跑左边了, 请无视, 将就看,我是在现有项目中演示的 )
    去掉padding效果后 图片沉浸了! 但内容进入了状态栏里 被遮挡.
    这里写图片描述
    那, 怎么以最方便的方式 让整个内容布局 往下挪动?

    有很多教程都是写的是在代码里 findView 然后设置padding , 很是麻烦, 要是多个界面都这样 代码岂止乱?
    曾经试图在xml中使用 状态栏高度值 ,结果发现这是几乎是不可能的, 因为编译后 xml固定了值,除非使用反射, 但这到了安卓9.0不能反射系统api怎么办…
    于是我想出了一种解决办法

    自定义一个View ,用来做状态栏高度占位

    /**
     * 作者:东芝
     * 功能:状态栏高度View,用于沉浸占位
     */
    
    public class StatusBarHeightView extends LinearLayout {
        private int statusBarHeight;
        private int type;
    
        public StatusBarHeightView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
            init(attrs);
        }
    
        public StatusBarHeightView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init(attrs);
        }
    
        public StatusBarHeightView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
            super(context, attrs, defStyleAttr);
            init(attrs);
    
    
        }
    
        private void init(@Nullable AttributeSet attrs) {
    
            int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                if(resourceId>0) {
                    statusBarHeight = getResources().getDimensionPixelSize(resourceId);
                }
            }else{
                //低版本 直接设置0
                statusBarHeight = 0;
            }
            if (attrs != null) {
                TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.StatusBarHeightView);
                type = typedArray.getInt(R.styleable.StatusBarHeightView_use_type, 0);
                typedArray.recycle();
            }
            if (type == 1) {
                setPadding(getPaddingLeft(), statusBarHeight, getPaddingRight(), getPaddingBottom());
            }
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            if (type == 0) {
                setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                        statusBarHeight);
            } else {
                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            }
        }
     
    }
    

    attrs.xml

        <declare-styleable name="StatusBarHeightView">
            <attr name="use_type" format="integer">
                <enum name="use_height" value="0" />
                <enum name="use_padding_top" value="1" />
            </attr>
        </declare-styleable>
    

    代码很简单, 就是写一个View, 支持paddingTop= 状态栏高度值 的View,
    解释下两个类型:
    use_height: 设置当前布局高度=状态栏高度值 用于无子View时的占位
    use_padding_top: 设置当前顶部padding=状态栏高度值 用于有子View时的占位
    适配低于4.4时 占位View的高度为0 所以不可见

    使用方法, 用StatusBarHeightView 来包住你要往下移动的内容! 单独留出要沉浸的View不包住,
    举例:

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >		
         <!--顶部的需要沉浸的图片View 或其他东西 视频View  等 -->     
         <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@mipmap/icon_top_bg"
            android:scaleType="centerCrop" />
                        
    <!-- app:use_type="use_padding_top 向上paddingTop状态栏高度-->
    	<com.xxx.views.StatusBarHeightView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentEnd="true"
            android:layout_marginEnd="@dimen/widget_size_5"
            app:use_type="use_padding_top"
            android:orientation="vertical" >
    		 <!--这里放内容布局或View-->
             <ImageView
                  android:id="@+id/ivUserShare"
                  android:layout_width="@dimen/title_height"
                  android:layout_height="@dimen/title_height"
                  android:padding="@dimen/widget_size_10"
                  android:src="@mipmap/icon_share_white" />
                            
    	</com.xxx.views.StatusBarHeightView>
    </RelativeLayout>
    
    //不要忘记了, 在当前activity onCreate中设置 取消padding,  因为这个padding 我们用代码实现了,不需要系统帮我
    StatusBarUtil.setRootViewFitsSystemWindows(this,false);
    

    java 代码这边不需要改动, 运行app即可.
    完美!
    这里写图片描述

    结束

    值得注意的是 如果你按我那样去做, 状态栏颜色无法被修改, 请检查上层布局是否设置了背景
    或者受了全局主题的

    <item name="android:windowBackground">@color/xxx</item>
    

    的颜色影响

    好了,可能有不对的地方望指出, 或出现任何兼容性适配问题 欢迎在下方评论

    关于兼容性

    该功能已通过大量真机测试, 低版本4.1到安卓9.0 的手机均未出现状态栏错位,颜色重叠显示不清,等问题,而且 app发布到国内外均未反映关于 这个状态栏适配方案导致的bug 或 其他问题, 可放心食用. 至于有一些文章说到 侧滑布局 DrawerLayout 需要特殊处理… 放心, 本文的兼容方案是获取activity 根层布局来处理实现兼容的, 与activity里面是什么布局 无关.

    ================================================================

    ================================================================

    补充:

    ================================================================

    ================================================================

    感谢@Narbolo 的提醒, 漏了个Rom类型判断的工具类,现在贴上

    
    public class OSUtils {
    
        public static final String ROM_MIUI = "MIUI";
        public static final String ROM_EMUI = "EMUI";
        public static final String ROM_FLYME = "FLYME";
        public static final String ROM_OPPO = "OPPO";
        public static final String ROM_SMARTISAN = "SMARTISAN";
        public static final String ROM_VIVO = "VIVO";
        public static final String ROM_QIKU = "QIKU";
    
        private static final String KEY_VERSION_MIUI = "ro.miui.ui.version.name";
        private static final String KEY_VERSION_EMUI = "ro.build.version.emui";
        private static final String KEY_VERSION_OPPO = "ro.build.version.opporom";
        private static final String KEY_VERSION_SMARTISAN = "ro.smartisan.version";
        private static final String KEY_VERSION_VIVO = "ro.vivo.os.version";
    
        private static String sName;
        private static String sVersion;
    
        public static boolean isEmui() {
            return check(ROM_EMUI);
        }
    
        public static boolean isMiui() {
            return check(ROM_MIUI);
        }
    
        public static boolean isVivo() {
            return check(ROM_VIVO);
        }
    
        public static boolean isOppo() {
            return check(ROM_OPPO);
        }
    
        public static boolean isFlyme() {
            return check(ROM_FLYME);
        }
    
        public static boolean is360() {
            return check(ROM_QIKU) || check("360");
        }
    
        public static boolean isSmartisan() {
            return check(ROM_SMARTISAN);
        }
    
        public static String getName() {
            if (sName == null) {
                check("");
            }
            return sName;
        }
    
        public static String getVersion() {
            if (sVersion == null) {
                check("");
            }
            return sVersion;
        }
    
        public static boolean check(String rom) {
            if (sName != null) {
                return sName.equals(rom);
            }
    
            if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_MIUI))) {
                sName = ROM_MIUI;
            } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_EMUI))) {
                sName = ROM_EMUI;
            } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_OPPO))) {
                sName = ROM_OPPO;
            } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_VIVO))) {
                sName = ROM_VIVO;
            } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_SMARTISAN))) {
                sName = ROM_SMARTISAN;
            } else {
                sVersion = Build.DISPLAY;
                if (sVersion.toUpperCase().contains(ROM_FLYME)) {
                    sName = ROM_FLYME;
                } else {
                    sVersion = Build.UNKNOWN;
                    sName = Build.MANUFACTURER.toUpperCase();
                }
            }
            return sName.equals(rom);
        }
    
        public static String getProp(String name) {
            String line = null;
            BufferedReader input = null;
            try {
                Process p = Runtime.getRuntime().exec("getprop " + name);
                input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);
                line = input.readLine();
                input.close();
            } catch (IOException ex) {
                return null;
            } finally {
                if (input != null) {
                    try {
                        input.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return line;
        }
    }
    
    
    展开全文
  • iphone 完美状态栏

    2020-07-29 14:18:49
    iPhone 6.0-6.1.2 完美状态栏
  • 状态栏

    2020-07-22 14:43:16
    import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; import android.graphics.Color;...import android.os.Build;...import android.support.annotation.ColorInt;...
    package xbsoft.com.commonlibrary.utils;
    
    import android.annotation.TargetApi;
    import android.app.Activity;
    import android.content.Context;
    import android.graphics.Color;
    import android.os.Build;
    import android.support.annotation.ColorInt;
    import android.support.annotation.IntRange;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.WindowManager;
    import android.widget.LinearLayout;
    
    import xbsoft.com.commonlibrary.R;
    
    
    public class StatusBarUtil {
        private static final int FAKE_STATUS_BAR_VIEW_ID = R.id.statusbarutil_fake_status_bar_view;
    
        /**
         * 设置状态栏全透明
         *
         * @param activity 需要设置的activity
         */
        public static void setTransparent(Activity activity) {
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
                return;
            }
            transparentStatusBar(activity);
            setRootView(activity);
        }
    
        /**
         * 使状态栏透明
         */
        @TargetApi(Build.VERSION_CODES.KITKAT)
        private static void transparentStatusBar(Activity activity) {
            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);
                //需要设置这个flag contentView才能延伸到状态栏
                activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
                //状态栏覆盖在contentView上面,设置透明使contentView的背景透出来
                activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
            } else {
                //让contentView延伸到状态栏并且设置状态栏颜色透明
                activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            }
        }
    
        /**
         * 设置根布局参数
         */
        private static void setRootView(Activity activity) {
            ViewGroup parent = (ViewGroup) activity.findViewById(android.R.id.content);
            for (int i = 0, count = parent.getChildCount(); i < count; i++) {
                View childView = parent.getChildAt(i);
                if (childView instanceof ViewGroup) {
                    childView.setFitsSystemWindows(true);
                    ((ViewGroup) childView).setClipToPadding(true);
                }
            }
        }
    
        /*隐藏状态栏但不隐藏状态栏字体*/
        public static void hideStatusBar(Activity activity) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);//隐藏状态栏但不隐藏状态栏字体
            }
        }
    
    
        /**
         * 设置状态栏颜色
         *
         * @param activity       需要设置的activity
         * @param color          状态栏颜色值
         * @param statusBarAlpha 状态栏透明度
         */
    
        public static void setColor(Activity activity, @ColorInt int color, @IntRange(from = 0, to = 255) int statusBarAlpha) {
    
            //5.0以上版本直接设置状态栏颜色透明度
            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));
                //4.4-5.0版本通过伪装一个状态栏来设置颜色和透明度
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
                ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
                View fakeStatusBarView = decorView.findViewById(FAKE_STATUS_BAR_VIEW_ID);
                if (fakeStatusBarView != null) {
                    if (fakeStatusBarView.getVisibility() == View.GONE) {
                        fakeStatusBarView.setVisibility(View.VISIBLE);
                    }
                    fakeStatusBarView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha));
                } else {
                    decorView.addView(createStatusBarView(activity, color, statusBarAlpha));
                }
                setRootView(activity);
            }
        }
    
        /**
         * 计算状态栏颜色
         *
         * @param color color值
         * @param alpha alpha值
         * @return 最终的状态栏颜色
         */
        private static int calculateStatusColor(@ColorInt int color, int alpha) {
            if (alpha == 0) {
                return color;
            }
            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 View createStatusBarView(Activity activity, @ColorInt int color, int alpha) {
            // 绘制一个和状态栏一样高的矩形
            View statusBarView = new View(activity);
            LinearLayout.LayoutParams params =
                    new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity));
            statusBarView.setLayoutParams(params);
            statusBarView.setBackgroundColor(calculateStatusColor(color, alpha));
            statusBarView.setId(FAKE_STATUS_BAR_VIEW_ID);
            return statusBarView;
        }
    
        /**
         * 获取状态栏高度
         *
         * @param context context
         * @return 状态栏高度
         */
        public static int getStatusBarHeight(Context context) {
            // 获得状态栏高度
            int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
            return context.getResources().getDimensionPixelSize(resourceId);
        }
    }
    

     

    展开全文
  • Android状态栏详解(全网最详)

    千次阅读 2020-05-11 08:38:12

    一、前言

    由于安卓系统的差异,以及碎片化的日益严重导致一个状态栏的控制要花费大量的功夫进行适配。状态栏导航栏算是在开发中比较常见的,但是一直都没有完全的搞懂,总是遇到一个问题解决一个问题,今天把这些知识点罗列在一起,不说完全搞懂状态栏这个东西,起码在以后用到的时候不会出现解决不了的问题。
    这篇文章首先会对系统各个版本控制状态栏的api以及实现的效果做介绍,然后对各个flag进行分析,最后对fitsSystemWindows这个属性进行详解,因为这个属性迷惑了我好久。

    二、状态栏

    android的状态栏大致经历了以下几个阶段:

    • 在android4.4以下就不要想着对状态栏做什么文章了,现在app的适配一般也是在android4.4以上了。
    • 在android4.4—android5.0可以实现状态栏的变色,但是效果还不是很好,主要实现方式是通过FLAG_TRANSLUCENT_STATUS这个属性设置状态栏为透明并且为全屏模式,然后通过添加一个与StatusBar 一样大小的View,将View 设置为我们想要的颜色,从而来实现状态栏变色。
    • 在android5.0—android6.0系统才真正的支持状态栏变色,系统加入了一个重要的属性和方法 android:statusBarColor (对应方法为 setStatusBarColor),通过这个属性可以直接设置状态栏的颜色。
    • 在android6.0上主要就是添加了一个功能可以修改状态栏上内容和图标的颜色(黑色和白色)

    1. android4.4—android5.0

    前面已经说过了android4.4—android5.0主要通过FLAG_TRANSLUCENT_STATUS这个属性实现状态栏变色,当使用这个flag时SYSTEM_UI_FLAG_LAYOUT_STABLESYSTEM_UI_FLAG_LAYOUT_FULLSCREEN会被自动添加。

    • 对于4.4的机型,小米和魅族是透明色,而其它系统上就只是黑色到透明色的渐变。
    • 对于5.x的机型,大部分是使背景色半透明,小米和魅族以及其它少数机型会全透明
    • 对于6.0以上的机型,设置此flage会使得StatusBar完全透明
    • 对于7.0以上的机型,设置此flage会使得StatusBar半透明

    这个属性怎样添加呢?有两种方式:

    1. 在代码中设置:activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    2. 在主题中设置:<item name="android:windowTranslucentStatus">true</item>

    布局代码:

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <LinearLayout
            android:id="@+id/imageView"
            android:layout_width="0dp"
            android:layout_height="100dp"
            android:background="@android:color/holo_red_dark"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="TextView"
                android:textColor="@android:color/white" />
        </LinearLayout>
    </android.support.constraint.ConstraintLayout>
    

    具体效果:
    在这里插入图片描述
    我在上面的图片中加了一个textview,可以看到这个textview绘制到了顶部的状态栏中,还记得之前说的方法吗,这时就需要添加一个view填充在状态栏上,view的高度就是状态栏的高度,颜色就是你想要的状态栏的颜色。大致的代码如下:

            ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
            int       count     = decorView.getChildCount();
            //判断是否已经添加了statusBarView
            if (count > 0 && decorView.getChildAt(count - 1) instanceof StatusBarView) {
                decorView.getChildAt(count - 1).setBackgroundColor(calculateStatusColor(color, statusBarAlpha));
            } else {
                //新建一个和状态栏高宽的view
                StatusBarView statusView = createStatusBarView(activity, color, statusBarAlpha);
                decorView.addView(statusView);
            }
            ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
            //rootview不会为状态栏留出状态栏空间
            ViewCompat.setFitsSystemWindows(rootView,true);
            rootView.setClipToPadding(true);
    
        private static StatusBarView createStatusBarView(Activity activity, 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;
        }
    

    2. android5.0—android6.0

    在开始之前首先看一张图片:
    在这里插入图片描述
    上面的那些颜色都是可以在主题中设置的,如果没有动态改变他们会一直遵循这个原则。
    android5.0是android的一次重大更新,很多api都是在这个版本上添加的(真想最低适配android5.0),setStatusBarColor是专门用来设置状态栏颜色的,但是让这个方法生效有一个前提条件:你必须给window添加FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS并且取消FLAG_TRANSLUCENT_STATUS,前面已经说过了FLAG_TRANSLUCENT_STATUS会让状态栏透明,那FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS这个属性又是干嘛的呢?

    解释:设置了FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,表明会Window负责系统bar的background 绘制,绘制透明背景的系统bar(状态栏和导航栏),然后用getStatusBarColor()和getNavigationBarColor()的颜色填充相应的区域。这就是Android 5.0 以上实现沉浸式导航栏的原理。

    添加方式也有两种:

    1. 在代码中设置:getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); getWindow().setStatusBarColor(ContextCompat.getColor(this,android.R.color.holo_blue_dark));
    2. 在布局文件中设置(因为是android5.0新添加的属性,所以在添加到values-v21文件夹下的主题中):<item name="android:windowTranslucentStatus">false</item> <item name="android:windowDrawsSystemBarBackgrounds">true</item> <item name="android:statusBarColor">@android:color/holo_blue_dark</item>

    具体效果:
    在这里插入图片描述
    可以看到状态栏的颜色和我们设置的一样,并且布局内容也自动移到了状态栏下方。

    3. android6.0

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

    解释:为setSystemUiVisibility(int)方法添加的Flag,请求status bar 绘制模式,它可以兼容亮色背景的status bar 。要在设置了FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag ,同时清除了FLAG_TRANSLUCENT_STATUS flag 才会生效。

    添加方式同样是两种:

    1. 在代码中设置: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); }
    2. 在布局文件中设置(因为是android5.0新添加的属性,所以在添加到values-v23文件夹下的主题中):<item name="android:windowLightStatusBar">true</item>

    三、setSystemUiVisibility

    这个属性可能很多人没有用过,但是他对于状态栏的控制是非常重要的,这个方法主要是用来设置系统ui的可见性以及和用户布局的位置关系。使用方式如下:

    getWindow().getDecorView().setSystemUiVisibility(flag);
    

    下面着重介绍一下flag:

    • SYSTEM_UI_FLAG_FULLSCREEN(4.1+):隐藏状态栏,手指在屏幕顶部往下拖动,状态栏会再次出现且不会消失,另外activity界面会重新调整大小,直观感觉就是activity高度有个变小的过程。
      在这里插入图片描述
    • SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN(4.1+):状态栏一直存在并且不会挤压activity高度,状态栏会覆盖在activity之上
      在这里插入图片描述
    • SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN(4.1+):配合SYSTEM_UI_FLAG_FULLSCREEN一起使用,效果使得状态栏出现的时候不会挤压activity高度,状态栏会覆盖在activity之上
      在这里插入图片描述
    • SYSTEM_UI_FLAG_HIDE_NAVIGATION(4.0+):会使得虚拟导航栏隐藏,但是由于NavigationBar是非常重要的,因此只要有用户交互(例如点击一个 button),系统就会清除这个flag使NavigationBar就会再次出现,同时activity界面会被挤压 。
      在这里插入图片描述
    • SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION(4.1+):效果使得导航栏不会挤压activity高度,导航栏会覆盖在activity之上。
      在这里插入图片描述
    • SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION(4.1+):配合 SYSTEM_UI_FLAG_HIDE_NAVIGATION 一起使用,效果使得导航栏出现的时候不会挤压activity高度,导航栏会覆盖在activity之上。
      在这里插入图片描述
    • SYSTEM_UI_FLAG_IMMERSIVE配合SYSTEM_UI_FLAG_HIDE_NAVIGATION一起使用,还记得之前使用SYSTEM_UI_FLAG_HIDE_NAVIGATION之后只要有用户交互,系统就会清除这个flag使NavigationBar就会再次出现,和SYSTEM_UI_FLAG_IMMERSIVE一起使用之后就会使SYSTEM_UI_FLAG_HIDE_NAVIGATION必须手指在屏幕底部往上拖动NavigationBar才会出现。
      在这里插入图片描述
    • SYSTEM_UI_FLAG_IMMERSIVE_STICKY配合View.SYSTEM_UI_FLAG_FULLSCREEN和View.SYSTEM_UI_FLAG_HIDE_NAVIGATION一起使用,会使状态栏和导航栏以透明的形式出现,并且一段时间后自动消失。
      在这里插入图片描述
    • SYSTEM_UI_FLAG_LAYOUT_STABLE:稳定布局,主要是在全屏和非全屏切换时,布局不要有大的变化。一般和View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN、View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION搭配使用。(其实具体有什么用我也没搞懂)

    四、fitsystemwindow

    终于写到坑我最深的东西了,这个属性相信所有人都用过,也都知道他的作用,但是总是碰到和期望不一样的效果,这个属性的作用就是**通过设置View的padding,使得应用的content部分——Activity中setContentView()中传入的就是content——不会与system window重叠。**也就是让内容布局向下调整一段距离。
    还有一些事情需要注意:

    • fitsSystemWindows 需要被设置给根View——这个属性可以被设置给任意View,但是只有根View(content部分的根)外面才是SystemWindow,所以只有设置给根View才有用。
    • 其它padding将通通被覆盖。需要注意,如果你对一个View设置了android:fitsSystemWindows=“true”,那么你对该View设置的其他padding将通通无效。

    到这里是不是有一个疑问,在使用系统提供的一些控件时fitsSystemWindows 这个属性有时候并不仅仅是设置给最外层的根布局,他的子view也会添加这个属性,不是说这个属性只对根view才有效吗?其实这句话本身没问题,但是他的的前提是没有对view进行重写,在KITKAT及以下的版本,你的自定义View能够通过覆盖fitsSystemWindows() : boolean函数,来增加自定义行为。如果返回true,意味着你已经占据了整个Insets,如果返回false,意味着给其他的View依旧留有机会。所以当我们返回false时子view的fitsSystemWindows 就有效。(举个例子:CoordinatorLayout嵌套AppBarLayout嵌套CollapsingToolbarLayout嵌套ImageView,这是一个常见的效果,具体是什么我就不多介绍了,这里的每一个view都需要设置fitsSystemWindows为true,这样事件才能传到 ImageView中)
    在Lollipop以及更高的版本,我们提供了一些新的API,使得自定义这个行为更加的方便。与之前的版本不同,现在你只需要覆盖OnApplyWindowInsets()方法,该方法允许View消耗它想消耗的任意空间(Insets),同时也能够为子方法,调用dispatchApplyWindowInsets()

    更妙的是,利用新的API,你甚至不需要拓展View类,你可以使用ViewCompat.setOnApplyWindowInsetsListener(),这个方法优先于View.onApplyWindowInsets()调用。ViewCompat 同时也提供了 onApplyWindowInsets() 和dispatchApplyWindowInsets()的兼容版本,无需冗长的版本判断。

    flContent.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
                @Override
                public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
                    return clContent.dispatchApplyWindowInsets(insets);
                }
            });
    

    上面的代码将flContent的fitsSystemWindows事件传递给clContent处理。

    关于fitsSystemWindows的详细介绍

    补充
    看到评论里有人提出光讲了差异,没有具体的适配代码,这里推荐一个适配库StatusBarUtil

    展开全文
  • Android沉浸式状态栏(透明状态栏)最佳实现

    万次阅读 多人点赞 2017-12-15 09:29:21
    Android沉浸式状态栏(透明状态栏)最佳实现 在Android4.4之前,我们的应用没法改变手机的状态栏颜色,当我们打开应用时,会出现上图中左侧的画面,在屏幕的顶部有一条黑色的状态栏,和应用的风格非常不协调;为了...
  • 今天总结一下状态栏的使用,当然也是参考别人的。但是总归自己得试试,然后把常用的几种情况记下来,因为这些东西是死的,下次拿过来就可以用。 一. 沉浸式状态栏 :Android 5.0以上才会支持沉浸式状态栏效果。 ...
  • Android 沉浸式状态栏攻略 让你的状态栏变色吧

    万次阅读 多人点赞 2015-09-22 11:03:18
    一、概述近期注意到QQ新版使用了沉浸式状态栏,ok,先声明一下:本篇博客效果下图:关于这个状态栏变色到底叫「Immersive Mode」/「Translucent Bars」有兴趣可以去 为什么在国内会有很多用户把 「透明
  • Android 沉浸式状态栏的实现方法、状态栏透明

    万次阅读 多人点赞 2016-08-23 17:53:04
    现在越来越多的软件都开始使用沉浸式状态栏了,下面总结一下沉浸式状态栏的两种使用方法 注意!沉浸式状态栏只支持安卓4.4及以上的版本状态栏:4.4上是渐变色,5.0上是完全透明,本文模拟器为4.4演示 效果图...
  • 沉浸式状态栏指的是,状态栏隐藏,在手指做了相关操作后,状态栏显示出来,例如视频播放器,在播放视频时是隐藏状态栏的,但是点击屏幕的时候,状态栏会显示出来,再例如文本阅读器,在阅读的时候是全屏的,然后从...
  • 简单说明一下(上图Activity采用默认Style,状态栏和标题栏都会显示):最大的草绿色区域是屏幕界面,红色次大区域我们称之为“应用界面区域”,最小紫色的区域我们称之为“View绘制区域”;屏幕顶端、应用界面区...
  • android设置透明状态栏

    万次阅读 2020-05-21 18:23:17
    沉浸式状态栏:就是你看视频(横屏)的时候没有状态栏,就算你点击屏幕一下也不会出现状态栏的(需要从上屏幕边缘往下划一下才能出来状态栏),由于不好截屏这里我就不上图了,大家心领神会一下就行了。 透明式状态...
  • Android开发技巧——设置系统状态栏颜色

    万次阅读 多人点赞 2017-07-28 22:15:06
    如何实现实现设置系统状态栏颜色需要至少在Android 4.4.2(API 19)以上。这是因为,在这个版本以下,没有任何的API可以帮助我们来实现。那么具体如何设置呢?Android 4.4.2实现Android 4.4.2新增了一个特性,即可以...
  • 沉浸式状态栏:,状态栏隐藏,在手指做了相关操作后,状态栏显示出来,例如视频播放器,在播放视频时是隐藏状态栏的,但是点击屏幕的时候,状态栏会显示出来,再例如文本阅读器,在阅读的时候是全屏的,然后从屏幕...
  • android标题栏、状态栏图标文字颜色及背景动态变化

    万次阅读 多人点赞 2017-06-29 14:58:53
    android中沉浸式状态栏的文章已经满大街了,可是在实现某些效果时,还是得各种搜索,测试一通后,最后还常常满足不了要求,即使好不容易在一部手机上满足了需求,放在另外一手机上,发现效果差强人意。今天把自己这...
  • 看了网上介绍,关于沉浸式状态栏大概有几种,透明状态栏,变色状态栏,沉浸式状态栏。变色状态栏大概只是通用说法,无论沉浸式还是透明的 都可以实现状态栏的变色。这里就不再介绍。 1,透明状态栏顾名思义就是透明...
  • 动态显示和隐藏状态栏

    万次阅读 热门讨论 2013-10-08 15:39:22
    小米Launcher有一个细节上的功能效果:在长按桌面应用图标时,会隐藏状态栏,然后在状态栏原有的布局上显示卸载或删除的操作栏。放手后,操作栏隐藏,状态栏显示出来。也就是说,这个过程是涉及到对状态栏的动态操作...
  • 公众号:smart_android 作者:耿广龙|loonggg ... 导语:沉浸式状态栏,改变状态栏的颜色使之与APP风格一体化...我相信大家肯定看到过很多软件有沉浸式状态栏,在运行该App时改变了手机屏幕顶部状态栏的颜色,使他们
  • 透明状态栏(沉浸式状态栏

    千次阅读 2016-01-26 08:58:17
    透明状态栏(沉浸式状态栏)透明状态栏(Translucate StatusBar)是android从4.4开始模仿ios推出的一种模式,他可以改变状态栏的颜色,使其更加的与自己的app样式所统一android 4.4推出的状态栏为透明样式,但会有一...
  • 最近在实现换肤功能时,遇到了需要切换安卓状态栏字体颜色的问题。 我们都知道安卓默认的状态栏字体及图标的默认颜色是白色,当我们将状态栏背景颜色设置为白色等浅色时,字体及图标就显示不清楚了。这时就需要将...
1 2 3 4 5 ... 20
收藏数 383,379
精华内容 153,351
关键字:

状态栏