美文网首页Ionic之路android知识点Android UI特效实现
Android 系统状态栏沉浸式/透明化完整解决方案

Android 系统状态栏沉浸式/透明化完整解决方案

作者: btman | 来源:发表于2016-04-01 12:58 被阅读102286次

    前言

    网上已经有很多有关于系统状态栏的解决方案,这篇文章也不会有什么新奇的解决方案,都是本人经过自己试验,统计提炼出来的相对靠谱的一套解决方案.
    如果是android大牛可以忽略本文,怕让您贱笑.只面向小白,帮助小白减少摸索的时间.

    关于术语

    网上有很多争论:

    你这状态栏是变色龙状态栏,不是沉浸式的
    这应该是沉浸式的状态栏吧,系统栏与actionbar颜色设为一致

    我只想说去你妹的,老子只要自己的app的状态栏能和主题颜色一致就行了,定义那么多术语,让我等小白情以何堪?
    吐槽归吐槽,但还是不得不去试着理解下这些术语怎么来的,引用这里的一段话:

    1. 沉浸式全屏模式
      隐藏status bar(状态栏)使屏幕全屏,让Activity接收所有的(整个屏幕的)触摸事件。
    2. 透明化系统状态栏
      透明化系统状态栏,使得布局侵入系统栏的后面,必须启用fitsSystemWindows属性来调整布局才不至于被系统栏覆盖。

    因此,我就这样理解了:

    沉浸式不就是隐藏状态栏嘛,状态栏不见了?这不就是app全屏模式嘛?wtf?

    而透明式式状态栏就是让app的内容布局可以扩展到系统状态栏?这里有个问题就是为什么能在系统状态栏还显示的情况下,将内容布局扩展到系统状态栏?恩,这应该很好理解,就是Z坐标系的作用了,系统状态栏是覆盖在内容布局上面的,并且是透明的。

    貌似这里所谓透明化系统状态栏才是本菜想要的,不管了,现在开始一一试验,至于这概念理解的对不对,管他呢?那到底应该叫什么,那我就叫自适应状态栏,行不行?

    前提条件

    让系统状态栏颜色随app主题颜色变化而变化这一设计,毫无疑问,也是向ios学习的:从android4.4开始引进的,并且在5.0进行了改进。因此,也只能将这一特性应用在android4.4以上的手机,无法做到全部适配。记得stormzhang(貌似是)曾说过:

    作为一个android程序员,还能有什么比做出ios风格的app更感到悲哀的呢?哎...

    两种情况下的解决方案:

    1. 使用toolbar
      这种方案相对简单,个人喜欢这种方案,本菜虽菜,但喜欢紧跟潮流。toolbar太好用了,
    2. 不使用toolbar

    1. 使用toolbar的解决方案

    这个方法参照了这里,薄荷app的toolbar适配方案

    其基本原理就是:
    theme里添加style: <item name="android:windowTranslucentStatus"> true </item>后,包含toolbar的内容布局就可以扩展至系统状态栏,状态栏会覆盖在toolbar上,如果此时使用android:fitsSystemWindows="true",就可以调整内容布局(估计也是在根布局上加padding)恢复到原来位置.但是,上面的解决方案确是给toolbar加上一个padding-top="25dp",这样就可以做到系统状态栏的颜色和toolbar的颜色保持一致.具体方案可以参照上面的薄荷app的方案链接.


    简述下步骤(只是简述,有疑问请参照上面薄荷app的链接即可):

    1. 引入v7包,并在布局里添加toolbar
    compile ‘com.android.support:appcompat-v7:22.2.1’
    
    1. 在代码中设置透明化:
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        WindowManager.LayoutParams localLayoutParams = getWindow().getAttributes();
        local LayoutParams.flags = (WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | localLayoutParams.flags);
    }
    

    当然也可以在theme的样式文件里添加style:<item name="android:windowTranslucentStatus">true</item>,效果相同,但是大神们都说样式文件里设置在某些型号里不生效.ok,大家都在代码里设置就好了

    1. 给toolbar加上padding-top,toolbar代码如下
    <android.support.v7.widget.Toolbar android:id="@+id/toolbar" 
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="@dimen/toolbar_padding_top"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
        app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        android:background="#30469b">
            <TextView android:layout_gravity="center" 
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:textSize="20sp"
              android:text="@string/app_name"/>
    </android.support.v7.widget.Toolbar>
    

    4.其中android:paddingTop="@dimen/toolbar_padding_top"要在values中的styles文件里设为0dp,在values-v19的styles里设为25dp,原因不多说了

    这样就可以达成了我们的目标,如果只是这样也就罢了,按照上面做就可以了,关键是本菜是喜欢紧跟潮流的,使用MD风格的DrawerLayout+NavigationView时,在android4.4的手机下,就会变这样了:


    android4.4上的效果

    很明显,drawerlayout并没用被扩展至系统状态栏,但在android5.0以上效果还是可以的,这让我很奇怪,只能归咎于5.0的优化了


    android5.0上的效果
    经过各种折腾终于想起来,可以把fitsSystemWindows的特性用在drawerlayout上试试,最后发现居然可以,最终将设置windowTranslucentStatus的代码调整如下:
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        WindowManager.LayoutParams localLayoutParams = getWindow().getAttributes();    
        local LayoutParams.flags = (WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | localLayoutParams.flags);
        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP){
            //将侧边栏顶部延伸至status bar
            mDrawerLayout.setFitsSystemWindows(true);        
            //将主页面顶部延伸至status bar;虽默认为false,但经测试,DrawerLayout需显示设置
            mDrawerLayout.setClipToPadding(false);
        }
    }
    

    最终android4.4上也可以显示正常:


    android4.4上修正后的效果

    2. 不使用toolbar的解决方案

    不使用toolbar时,而是actionbar时,因为actionbar不好定制,所以无法采用上面那个方法,只能采用其它方法,这里的方案主要参考这里:Translucent System Bar 的最佳实践
    这篇简书看的本菜晕乎乎的,仔细看下来,其实都是基于一个原理:
    不管有没有actionbar,内容布局的背景颜色一律设为主题颜色,然后有actionbar的话,就将actionbar与内容布局的背景颜色同时设为主题颜色,然后,每个内容布局的根布局都要设上fitsSystemWindows="true"进行调整,感觉超麻烦有没有?


    不说多少,简述步骤:

    1. 在代码中设置透明化,步骤同上
    2. 设置内容布局的根布局的背景颜色为主题颜色,同时设置fitsSystemWindows="true"
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorPrimary"
        android:fitsSystemWindows="true" 
        android:orientation="vertical">
    
    1. 在内容布局的下面再设置一层内容布局,设背景颜色为白色(或其它颜色):
    <LinearLayout    android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/c_light_white"
        android:gravity="center"
        android:orientation="vertical">
    

    至此,这种方案也完成了,看下效果:


    android4.4上的效果

    可以看出,这种方案一般情况下,还是可行的,但是有三个问题:

    1. 如果用上drawerlayout+navigationview,actionbar就会覆盖在侧边栏上(如上图),暂时未找到解决方案,但是我想说你都用drawerlayout+navigationview了,为何不用toolbar,因此这个问题应该不是问题,况且还可以使用其它的侧边栏实现方式,各位道友可以试试
    2. 这种方案在每个根布局上都要设fitsSystemWindows="true"进行调整,当然上面也有优化方案,可仍然觉得很麻烦,
    3. 每个根布局里都要多加的一层布局来覆盖根布局的背景主题颜色

    因此,这种方案的确不是上上之选.

    总结

    本文主要在考虑使用标题栏(actionbar/toolbar)的情况下,做出的方案,当然你也可以自定义标题栏,或者不使用标题栏;其实都可以基于上面一样的道理:

    在状态栏透明化的前提下,调整顶部view的padding-top,来达到状态栏自适应一体化的目的

    网上还有其它蛮多的解决方案,如:

    1. 使用开源库SystemBarTint,这个库也挺不错的,可以动态改变系统状态栏颜色,但是作者已经2年没有维护了,现在技术更新迭代这么快,鬼知道这个库会不会出现什么问题,因此可以放弃使用了
    2. 如果不怕麻烦,还可以new一个高度和状态栏一样高的view,插入到内容布局的上面,但是,想想都觉得麻烦,我也懒得试了

    关于本文中的样例代码已上传至github,欢迎star,有问题也欢迎各位撸友,道友们来指正.

    相关文章

      网友评论

      • 827f93bd49b0:为啥我运行楼主github上的demo在4.4上没有效果?还是黑色的状态栏啊
      • 桂林的小河:楼主写的很好,我也写了类似的实现方式,更精简一些,希望指教一下哈,http://www.jianshu.com/p/e89ee0a77bb5
      • a1cc38198f51:中兴的 ZTE BV0710 手机适配不了. 设置字体颜色无效.
      • b9b03d396e19:local LayoutParams.flags 这东西哪来的???
        故事细腻言不由己:去掉中间的空格啊亲。。。
      • 醉酒肆之: 手机系统5.0protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //取消标题栏//去掉标题栏注意这句一定要写在setContentView()方法的前面,不然会报错的
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        WindowManager.LayoutParams localLayoutParams = getWindow().getAttributes();
        localLayoutParams.flags = (WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | localLayoutParams.flags);
        }
      • 醉酒肆之:<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android&quot;
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        xmlns:app="http://schemas.android.com/apk/res-auto&quot;
        android:orientation="vertical">

        <app:android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
        app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        android:background="@color/topbar_bg">

        </app:android.support.v7.widget.Toolbar>

        <FrameLayout
        android:id="@+id/flBody"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white"
        />

        </LinearLayout>
      • 醉酒肆之:使用Toolbar,会产生页面距离状态栏,高度很高的空白沉浸区域,即便删除paddingTop也存在,如果删除Toolbar控件,那么消失,我不知哪里出了问题。
        王学宏:请问toolbar会遮挡一部分内容是怎么解决。
      • HeLe小子拽:https://github.com/gyf-dev/ImmersionBar android 4.4以上沉浸式状态栏管理,一句代码轻松实现,并且一句代码轻松改变状态栏字体深色与亮色,已经上传jcenter了,可以直接依赖,欢迎star
        爱哭的笨小孩:看了半天不如看评论席
        黑色幽默汪佳奇:nice,作者很用心!!!
      • Provence_a99c:谢谢楼主分享
      • ea3ed9b59c4a:不错,受教了
      • 70701e285746:android:fitsSystemWindows="true" 怎么把Activity设置偷摸啊?
      • 南朝小木瓜:请问我用的是自定义的toolbar,用第一种方法也可以么?
      • MaybeMercy:有的手机状态栏的高度不是 25 dp ,建议最好动态获取一下状态栏的高度。
      • 大象会飞95:试验可用!很好很方便!但是有一点当底部有输入框这时候软键盘弹出无法将输入框顶上去,楼主可有办法解决这一问题,麻烦告知!万分感谢!!!
        快乐物语_2266: mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        public void onGlobalLayout() {
        possiblyResizeChildOfContent();
        }
        });
        32dee779de4f:@球娃 兄弟 你解决了吗。。
        3d25c5710b35:兄弟,这个问题解决了吗
      • stormzhang:对,那句话是我说的,也希望每个 Androider 都能有这样的觉悟:Android is not iOS!
        0339b9a0cfd3:秦艾德特来参拜教主:fearful:
        wupapa:参见教主
        73efe271fa0c:参见教主
      • Nightsong:navigationbar设置颜色后,再通过手机滑动隐藏导航栏后,导航栏还会遮住view,如果设置透明色就不会遮住,怎么才能解决这个问题,因为我要设置成非透明色。
      • Derek_Yan:不是全透明的~~
        btman:@Derek_Yan 受教了 :smile: 由于没有5.0的真机,看别人文章说那个属性5.0就可以全透明,有机会试试去,多谢了 :pray: :pray:
        Derek_Yan:@btman getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        这个属性4.4算是全透明(有的机子是过渡形式的透明),5.0就是半透明了 我的模拟器、真机都是半透明,
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){//4.4 全透明状态栏
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        }

        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {//5.0 全透明实现
        Window window = getWindow();
        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
        | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        window.setStatusBarColor(Color.TRANSPARENT);//calculateStatusColor(Color.WHITE, (int) alphaValue)
        }
        以上代码基本解决适配各种版本全透明状态栏(如导航栏有需求可以再加导航栏)
        btman:@Derek_Yan 真机上是透明的。。

      本文标题:Android 系统状态栏沉浸式/透明化完整解决方案

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