美文网首页Android开发沉浸式状态栏Android知识
Android透明状态栏与沉浸模式全解

Android透明状态栏与沉浸模式全解

作者: 不会上树的猴子 | 来源:发表于2017-04-03 13:06 被阅读1323次

    Android透明状态栏与沉浸模式全解

    现在如今利用状态栏做文章的主要就是如下四种场景了,先上图

    • 网易云音乐 状态栏与标题栏同色,无缝衔接
    • 多看阅读 全屏沉浸阅读(视频,游戏同理)
    • 淘宝 状态栏深色
    • 饿了么 图片延伸至状态栏下面

    透明状态栏模式就是大家说的"沉浸式状态栏"了.

    沉浸模式是一般用于小说、视频、游戏。将用户的注意完全聚焦在内容上,是真正的沉浸模式.

    从3.x版本开始, 系统DecorView提供了setSystemUiVisibility方法, 可以通过设置Flag更改所谓SystemUI的属性,可以操作各种状态栏,导航栏的的显示与隐藏效果。非常好用,有篇很好的总结文章Android Activity 全屏方法总结
    调用方式:

    getWindow().getDecorView().setSystemUiVisibility(
        View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
    );
    

    不同的标记用|连接,这里着重解释一下这几个标记

    • View.SYSTEM_UI_FLAG_LAYOUT_STABLE Level 16
      保持整个View稳定,使View不会因为SystemUI的变化而做layout,常跟statusbar 悬浮, 隐藏共用,若不设置的话,statusbar的的显示和隐藏则会导致ContentView重新计算大小并绘制,因此这个标记通常是必须的

    • View.SYSTEM_UI_FLAG_FULLSCREEN Level 16
      状态栏隐藏,完全不见了,就如同在看视频时,屏幕上没有任何内容之外的东西

    • View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN Level 16
      状态栏透明,悬浮在浮于Activity的上面,想像有个z轴,这时activity可以完全占用整个屏幕,而之前我们的屏幕还要分一些给statusbar

    • View.SYSTEM_UI_FLAG_HIDE_NAVIGATION Level 14
      隐藏导航栏, 性质如SYSTEM_UI_FLAG_FULLSCREEN

    • View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION Level 16
      导航栏上浮于Activity,性质如SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

    1.首先看效果图(1)和(3),这两个的实现原理其实一样,就是状态栏着色的颜色不同,又两种实现思路

    1.直接将状态栏的颜色设置为目标颜色
    window.setStatusBarColor(int);

    它只适用于5.0之后。因此直接设置statusBar颜色的方式在5.0以下不行

    2.将状态栏设置为"透明+悬浮",这时我们的布局就可以延伸到状态栏下面了,然后在activity的布局里面制造一块和状态栏等尺寸的view,设置需要的背景颜色即可,因为状态栏是透明的,因此看起来就像是状态栏被上了色一样,效果相同。通过添加这个标记便可以实现我们需要的"透明+悬浮"效果,
    window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

    顺变说下清除标记的方法
    window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

    其实我们查看FLAG_TRANSLUCENT_STATUS的源码就会发现,它其实就是上面说的system UI visibility flags里面的稳定布局SYSTEM_UI_FLAG_LAYOUT_STABLE和悬浮status标记的组合
    
    * <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;
    

    这就很容易理解了,现在直接上全部代码

     /**
     * 思路:直接设置状态栏的颜色
     */
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private void setStatusBarUpperAPI21() {
        Window window = getWindow();
        //取消设置悬浮透明状态栏,ContentView便不会进入状态栏的下方了
        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    
        //设置状态栏颜色
        window.setStatusBarColor(getResources().getColor(R.color.bg_green));
    }
    
    /**
     * 思路:设置状态栏悬浮透明,然后制造一个和状态栏等尺寸的View设置好颜色填进去,就好像是状态栏着色了一样
     */
    private void setStatusBarUpperAPI19() {
        Window window = getWindow();
        //设置悬浮透明状态栏
        window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    
        ViewGroup mContentView = (ViewGroup) findViewById(Window.ID_ANDROID_CONTENT);
        int statusBarHeight = getStatusBarHeight();
        int statusColor = getResources().getColor(R.color.bg_green);
    
        View mTopView = mContentView.getChildAt(0);
        if (mTopView != null && mTopView.getLayoutParams() != null &&
                mTopView.getLayoutParams().height == statusBarHeight) {
            mTopView.setBackgroundColor(statusColor);
            return;
        }
    
        //制造一个和状态栏等尺寸的 View
        mTopView = new View(this);
        ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, statusBarHeight);
        mTopView.setBackgroundColor(statusColor);
        //将view添加到第一个位置
        mContentView.addView(mTopView, 0, lp);
    }
    
    private int getStatusBarHeight() {
        int result = 0;
        int resId = getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resId > 0) {
            result = getResources().getDimensionPixelSize(resId);
        }
        return result;
    }
    

    这里注意为什么还要区分5.0和4.4不同的方案来实现,因为WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS这个标记在6.0上不是全透明而是半透明的,那么如果要实现效果(1)网易云音乐那样的效果的话就不行了,所以在5.0以上的版本我们直接设置statusBar的颜色。5.0以下通过制造一个和statusbar等尺寸的view着色填进去。

    最终的效果如下,让4.4也有了差不多的效果:


    而效果(3)就是改了个深色的颜色就行了:


    values/styles.xml添加内容:
    <style name="fullScreenTheme" parent="AppTheme">
    </style>

    values-v19/styles.xml添加内容:

        <style name="fullScreenTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="android:windowTranslucentStatus">true</item>
        <item name="android:windowTranslucentNavigation">true</item>
    </style>
    

    values-v21/styles.xml添加内容:

        <style name="fullScreenTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="android:windowTranslucentStatus">false</item>
        <item name="android:windowTranslucentNavigation">true</item>
        <item name="android:statusBarColor">@android:color/transparent</item>
    </style>
    

    windowTranslucentStatus:设置透明悬浮状态栏,4.4就有
    windowTranslucentNavigation:设置透明悬浮导航栏,4.4就有

    注意既然windowTranslucentStatus在4.4就有了,为什么在5.0的style要设置windowTranslucentStatus为false并且呢,其实这个问题前面就说过了

    window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

    <item name="android:windowTranslucentStatus">true</item>
    看出来了吗,对他们俩其实是一个东西,一个是xml实现,一个是代码实现
    所以windowTranslucentStatus在6.0上的效果是半透明的。效果是这样的,

    而我们的最终效果:


    另外,如果加了DrawerLayout,打开是状态栏在5.0以上没有任何问题,但是在4.4就是是灰色的。需要添加两行代码做处理:

        mDrawerLayout.setFitsSystemWindows(true);
        mDrawerLayout.setClipToPadding(false);
    

    setFitsSystemWindows方法的作用是是否为系统默认的装饰预留空间
    setClipToPadding方法是使得绘制区域可以延伸到padding区域

    其实这里用了这两个方法虽然有效,但是我也不知道为什么,有知道朋友请告诉我。

    这样我们就完全使用代码完美解决了效果(4)。

    • 代码实现:
      主要就是setSystemUiVisibility的运用

      public void onShow() {
      //5.0以上手动设置statusBar为透明。不能使用getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
      //因为这在4.4,5.0确实是全透明,但是在6.0是半透明!!!
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      getWindow().setStatusBarColor(Color.TRANSPARENT);
      } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {

            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        }
      
        getWindow().getDecorView().setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
      
        );
      

      }

    3.效果(2)全屏沉浸,如小说,视频,游戏

    在看完setSystemUiVisibility的各种flag的左右后,应该可以凭自己的需要组合搭配flag来实现全屏了,直接上代码:

    public void onHide() {
    
    
        //4.1及以上通用flags组合
        int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
    
    
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            getWindow().getDecorView().setSystemUiVisibility(
                    flags | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                            | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
            );
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            getWindow().getDecorView().setSystemUiVisibility(flags);
        }
    }
    

    就这么几行代码,注意这个全屏是可以支持到4.1的

    参考:
    Android Activity 全屏方法总结
    Android 系统状态栏沉浸式/透明化完整解决方案
    Android 状态栏全透明策略
    Android开发:Translucent System Bar 的最佳实践

    相关文章

      网友评论

      本文标题:Android透明状态栏与沉浸模式全解

      本文链接:https://www.haomeiwen.com/subject/earvbttx.html