android状态栏总结

作者: 紫阚 | 来源:发表于2016-06-23 10:35 被阅读2464次
    1. 针对状态栏的操作,只针对4.4kitKat(含)以上的机型,部分国产rom会失效,目前发现的有华为的EMUI
    1. Activity必须是noActionbar主题
    2. 本文基于StatusBarUtils略作修改,感谢作者laobie
    3. 本文源码地址
    相关属性重温
    • FitsSystemWindows
      在使用FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDSFLAG_TRANSLUCENT_STATUS挤占了状态栏的高度的时候,我们的布局文件也跟着顶到了状态栏上。通过FitsSystemWindows,系统会把app布局文件的paddingTop修改成状态栏的高度,达到适配的效果
      false顶到状态栏
    true,paddingTop有一个状态栏高度
    • android.R.id.content
      通过它我们可以在不需要知道ID的情况下,访问当前Activity根节点,一般是ContentFrameLayout类型,通过((ViewGroup)findViewById(R.id.content)).getChildAt(0);可以获取到我们布局xml的根节点

    • getDecorView()
      通过他,我们可以获取到整个Window界面的最顶层View。我们用的很多的findViewById(),就是基于这个DecorView查找里面的子节点的。比如Activity主题是Theme.AppCompat.Light.NoActionBar的组织层级如下,

    noActionbarView层次.png

    为什么嵌套那么深,因为各个层次可能都会有几个ViewStub方便注入,只是我们现在的主题用不到就是空了。

    下面开始实战了,一般我们用的比较多的是透明状态栏、设置状态栏颜色、DrawerLayout盖在状态栏上三种。

    1. 透明状态栏

    这种情况一般用于把图片延伸到状态栏上,图片还分两种

    1. 作为background的全屏大图
    2. 作为ImageView控件存在,一般只占屏幕高度的一部分

    这两种情况下的处理方式还不一样,具体看下文

    /** * 使状态栏透明 */
    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 | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);        
              activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
        } 
         else {       
              activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);   
      }
    }
    

    上面的代码针对5.0以上或者4.4以上5.0以下的机型,让其状态栏透明、导航栏半透明,效果如下:


    透明状态栏的结果是布局文件顶到了状态栏上

    dump了下节点信息,我们发现布局的paddingTop从(50px状态栏高度)被修改成了0px。从而让图片延伸到了状态栏

    不透明状态栏.png
    透明状态栏.png
    这种情况下,对app布局文件通过设置FitsSystemWindows就可以完成适配了
    /**
     * 获取activity的根节点
     *@param activity
     *@return
     */
    public static ViewGroup getAppRootView(Activity activity) { 
       return (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
    }
    
    /**  
    *将目标View的paddingTop从0设置为状态栏的高度
     */
    private static void setRootViewFitSystemWindow(Activity activity) { 
       ViewGroup rootView = getAppRootView(activity);   
       rootView.setFitsSystemWindows(true);   
       rootView.setClipToPadding(true);
    }
    
    app内容布局有了padding.png
    注意!!要延伸到状态栏的图片,如果不是根节点的背景图,而是一个ImageView控件,那么就不能设置setFitsSystemWindows了,原因参考上图红框。这种情况下,为了防止顶到状态栏,需要手动在xml里通过paddingTopmarginTop调整标题栏元素位置达到适配。类似下图 延伸到状态栏的图片是一个ImageView

    2. 状态栏颜色

    设置状态栏颜色的步骤和透明类似,差别在于5.0以下机型无法直接设置状态栏颜色,我们可以模拟一个纯色的View放到状态栏下面间接达到着色效果。放置的父层级我们选择DecorView

    如果发现状态栏颜色设置无效,有可能是布局文件的root节点加了backgroundColor所致

    /**
     * 
    设置状态栏颜色
     *
     * @param activity       需要设置的activity
     * @param color          状态栏颜色值
     * @param statusBarAlpha 状态栏透明度
     */
    public static void setColor(Activity activity, int color, int statusBarAlpha) { 
       if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)     return; 
    
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {  
          activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);       
          activity.getWindow().setStatusBarColor(UIUtils.calcColorWithAlpha(color, statusBarAlpha));   
     } else {        
         activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);     
         ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();        
         insertMockStatusBackgroundView(decorView, color, statusBarAlpha);  
      }    
         setRootViewFitSystemWindow(activity);
    }
    

    上面的代码判断DecorView下第0个View是不是我们自定义的StatusbarView类型,如果是则设置颜色,否则创建一个纯色控件插入到DecorView内,同时布局内容上间距腾出给纯色控件。

    3. DrawerLayout盖在状态栏上

    DrawerLayout主要有两个子元素1:内容View 2:侧滑栏
    侧滑栏必然要在内容View后面以覆盖前者

    侧滑栏效果

    所以我们要做的有2步

    1. 插入纯色控件到内容View(如果不是LinarLayout需要考虑内容View元素重新排列)
    2. DrawerLayout设置fitSystemWindow=false;以覆盖到状态栏上

    代码如下:

    /** * 为DrawerLayout 布局设置状态栏变色
     * 
    * @param activity       需要设置的activity 
    * @param drawerLayout   DrawerLayout
     * @param color          状态栏颜色值
     * @param statusBarAlpha 状态栏透明度 
    */public static void setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, int color, int statusBarAlpha) { 
       if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {        return;    }  
    
       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(Color.TRANSPARENT);   
       } else {        
         activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); 
       }    
         ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0);    
         //如果内容view不是LinearLayout,则需要重新排列内容view子元素的内容    
         insertMockStatusBackgroundView(contentLayout, color, statusBarAlpha);    
         drawerLayout.setFitsSystemWindows(false);
    }
    

    总结:

    上面三个方法,涵盖了app在大部分沉浸场景下的状态。在了解了底层实现后,我们也可以轻松在安卓上实现IOS的沉浸效果了,最低版本4.4 kitkat现在已经非常普及,随着MIUI、FlyME相继都升级到5.0、6.0以上,魅蓝、红米等大批国产中低端机型的都可以体验到这种酷炫的效果了。

    相关文章

      网友评论

      • 苍蝇的梦:注意!!要延伸到状态栏的图片,如果不是根节点的背景图,而是一个ImageView控件,那么就不能设置setFitsSystemWindows了,原因参考上图红框。这种情况下,为了防止顶到状态栏,需要手动在xml里通过paddingTop、marginTop调整标题栏元素位置达到适配。类似下图

        这边.只能手动设置top的距离嘛?不同机型距离会有差距啊.
        如果在activity里获取高度再设置又可能会出现时效问题
        紫阚:@拾荒者老大
        top距离可以根据本文提供的函数获取。同个机型状态栏高度是固定的,所以可以缓存起来不用重复计算
        时效问题没发现过,同步执行很快的。
      • lieco:你好,如果碰到虚拟键盘怎么办呢, 如你上面的亚索图,好像安卓当中也没有判断是否存在虚拟按键的方法。
        紫阚:@lieco 你说的是虚拟导航栏吧?我们会加一层黑色半透明的底色,具体样式由系统决定
        关键代码`WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION`
        你也可以在源码中移除这句话,让导航栏回复黑色底色。
      • Jsonzhang:首先感谢楼主的分享……在setColor的方法中,好像没有UIUtils.calcColorWithAlpha和insertMockStatusBackgroundView方法,我把代码copy过来找不到这两个?
        Jsonzhang:恩,谢谢
        紫阚:@Mrzhangsl 你好,文章的代码都是省略了细节的,源码在文章开头第四条超链接上;也可以直接过去http://git.oschina.net/yso/ZXUtils

      本文标题:android状态栏总结

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