沉浸式:Android KITKAT(4.4)以后(包括4.4)

activity视图.png
//DecorView获取
Activity.getWindow().getDecorView()
//DecorView获取contentView
Activity.findViewById(android.R.id.content)
//获取activity的 setContentView(int LayoutXml);//即我们xml里面写的xml布局
ViewGroup rootView = (ViewGroup) content.getChildAt(0);

全屏化一般用在启动页:
参考:http://blog.csdn.net/lu_ca/article/details/72778694

requestWindowFeature(Window.FEATURE_NO_TITLE);// 隐藏标题
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);// 设置全屏

setContentView(R.layout.activity_main);
注意:setContentView一定要写在设置全屏后边

沉浸式的一般套路

在Android上,关于对StatusBar(状态栏)的操作,一直都在不断改善,并且表现越来越好,在Android4.4 以下,我们可以对StatusBar和 NavigationBar进行显示和隐藏操作。但是直到Android4.4,我们才能真正意义上的实现沉浸式状态栏。从Android4.4 到现在(Android 7.1),关于沉浸式大概可以分成三个阶段:

  • 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)开始,我们可以改状态栏的绘制模式,
    SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
    可以显示白色或浅黑色的内容和图标(除了魅族手机,魅族自家有做源码更改,6.0以下就能实现)

  • 代码未行,效果先上

image.png
  • Android4.4(API 19) - Android 5.0(API 21)如何实现

在 [KITKAT][null-link] 之后,Android Window支持了一些新的属性,4.4以上能设置沉浸状态栏,正是因为下面的flag引入了,通过这两个flag可以设置Status Bar或Nav Bar透明。设置这个属性不做其他操作一定会入侵状态栏

WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION

View.SYSTEM_UI_FLAG_LAYOUT_STABLEView.SYSTEM_UI_FLAG_LAYOUT_FULLSCRE这两个flag会被自动添加到system UI visibility中。

正如它们的变量名的意思,使用这两个属性,可以使得状态栏和导航栏变为透明,导航栏指的就是Android下方的三大按键,当然只使用第一个属性也可以达到今天所要完成的效果。下面的示例代码将使状态栏和导航栏变得透明
我们注意到上面的flag名为TRANSLUCENT,而不是TRANSPARENT知乎 指出了,TranslucentStatus 在 4.4 和 5.x 上表现不同,4.4 是一层渐变的遮罩层,5.x 以上是一条半透明的遮罩层(5.0以上不建议用这个方法,下面有详解),比如Genymotion模拟器上就是这样,但在一些其他机器比如小米上,就是全透明的,这应该是和系统有关的。
Genymotion模拟器4.4上透明状态栏的效果:

image.png

小米4.4上透明状态栏的效果:
image.png

Genymotion模拟器5.x上透明状态栏的效果:
image.png

  @Override
  protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       initWindow();
  }

 @TargetApi(19)
 private void initWindow(){
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
           getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
           getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);   
    }

直接运行之后,状态栏直接透明了,但是并不是我们想要的效果。当设置Status Bar透明后,由于visibility flag的自动添加,屏幕会变成全屏的。所以这时候ToolBar就直接跑到StatusBar下面了,如图:

image.png

这个问题也很好解决,在 style theme 添加,或者Activity的layout的属性android:fitsSystemWindows="true",但是发现,StatusBar变灰色了。(那是xml里面主布局的颜色,因为fitsSystemWindows是设置padding的,其实只需要把xml主布局颜色设置了就不会变灰了)

image.png

小结:
Android4.4上实现沉浸式状态栏的套路是:为window添加FLAG_TRANSLUCENT_STATUS Flag

  • 方法1.往DecorView添加一个和status bar 一样大小的View 站位,然后添加setFitsSystemWindows从而让让标题栏不会与status bar 重叠。(因为DecorView是framlayout即使加View也不行把contenView顶下来,所以contenView增加setFitsSystemWindows属性会自定增加status bar的高度)即上面演示的代码。
  • 方法2.不加VIew,直接手动设置xml里面的主布局padding属性为statusView的高度。(不要设置margin会出现状态栏变成灰色)
  • 方法3.最简单,直接设置setFitsSystemWindows后,设置主布局颜色为标题栏颜色就可以了。但是这样的做法不能解决图片问题沉浸式问题。
  • 若:而图片延伸到状态栏只需要设置FLAG_TRANSLUCENT_STATUS就OK
    ps:
    android:clipChildren:clipChildren表示是否限制子View在其范围内,默认true
    android:clipToPadding:ClipToPadding用来定义ViewGroup是否允许在padding中绘制。默认情况下,cliptopadding被设置为ture, 也就是把padding中的值都进行裁切了,如图片超出边界后被裁剪。默认true
  • 2.2 Android 5.0(API 21)以上实现沉浸式的方式

google 加入了一个比较重要的方法setStatusBarColor (对应属性:android:statusBarColor)
注意:想要这个方法生效,必须还要配合一个Flag一起使用,必须设置FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS ,并且不能设置FLAG_TRANSLUCENT_STATUS(Android 4.4才用这个)
ps:如果绘制的时候不清除这个flag或者加进去,View.SYSTEM_UI_FLAG_LAYOUT_STABLE的话,实现statusBar绘制了同时,自己的View也会入侵
这个flag 也是在Android 5.0添加的,它的作用是什么呢?

解释:设置了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));

Android 5.0图片延伸到状态栏只需设置windowTranslucentStatus,将 statusBarColor 设置为透明即可:就是相当于没有清除FLAG_TRANSLUCENT_STATUS,所以xml入侵了

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

tip

在Android 5.0 之后我们除了可以在代码中改变状态栏的颜色,还可以在XML中设置主题色,这种方式我们的App不属于沉浸式,在状态栏的下面

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">    
    <!-- Customize your theme here. -->    
      <!-- Customize your theme here. -->  
    <item name="colorPrimary">@color/colorPrimary</item>       <!-- toolbar -->      
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>   <!-- 状态栏 -->    
    <item name="colorAccent">@color/colorAccent</item>              <!-- 页面中常用控件默认颜色 -->  
    <item name="android:windowBackground">@color/bg_f6</item><!--window背景-->  
</style>
image.png

注意:FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,表明会Window负责系统bar的background 绘制(得先清除4.4的flag),所以不需要设置fitsSystemWindows或者手动设置一个和statusBar一样的大小的View,但是想实现图片沉浸式,要设置windowTranslucentStatus
2.3 Android 6.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 才会生效。
代码:

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

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

<style name="MDTheme" parent="Theme.Design.Light.NoActionBar">
              //为了防止冲突取消4.4的透明
        <item name="android:windowTranslucentStatus">false</item>
          //设置5.0的沉浸式
        <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>
   <!-- <item name="android:windowTranslucentStatus">true</item> -->         <!-- 适用4.4到5.0的系统-->  
    </style>

好了说到这里:如何统一4.4和5.0的沉浸式呢?

个人认为方法有几个方法:

法一:设置 fitsSystemWindows 属性

这样既能解决标题栏入侵状态栏问题,和输入法弹出问题
在 style theme 添加,或者Activity的layout的属性android:fitsSystemWindows="true"!

属性解释:在沉浸式的情况下,如果某个View 的fitsSystemWindows 设为true,那么该View的padding属性将由系统设置,用户在布局文件中设置的padding会被忽略。系统会为该View设置一个paddingTop,值为statusbar的高度。fitsSystemWindows默认为false

参考:https://www.jianshu.com/p/5cc3bd23be7b
设置前这个属性之前是这样的的:

image.png

但是设置完之后就变成,顶部变白色(其实那是根布局的颜色)


image.png

解决方法一:根布局设置为标题所需要的颜色
解决方法二:自己在decoView绘制一个和状态栏大小一样,颜色和标题栏一样的View!具体步骤!
解决:自己添加一个带颜色和状态栏一样高的矩形的View

/**
     * 生成一个和状态栏大小相同的矩形条
     *
     * @param activity 需要设置的activity
     * @param color    状态栏颜色值
     * @return 状态栏矩形条
     */
    private static View createStatusView(Activity activity, int color) {
        // 获得状态栏高度
        int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android");
        int statusBarHeight = activity.getResources().getDimensionPixelSize(resourceId);

        // 绘制一个和状态栏一样高的矩形
        View statusView = new View(activity);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                statusBarHeight);
        statusView.setLayoutParams(params);
        statusView.setBackgroundColor(color);
        return statusView;
    }

整体代码:

   /**
     * 设置状态栏颜色
     *
     * @param activity 需要设置的activity
     * @param color    状态栏颜色值
     */
    public static void setColor(Activity activity, int color) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            // 设置状态栏透明
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
            // 生成一个状态栏大小的矩形
            View statusView = createStatusView(activity, color);
            // 添加 statusView 到布局中
            ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
            decorView.addView(statusView);
            // 设置根布局的参数
            ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
            rootView.setFitsSystemWindows(true);
            rootView.setClipToPadding(true);
        }
    }

在 setContentView() 之后调用 setColor(Activity activity, int color) 方法即可。

法二:设置手动设置标题栏的paddingTop

这可以实现titlebar整体文字不入侵状态栏,ImageView整体浸入入侵状态栏,但是得手动解决输入法问题(5.0以后的不要清除FLAG_TRANSLUCENT_STATUS )

/**
 * 通过设置全屏,设置状态栏透明
 *
 * @param activity
 */
private void fullScreen(Activity activity) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        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 {
            Window window = activity.getWindow();
            WindowManager.LayoutParams attributes = window.getAttributes();
            int flagTranslucentStatus = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
            int flagTranslucentNavigation = WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
            attributes.flags |= flagTranslucentStatus;
//                attributes.flags |= flagTranslucentNavigation;
            window.setAttributes(attributes);
        }
    }
}

 public  void setTitleBarPadding(final Activity activity, final View viewPadding) {
        if (viewPadding != null) {
            Object haveSetOffset = viewPadding.getTag(TAG_KEY_HAVE_SET_OFFSET);
            if (haveSetOffset != null && (Boolean) haveSetOffset) {
                return;
            }
            viewPadding.setPadding(viewPadding.getPaddingLeft(), viewPadding.getPaddingTop() + getStatusBarHeight(activity),
                    viewPadding.getPaddingRight(), viewPadding.getPaddingBottom());
            viewPadding.setTag(TAG_KEY_HAVE_SET_OFFSET, true);
        }
      
    }

在 setContentView() 之后调用 fullScreen() 和setTitleBarPadding()方法即可。

法三:直接在标题栏里面设置一个跟状态栏一样大小的View。

得到的效果与2类似。
解决输入法与沉浸式冲突:https://blog.csdn.net/smileiam/article/details/69055963

推荐库:https://github.com/gyf-dev/ImmersionBar
推荐工具类:https://github.com/laobie/StatusBarUtil

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,274评论 25 707
  • 一、前言 其实我是不打算写这篇文章的,为什么呢?因为关于沉浸式状态栏的文章太多了,随便google一下就能出来几十...
    依然范特稀西阅读 44,170评论 27 180
  • 前言 原文:http://blog.csdn.net/mybeta/article/details/5076032...
    naturs阅读 23,069评论 8 70
  • 情人节快到了,肉爸独自在在家里向毛毛吹嘘自己年轻时的历史“毛毛,旺仔,你肉爸我年轻那会儿啊,那是个情史丰富啊,什么...
    南笙北爱阅读 199评论 0 0
  • 人、目的、行为、媒介、环境、时间(通过集创堂前辈们的学习,在五要素的后面加上了“时间”)
    DQLee阅读 175评论 0 0