沉浸式状态栏_沉浸式状态栏demo - CSDN
精华内容
参与话题
  • Android 沉浸式状态栏与隐藏导航栏

    万次阅读 2017-07-06 10:22:26
    可以看到,有状态栏、ActionBar(ToolBar)、导航栏等,一般来说,APP实现沉浸式有三种需求:沉浸式状态栏,隐藏导航栏,APP全屏 沉浸式状态栏是指状态栏与ActionBar颜色相匹配, 隐藏导航栏不用多说,就是将导航栏...

    1 前言

    一般我们在Android的APP开发中,APP的界面如下:
    这里写图片描述
    可以看到,有状态栏、ActionBar(ToolBar)、导航栏等,一般来说,APP实现沉浸式有三种需求:沉浸式状态栏,隐藏导航栏,APP全屏
    沉浸式状态栏是指状态栏与ActionBar颜色相匹配,
    隐藏导航栏不用多说,就是将导航栏隐藏,去掉下面的黑条。
    APP全屏是指将状态栏与导航栏都隐藏,例如很多游戏界面,都是APP全屏。
    所以,在做这一步时,关键要问清楚产品狗的需求,免得白费功夫。
    下面,分别来介绍这三种方式的实现。

    2 沉浸式状态栏

    沉浸式状态栏效果一般如下:
    顺便在网上找的图
    这里写图片描述
    关于沉浸式状态栏网上的方案很多,比如android 5.0 以上的MD设计,或者修改activiyty的window的setStatusBarColor()方法,设置颜色。需要说明一点的时,沉浸式状态栏只对API19以上有效。
    这里我依然采用的是设置Activity的Window设置setStatusBarColor()的方法。代码如下:

    /**
         * 设置状态栏的颜色
         */
        @TargetApi(Build.VERSION_CODES.KITKAT)
        public static void statusBarTintColor(Activity activity, int color) {
            // 代表 5.0 及以上
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                activity.getWindow().setStatusBarColor(color);
                return;
            }
    
            // versionCode > 4.4  and versionCode < 5.0
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                //透明状态栏
                activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
                ViewGroup androidContainer = (ViewGroup) activity.findViewById(android.R.id.content);
                // 留出高度 setFitsSystemWindows  true代表会调整布局,会把状态栏的高度留出来
                View contentView = androidContainer.getChildAt(0);
                if (contentView != null) {
                    contentView.setFitsSystemWindows(true);
                }
                // 在原来的位置上添加一个状态栏
                View statusBarView = createStatusBarView(activity);
                androidContainer.addView(statusBarView, 0);
                statusBarView.setBackgroundColor(color);
            }
        }
    
        /**
         * 创建一个需要填充statusBarView
         */
        private static View createStatusBarView(Activity activity) {
            View statusBarView = new View(activity);
            ViewGroup.LayoutParams statusBarParams = new ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity));
            statusBarView.setLayoutParams(statusBarParams);
            return statusBarView;
        }
    
        /**
         * 获取状态栏的高度
         */
        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;
        }

    3 隐藏导航栏

    隐藏导航栏就是使用了UI Flag

    /**
         *
         * @param activity
         * @param
         */
        public static void setNavigationBar(Activity activity,int visible){
            View decorView = activity.getWindow().getDecorView();
            //显示NavigationBar
            if (View.GONE == visible){
                int option = SYSTEM_UI_FLAG_HIDE_NAVIGATION;
                decorView.setSystemUiVisibility(option);
            }
        }

    4 APP全屏

    这里的APP全屏又分为隐藏状态栏与ActionBar,与隐藏导航栏,状态栏。
    隐藏状态栏:

    /**
         * 设置Activity的statusBar隐藏
         * @param activity
         */
        public static void statusBarHide(Activity activity){
            // 代表 5.0 及以上
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                View decorView = activity.getWindow().getDecorView();
                int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
                decorView.setSystemUiVisibility(option);
                activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
                ActionBar actionBar = activity.getActionBar();
                actionBar.hide();
                return;
            }
    
            // versionCode > 4.4  and versionCode < 5.0
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            }
    
        }

    效果如下:
    这里写图片描述

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

    隐藏导航栏,状态栏:
    一般游戏需要这种界面,代码如下:
    在Activity的onWindowFocusChanged()中去设置界面完全全屏。

     /**
         * 导航栏,状态栏隐藏
         * @param activity
         */
        public static void NavigationBarStatusBar(Activity activity,boolean hasFocus){
            if (hasFocus && Build.VERSION.SDK_INT >= 19) {
                View decorView = activity.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,可以让导航栏,状态栏都透明化。

    /**
         * 导航栏,状态栏透明
         * @param activity
         */
        public static void setNavigationBarStatusBarTranslucent(Activity activity){
            if (Build.VERSION.SDK_INT >= 21) {
                View decorView = activity.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);
                activity.getWindow().setNavigationBarColor(Color.TRANSPARENT);
                activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
            }
            ActionBar actionBar = activity.getActionBar();
            actionBar.hide();
        }

    效果如下:
    这里写图片描述

    参考:
    http://blog.csdn.net/guolin_blog/article/details/51763825

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

    万次阅读 多人点赞 2017-12-15 09:29:21
    Android沉浸式状态栏(透明状态栏)最佳实现 在Android4.4之前,我们的应用没法改变手机的状态栏颜色,当我们打开应用时,会出现上图中左侧的画面,在屏幕的顶部有一条黑色的状态栏,和应用的风格非常不协调;为了...

    Android沉浸式状态栏(透明状态栏)最佳实现

    这里写图片描述
    在Android4.4之前,我们的应用没法改变手机的状态栏颜色,当我们打开应用时,会出现上图中左侧的画面,在屏幕的顶部有一条黑色的状态栏,和应用的风格非常不协调;为了提供更好的界面交互,google在Android4.4以后提供了设置沉浸式状态栏的方法,对于沉浸式状态栏这个名字存在争议,我们不做讨论,实际的效果其实就是透明的状态栏,然后在状态栏的位置显示我们自定义的颜色,通常为应用的actionbar的颜色,或者是将应用的整体的一张图片也占据到状态栏中,如下图所示:
    这里写图片描述

    由于这种透明的状态栏是在Android4.4以后才出现的,所以我们需要为4.4以上的版本做适配,方法有两种,一种是在资源文件(style)中设置,一个是在代码中设置。

    在资源文件中设置透明状态栏

    首先,我们先在values下的style中加入如下代码:

        <!-- 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>
            <item name="colorAccent">@color/colorAccent</item>
        </style>
    
        <style name="AppTheme.Main" parent="AppTheme" />

    AppTheme.Main是我们要设置给activity的主题,它应该继承于AppTheme.NoActionBar,然后
    我们在values-v19中加入同样的AppTheme.Main,如下所示:

        <style name="AppTheme.Main" parent="AppTheme.NoActionBar">
            <item name="android:windowTranslucentStatus">true</item>
        </style>

    然后运行程序,效果如图所示:
    这里写图片描述
    图片中我们发现,虽然我们实现了透明的状态栏,但是上边的文字和状态栏的信息重叠了。为了修复这个问题,我们应该在布局文件的根布局中加入android:fitsSystemWindows=”true” 就可以了,加入后效果如下:
    这里写图片描述
    应用的文字和系统显示的文字错开了,这也是我们最终的显示效果

    需要注意的几点:

    1. 为Android4.4+适配的同时,不要忘了在values的style.xml中加入同名的自定义style,不然4.4以下会报错
    2. 不要忘记在布局文件中加入 android:fitsSystemWindows=”true”,不然布局内容会和状态栏内容重叠
    3. 在Android5.0中,透明标题的效果和4.4中有所不同,但是实现方法相同,我们可以不必为5.0+单独适配,同时5.0+的版本提供了新的API android:statusBarColor,可以用来单独设置状态栏的颜色,但是这种方法不适用于我们例子中的图片占据状态栏,它适用于纯色的状态栏,并且它应该放到value-v21文件夹中

      上边的例子中,我们只设置了一个图片,使其占据了状态栏。但是我们平常见到更多的可能不是这种,而且带actionbar的情况,如下所示:
      这里写图片描述
      在上图中,存在一个actionbar,然后状态栏的颜色与其相同,接下来我们来实现这种效果。

    style文件不用改变,我们需要在布局文件中加入一个toolbar,代码如下:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        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"
        android:background="@color/colorAccent"
        android:orientation="vertical"
        tools:context="com.zephyr.musicapp.MainActivity">
    
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:fitsSystemWindows="true"
                android:background="?attr/colorPrimary"
                app:popupTheme="@style/AppTheme.PopupOverlay"/>
    
    </LinearLayout>
    然后在activity中
            Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
            setSupportActionBar(toolbar);

    在一个LinearLayout中包含了一个toolbar,和第一个例子相比,我们把android:fitsSystemWindows=”true”加在了Toolbar上边,而不是LinearLayout中,如果把android:fitsSystemWindows=”true”放到LinearLayout中,会出现下边的效果:
    这里写图片描述
    虽然状态栏透明了,但是颜色却和根布局的颜色相同而不是toolbar,这样显然不行,如果我们把android:fitsSystemWindows=”true”放到toolbar中就可以实现我们的效果,如下图所示:
    这里写图片描述

    在这里,我们还需要特别注意一个地方,那就是在设置toolbar高度的时候,应该设置成wrap_content,不然也没法实现我们的效果,比如我把高度固定为50dp,效果如下:
    这里写图片描述
    虽然占据了状态栏,但是toolbar的高度明显不够了,效果很不好。

    在Android5.0以后,增加了新的API android:statusBarColor 用于设置状态栏的颜色,同时以前的windowTranslucentStatus也是对它有效的,所以如果想通过新的API设置状态栏颜色的话,首先应该将windowTranslucentStatus设置为false,不然是没有效果的。

    以上就是通过资源文件设置透明状态栏的方法。

    代码中设置透明状态栏

    在代码中设置透明状态栏的方法也很简单,思路就是首先通过 WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS设置当前window为透明状态栏,这样标题栏就可以占据状态栏了,但是会出现布局中的内容和状态栏的内容重叠的问题,为了解决这个问题,我们应该获得状态栏的高度,然后设置标题栏的paddingTop为状态栏的高度,这样就可以实现透明效果的标题栏的,代码如下:

     protected void setImmerseLayout(View view) {// view为标题栏
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                Window window = getWindow();
                window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
                int statusBarHeight = getStatusBarHeight(this.getBaseContext());
                view.setPadding(0, statusBarHeight, 0, 0);
            }
        }
    
        public 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;
        }

    fitSystemWindows属性:
    官方描述:
    Boolean internal attribute to adjust view layout based on system windows such as the status bar. If true, adjusts the padding of this view to leave space for the system windows. Will only take effect if this view is in a non-embedded activity.


    补充:
    当为纯色actionbar的情况下,我们将toolbar的高度设为wrap_content,并且将fitsSystemWindows=“true”参数设置到了toolbar上,这样虽然实现了4.4版本的沉浸效果,但是却不能有效的适配5.0及以上的版本,所以做出以上修改。
    在4.4版本上:
    1. 我们将根布局的背景颜色设置成和toolbar一样的,并且设fitsSystemWindows为true。
    2. toolbar的fitsSystemWindows属性去掉,并且高度设置为?attr/actionBarSize
    3. 在toolbar下增加一个子布局,颜色设置为white,这样就可以在4.4及5.0以上实现带actionbar的沉浸式布局

    2017.12.14更新:
    使用纯色actionbar的时候,可以使用容器包裹toolbar,只设置容器 fitsSystemWindows为true。由于fitsSystemWindows属性本质上是给当前控件设置了一个padding,所以我们设置到根布局的话,会导致状态栏是透明的,并且和窗口背景一样,和toolbar背景不同。如果我们设置给toolbar,则会由于padding的存在,导致toolbar的内容下移。所以我们选择使用LinearLayout包裹toolbar,并将toolbar的背景等属性设置在appbarlayout上就可以完美实现效果,代码:

     <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/colorPrimary"
        android:fitsSystemWindows="true"
        android:orientation="vertical">
    
        <android.support.v7.widget.Toolbar
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:title="Login"/>
    </LinearLayout>
    
    展开全文
  • 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;
        }
    }
    
    
    展开全文
  • Android关于沉浸式状态栏总结 Android适配—沉浸式状态栏 Android App 沉浸式状态栏解决方案 Android 沉浸式解析和轮子使用 Android沉浸式(透明)状态栏适配 Android状态栏微技巧,带你真正理解沉浸式模式 android...

    背景

    之前做过Android沉浸式状态栏的相关需求,但是一直忙于工作,没时间系统的整理下沉浸式相关的知识,所以今天抽出时间,写一篇 Android沉浸式状态栏的文章。

    何为沉浸式

    沉浸式就是要给用户提供完全沉浸的体验,使用户有一种置身于虚拟世界之中的感觉。
    沉浸式模式就是整个屏幕中显示都是应用的内容,没有状态栏也没有导航栏,用户不会被一些系统的界面元素所打扰。
    Android沉浸式模式的本质就是全屏化,但这可能并不是我们想要的,我们还是来实现下网上传的沸沸扬扬的Android沉浸式状态栏

    沉浸式状态栏的兼容情况

    Android版本 透明状态栏
    <4.4 ×
    4.4-5.0
    >=5.0
    Android版本 黑白字符状态栏
    <6.0 ×
    >=6.0

    fitsSystemWindows

    在讲沉浸式状态栏之前,我们先来认识一个属性——fitsSystemWindows,这个属性在沉浸式状态中扮演着非常重要的角色。

    • 官方描述

    Boolean internal attribute to adjust view layout based on system
    windows such as the status bar. If true, adjusts the padding of this view to leave space for the system windows. Will only take effect if this view is in a non-embedded activity.

    • 中文描述

    Boolean内部属性是基于系统窗口(如status bar)调整视图布局。如果为true,将调整视图padding为系统窗口预留出空间。Will only take effect if this view is in a non-embedded activity.
    这句话的意思是view不在非嵌入式的activity才会生效。
    嵌入式activity是托管在父activity中的活动。常见的例子是TabHost / TabActivity设计。特别是,嵌入式Acitvities位于主机中 LocalActivityManager,这在概念上类似于 FragmentManager 它允许您在另一个内部显示一个Activity。

    根据这个定义,很容易理解为什么只有主机(非嵌入式)Activity才能支持 fitsSystemWindows 属性,因为任何嵌入的活动都限制在其主机定义的区域内。

    • 注意:

    fitsSystemWindows只作用在sdk>=19的系统上就是高于4.4的系统,android:fitsSystemWindows默认值为false,并且在哪个控件设置android:fitsSystemWindows="true"会有不一样的效果

    android:fitsSystemWindows=“true”,这个属性可以给任何view设置,只要设置了这个属性此view的所有padding属性失效.只有在设置了透明状态栏(StatusBar)或者导航栏(NavigationBar)此属性才会生效

    当设置了透明状态栏(StatusBar)时:
    当为此activity设置了

    <item name="android:windowTranslucentStatus">true</item>
    或者
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
       getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    }
    

    如果有以上两种情况之一,我们的状态栏(StatusBar)就会变成透明,并且布局会扩展到StatusBar的位置同时,所有设置了android:fitsSystemWindows="true"属性的view会自动添加一个值等于状态栏高度的paddingTop

    当设置了透明导航栏(NavigationBar)时:
    当为此activity设置了:

    <item name="android:windowTranslucentNavigation">true</item>
    或者
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
    }
    

    如果有以上两种情况之一,我们的导航栏(NavigationBar)就会变成透明,并且布局会扩展到NavigationBar的位置。同时,所有设置了android:fitsSystemWindows="true"属性的view会自动添加一个值等于导航栏高度的paddingBottom。

    沉浸式状态栏实现的一般思路

    • 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)实现沉浸式的方式

    Android 4.4 为什么能够实现沉浸式的效果呢?因为在Android 4.4 新增了一个重要的属性:FLAG_TRANSLUCENT_STATUS

     /**
             * Window flag: request a translucent status bar with minimal system-provided
             * background protection.
             *
             * <p>This flag can be controlled in your theme through the
             * {@link android.R.attr#windowTranslucentStatus} attribute; this attribute
             * is automatically set for you in the standard translucent decor themes
             * such as
             * {@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}.</p>
             *
             * <p>When this flag is enabled for a window, it automatically sets
             * the system UI visibility flags {@link View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and
             * {@link View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}.</p>
             */
            public static final int FLAG_TRANSLUCENT_STATUS = 0x04000000;
    

    解释:设置状态栏透明,并且变为全屏模式。上面的解释已经说得很清楚了,当window的这个属性有效的时候,会自动设置 system ui visibility的标志SYSTEM_UI_FLAG_LAYOUT_STABLE和SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN.

    有两种方式实现这个属性:

    可以在代码中设置,如下:

    activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    

    当然也可以在theme 中设置属性windowTranslucentStatus,如下:

    android:windowTranslucentStatus
    

    效果如下:
    Alt
    效果如上图,可以看出,沉浸式的效果是出来了,但是也有一个问题,我们的标题栏和状态栏重叠了,相当于整个布局上移了StatusBar 的高度。为了让标题栏回到原来的位置,我们在标题栏的上方添加一个大小和StatusBar大小一样的View,View 的BackgroundColor 为标题栏一样的颜色,这个View起到一个占位的作用。这个时候,标题栏就会下移StatusBar的高度,回到正常的位置。
    添加如下代码:

           //获取windowphone下的decorView
            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);
    

    创建和status bar 一样大小的View的代码如下:

     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;
        }
    

    其中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),通过这个方法,可以很轻松地实现沉浸式状态栏。方法如下:

     /**
         * 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一起使用,必须设置FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS ,并且不能设置FLAG_TRANSLUCENT_STATUS(Android 4.4才用这个)
    看一下FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS这个flag:
    可以看到,这个flag 也是在Android 5.0添加的,它的作用是什么呢?

    Alt

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

    实现沉浸式添加如下代码:

    getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
    //注意要清除 FLAG_TRANSLUCENT_STATUS flag
    getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    getWindow().setStatusBarColor(getResources().getColor(android.R.color.holo_red_light));
    

    效果如下:
    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

    代码实现方式中通过版本号的判断兼容 Android5.0以下和Android 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));
            } 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);
                }
    
                ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
                rootView.setFitsSystemWindows(true);
                rootView.setClipToPadding(true);
                setRootView(activity);
    }
    

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

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

    Alt

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

    添加如下代码:

    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);
    }
    

    效果如下:
    Alt
    除了在代码中添加以外,还可以直接在主题中使用属性:

     <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关于沉浸式状态栏总结
    Android适配—沉浸式状态栏
    Android App 沉浸式状态栏解决方案
    Android 沉浸式解析和轮子使用
    Android沉浸式(透明)状态栏适配
    Android状态栏微技巧,带你真正理解沉浸式模式
    android沉浸式状态栏封装—教你玩出新花样
    ImmersionBar – android 4.4以上沉浸式实现

    展开全文
  • 沉浸式状态栏

    2018-06-13 19:01:48
    沉浸式状态栏 简单介绍 沉浸式状态栏其实是对沉浸模式(Immersive Mode)在状态栏上的体现,即让状态栏变透明,或者是在打游戏和看视频时彻底隐藏起状态栏,让用户拥有良好的体验。 比如说我们看视频,状态栏...
  • 首先去除顶部自带的标题,并且只有在**Android 4.4(API 19)**以上版本才适用: requestWindowFeature(Window.FEATURE_NO_TITLE);//不显示标题 setContentView(R.layout.activity_main2); if (Build.VERSION....
  • 本篇实现的是沉浸式状态栏。 其实,实质上就是使手机状态栏的颜色改变,使其成为自己想要的颜色。如果把它设置成和APP主色调相同,这样看上去就会好看许多。
  • Android状态栏微技巧,带你真正理解沉浸式模式

    万次阅读 多人点赞 2017-04-16 16:44:40
    记得之前有朋友在留言里让我写一篇关于沉浸式状态栏的文章,正巧我确实有这个打算,那么本篇就给大家带来一次沉浸式状态栏的微技巧讲解。 其实说到沉浸式状态栏这个名字我也是感到很无奈,真不知道这种叫法是谁先...
  • Android 沉浸式状态栏,包括普通沉浸式与图片沉浸式,亲测好用!
  • Cordova 实现沉浸式(透明)状态栏效果

    万次阅读 热门讨论 2017-07-05 15:04:31
    沉浸式状态栏(Immersive Mode)和透明状态栏(Translucent Bar)。这两个概念很多地方的解释都不是很清晰,所以导致很多人都各有各的认识。所以这里我也有一个自己的认识,笔者认为沉浸式状态栏也可以说是全屏模式...
  • 2017年08月24日 · 5.5k 次阅读 H5+状态栏透明(沉浸式)/全屏/设置颜色 整理 移动端web H5+对web APP状态栏设置整理: ...首先可以检测下当前环境是否支持沉浸式状态栏,检测语句: //是否...
  • 最近进了一家新的公司 新...但是其他测试机状态栏没有(当时整个app都没有加沉浸式状态栏效果 。。。) 于是我临危受命 在新版本即将上线的时候被要求把整个app都加上沉浸式效果 原生的沉浸式网上一大堆 但是cordova 的
  • 解决华为手机沉浸式状态栏的问题

    千次阅读 2017-04-17 13:47:23
    最近在项目中用到沉浸式状态栏,但是在华为手机上在XML文件中设置主题并没有什么用,而且很多app也没有解决这个问题,闲言少叙,直接上图: 代码如下:import android.app.Activity; import android.content.res....
  • 经测试 动态设置屏幕宽高可解决 getDialog().getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ConfigYibaisong.window_y); //宽度随意 高度不能用ViewGroup.LayoutParams.MATCH_PARENT,高度自己获取...
  • Android 实现变色状态栏

    万次阅读 多人点赞 2015-09-11 11:36:37
    首先我们得了解什么是透明状态栏以及什么是沉浸式状态栏,以及其区别,国内习惯称透明状态栏为沉浸式状态栏,但是两者是有本质区别的。先来看看什么是沉浸式模式。 Android 4.4中,沉浸式体验得到了再次强化,提供...
  • Fragment切换中适配"沉浸式状态栏

    万次阅读 2018-07-11 11:10:55
    传送门: fitSystemWindow属性的作用 http://blog.csdn.net/wangxp423/article/details/79564244 fitSystemWindow属性实战 ...适配“沉浸式状态栏 http://blog.csdn.net/...
  • Android 实现沉浸式状态栏

    万次阅读 多人点赞 2015-09-12 10:09:56
    上一篇文章将Android 实现变色状态栏我们实现了变色的状态栏,也介绍了沉浸式状态栏和透明状态栏的区别,这篇文章我们实现沉浸式状态栏沉浸式状态栏的来源就是很多手机用的是实体按键,没有虚拟键,于是开了沉浸...
  • flutter 沉浸式状态栏

    千次阅读 2018-11-26 10:56:47
    padding: new EdgeInsets.all(0.0),加上这句就可以 具体原因不知 希望大佬解答~~
  • HBuilder 设置沉浸式状态栏显示效果

    千次阅读 2018-09-17 11:07:24
    1.在manifest.json 要设置 "fullscreen": false; 2.在 manifest.json 设置 "plus": { "launchwebview": { "statusbar": { "background"... }
  • 在mainfest.json的plus中以下位置添加status...: true/*沉浸式状态栏*/ }, "splashscreen": { "autoclose": true,/*是否自动关闭程序启动界面,true表示应用加载应用入口页面后自动关闭;...
1 2 3 4 5 ... 20
收藏数 6,467
精华内容 2,586
关键字:

沉浸式状态栏