Android适配---沉浸式状态栏

沉浸式状态栏

同事为状态栏有蒙层和无法调整黑色字符图标困扰了很久,最近稍微闲下来了,就开始搞一搞。我翻看了今日头条、微博的处理情况,都很ok啊。算是同是做资讯的,没理由咱不行啊,待我试试?

  • 沉浸式状态栏版本兼容情况
Android版本 <4.4 4.4-5.0 =>5.0
透明状态栏 ×
Android版本 <6.0 =>6.0
黑白字符状态栏 ×

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. 这句不是很明白,我想官方想表达的意思是,要想生效,需要设置沉浸式状态栏。

  • 注意: 该方法在4.4以上才会生效,android:fitsSystemWindows默认值为false,并且在哪个控件设置android:fitsSystemWindows="true"会有不一样的效果:
  1. 不设置android:fitsSystemWindows值,效果如图1
  2. 只要LinearLayout设置了android:fitsSystemWindows="true",子控件(如下面布局代码中TextView)不管设不设置为true,只会为LinearLayout添加padding给系统窗口预留空间,效果如图2
  3. 只设置TextView的android:fitsSystemWindows="true",只会为TextView添加padding给系统窗口预留空间,有一个灰色蒙层导致色值有点偏差,往下看有解决方法,效果如图3
<?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:orientation="vertical"
    android:background="@mipmap/beauty"
    tools:context="com.blacktoast.demo.statusbar.MainActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:background="#55ff0000"
        android:fitsSystemWindows="true" />

</LinearLayout>
不设置android:fitsSystemWindows值
LinearLayout设置了android:fitsSystemWindows="true"
只设置TextView的android:fitsSystemWindows="true"

透明状态栏(灰色蒙层)

  1. <4.4版本无法调整透明状态栏

无处理方案,GG

  1. 4.4 <= 版本 < 5.0 系统提供方法调整透明状态栏

分为两种方法实现styles.xml和代码调整

styles.xml

  • values/styles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>

    <!-- 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.MainTheme"></style>
</resources>
  • values-v19/styles.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="AppTheme.MainTheme">
        <item name="android:windowTranslucentStatus">true</item>
    </style>
</resources>

代码调整

window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
  1. 版本>= 6.0可能有灰色蒙层(我们需要解决的问题在这)

之前没遇到这个问题,一接手有点懵逼。。先Google看看?。参照这个链接试了一把解决Android7.0下沉浸式状态栏变灰问题,反正我是没试成功。。博主分析的没毛病,后来一细想,应该是我没添加FLAG_TRANSLUCENT_STATUS的原因。我没去深究,因为我发现新的解决思路。

  • 7.0DecorView源码
DecorView(Context context, int featureId, PhoneWindow window,WindowManager.LayoutParams params) {
    super(context);
    ......//省略无关代码
    mForceWindowDrawsStatusBarBackground = context.getResources()
        .getBoolean(R.bool.config_forceWindowDrawsStatusBarBackground)
        && context.getApplicationInfo().targetSdkVersion >= N;
    
    //设置默认的值,灰色
    mSemiTransparentStatusBarColor = context.getResources()
    .getColor(R.color.system_bar_background_semi_transparent, null /* theme */);
    ......//省略无关代码
}
private int calculateStatusBarColor() {
    int flags = mWindow.getAttributes().flags;
    return (flags & FLAG_TRANSLUCENT_STATUS) != 0 ?
        mSemiTransparentStatusBarColor : 
        (flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 ?
        mWindow.mStatusBarColor : Color.BLACK;
}
  • 博主是通过反射更改默认色值,博主的分析我就不讲解了,可以自己去详细看。系统获取状态栏色值主要是通过calculateStatusBarColor()方法,分为三种情况:
  1. 如果window的flag包含FLAG_TRANSLUCENT_STATUS,则使用mSemiTransparentStatusBarColor色值,即使用R.color.system_bar_background_semi_transparent色值;
  2. 如果window的flag不包含FLAG_TRANSLUCENT_STATUS,并且window的flag包含FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS时,使用mWindow.mStatusBarColor色值,即setStatusColor的色值;
  3. 如果window的flag不包含FLAG_TRANSLUCENT_STATUS,并且window的flag不包含FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS时,使用黑色Color.BLACK

使用方式1和2都能实现透明状态栏的效果,以下采用方式2实现,注意:需放在setContentView()方法之前

Window window = activity.getWindow();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
            | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
    window.getDecorView().setSystemUiVisibility(option);
    window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
    activity.getWindow().setStatusBarColor(色值);
}

黑白图标字符状态栏

解决黑白图标字符状态栏需考虑三种情况:魅族手机、小米手机、其他

  1. 魅族手机:Flyme系统API
  2. 小米手机:MIUI 9「状态栏黑色字符」实现方法变更通知
  3. 其他:
  4. 锤子手机:使用3的处理方式,还需手动调整设置(设置-全局高级设置-状态栏风格-自适应),否则还是存在灰色蒙层
    public static void setStatusTextColor(boolean useDart, Activity activity) {
        if (isFlyme()) {
            processFlyme(activity, useDart);
        } else if (isMIUI()) {
            processMIUI(activity, useDart);
        } else {
            if (useDart) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    activity.getWindow().getDecorView().setSystemUiVisibility(
                            View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
                }
            } else {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    activity.getWindow().getDecorView().setSystemUiVisibility(
                            View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
                }
            }
        }
    }
    private static void processFlyme(Activity activity, boolean darkmode) {
        Window window = activity.getWindow();
        if(window != null) {
            try {
                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((Object)null);
                int value = meizuFlags.getInt(lp);
                if(darkmode) {
                    value |= bit;
                } else {
                    value &= ~bit;
                }

                meizuFlags.setInt(lp, value);
                window.setAttributes(lp);
            } catch (Exception var8) {
                Log.w("StatusBarUtils", "setStatusBarDarkIcon: failed");
            }
        }
    }
    private static void processMIUI(Activity activity, boolean darkmode) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {   // 即基于 Android 6.0 ,开发版 7.7.13 及以后版本
            compatHighMIUI(activity, darkmode);
        } else {
            compatLowMIUI(activity, darkmode);
        }
    }
    @TargetApi(Build.VERSION_CODES.M)
    private static void compatHighMIUI(Activity activity, boolean darkmode) {
        View decorView = activity.getWindow().getDecorView();
        if (darkmode) {
            decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
        } else {
            int flag = decorView.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
            decorView.setSystemUiVisibility(flag);
        }
    }
    private static void compatLowMIUI(Activity activity, boolean darkmode) {
        Class<? extends Window> clazz = activity.getWindow().getClass();
        try {
            int darkModeFlag = 0;
            Class<?> layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
            Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
            darkModeFlag = field.getInt(layoutParams);
            Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
            extraFlagField.invoke(activity.getWindow(), darkmode ? darkModeFlag : 0, darkModeFlag);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

参考demo

参考链接:

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

推荐阅读更多精彩内容