Android 淺色狀態(tài)欄深色文字模式教程

我們都知道檬嘀,安卓可以自定義狀態(tài)欄是從 API19(4.4) 開始的憾股,Google 給 KitKat 引入了 translucentStatusBar 特性英遭。從此掷伙,安卓也可以像 iOS 一樣自定義狀態(tài)欄的顏色务唐,甚至可以將布局布到狀態(tài)欄后面雳攘。然而,一如往常谷歌出的東西都是坑的尿性枫笛,谷歌卻沒考慮到將狀態(tài)欄設置為淺色后吨灭,狀態(tài)欄上的文字和圖標辨識困難的問題。(這不叫黑刑巧,這叫愛的深沉)

直到 Google 在 2015 年的 Google I/O 大會上推出了一個安卓狀態(tài)欄的新特性:LightStatusBarWindow喧兄,即所謂的淺色狀態(tài)欄模式。從 API19 到 API23 過了整整四個大版本啊楚,這才解決了這個問題吠冤。沒錯,重要的東西總是晚來一步恭理。

然而拯辙,早在谷歌之前,國內的魅族與小米早已在自家的 Rom 中添加了淺色狀態(tài)欄的功能颜价。最低甚至支持 4.4 涯保。但是很多安卓開發(fā)者并不知道此事,以至于很多 app 使用了淺色的狀態(tài)欄導致用戶無法看清狀態(tài)欄上的文字圖標周伦。


左圖是數字尾巴夕春,正確使用了淺色狀態(tài)欄模式。右圖是某知名P2P app横辆,未使用淺色狀態(tài)欄模式撇他,右上角的充電圖標證明了該 app 并不是全屏狀態(tài)而是的確沒用淺色狀態(tài)欄= =

MIUI茄猫、Flyme以及原生安卓的淺色狀態(tài)欄實現各不相同。而且這三個實現該功能的最低版本要求也不同困肩。MIUI 的要求為 MIUI V6 及以上划纽,Flyme 為 Flyme4 及以上,原生安卓為 API23 及以上锌畸∮铝樱考慮到國內 MIUI 及 Flyme 用戶的數量,所以我們的 app 中勢必需要對當前手機系統(tǒng)進行判斷之后調用合適的方法來設置淺色狀態(tài)欄潭枣。

以下是我總結的判斷手機系統(tǒng)淺色狀態(tài)欄是否可用的工具類:

public class RomUtils {

    class AvailableRomType {
        public static final int MIUI = 1;
        public static final int FLYME = 2;
        public static final int ANDROID_NATIVE = 3;
        public static final int NA = 4;
    }

    public static boolean isLightStatusBarAvailable () {
        if (isMIUIV6OrAbove() || isFlymeV4OrAbove() || isAndroidMOrAbove()) {
            return true;
        }
        return false;
    }

    public static int getLightStatausBarAvailableRomType() {
        if (isMIUIV6OrAbove()) {
            return AvailableRomType.MIUI;
        }

        if (isFlymeV4OrAbove()) {
            return AvailableRomType.FLYME;
        }

        if (isAndroidMOrAbove()) {
            return AvailableRomType.ANDROID_NATIVE;
        }

        return AvailableRomType.NA;
    }

    //Flyme V4的displayId格式為 [Flyme OS 4.x.x.xA]
    //Flyme V5的displayId格式為 [Flyme 5.x.x.x beta]
    private static boolean isFlymeV4OrAbove() {
        String displayId = Build.DISPLAY;
        if (!TextUtils.isEmpty(displayId) && displayId.contains("Flyme")) {
            String[] displayIdArray = displayId.split(" ");
            for (String temp : displayIdArray) {
                //版本號4以上比默,形如4.x.
                if (temp.matches("^[4-9]\\.(\\d+\\.)+\\S*")) {
                    return true;
                }
            }
        }
        return false;
    }

    //MIUI V6對應的versionCode是4
    //MIUI V7對應的versionCode是5
    private static boolean isMIUIV6OrAbove() {
        String miuiVersionCodeStr = getSystemProperty("ro.miui.ui.version.code");
        if (!TextUtils.isEmpty(miuiVersionCodeStr)) {
            try {
                int miuiVersionCode = Integer.parseInt(miuiVersionCodeStr);
                if (miuiVersionCode >= 4) {
                    return true;
                }
            } catch (Exception e) {}
        }
        return false;
    }

    //Android Api 23以上
    private static boolean isAndroidMOrAbove() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            return true;
        }
        return false;
    }

    private static String getSystemProperty(String propName) {
        String line;
        BufferedReader input = null;
        try {
            Process p = Runtime.getRuntime().exec("getprop " + propName);
            input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);
            line = input.readLine();
            input.close();
        } catch (IOException ex) {
            return null;
        } finally {
            if (input != null) {
                try {
                    input.close();
                } catch (IOException e) {
                }
            }
        }
        return line;
    }
}

當判斷當前系統(tǒng)可以使用淺色狀態(tài)欄可以用后,根據得到的 Rom 類型調用對應的設置淺色狀態(tài)欄方法:

public class LightStatusBarUtils {

    public static void setLightStatusBar(Activity activity, boolean dark) {
        switch (RomUtils.getLightStatausBarAvailableRomType()) {
            case RomUtils.AvailableRomType.MIUI:
                setMIUILightStatusBar(activity, dark);
                break;

            case RomUtils.AvailableRomType.FLYME:
                setFlymeLightStatusBar(activity, dark);
                break;

            case RomUtils.AvailableRomType.ANDROID_NATIVE:
                setAndroidNativeLightStatusBar(activity, dark);
                break;

            case RomUtils.AvailableRomType.NA:
                // N/A do nothing
                break;
        }
    }

    private static boolean setMIUILightStatusBar(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);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    private static boolean setFlymeLightStatusBar(Activity activity, boolean dark) {
        boolean result = false;
        if (activity != null) {
            try {
                WindowManager.LayoutParams lp = activity.getWindow().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(null);
                int value = meizuFlags.getInt(lp);
                if (dark) {
                    value |= bit;
                } else {
                    value &= ~bit;
                }
                meizuFlags.setInt(lp, value);
                activity.getWindow().setAttributes(lp);
                result = true;
            } catch (Exception e) {
            }
        }
        return result;
    }

    private static void setAndroidNativeLightStatusBar(Activity activity, boolean dark) {
        View decor = activity.getWindow().getDecorView();
        if (dark) {
            decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
        } else {
            // We want to change tint color to white again.
            // You can also record the flags in advance so that you can turn UI back completely if
            // you have set other flags before, such as translucent or full screen.
            decor.setSystemUiVisibility(0);
        }
    }

}

以上就是我總結的用于設置淺色狀態(tài)欄的兩個主要類:RomUtils 用于判定當前系統(tǒng)是否可用淺色狀態(tài)欄模式盆犁,以及當前系統(tǒng)的類型命咐。LightStatusBarUtils 包含了各個系統(tǒng)對應的設置淺色狀態(tài)欄的方法,開發(fā)者可以根據 RomUtils 得到的 Rom 類型調用其中對應的方法谐岁。

接著我們創(chuàng)建一個 app 項目醋奠,來試試效果。在 MainActivity 的布局中我放了兩個 TextView 和一個 Button伊佃。兩個 TextView 分別用于顯示當前系統(tǒng)版本信息和顯示當前系統(tǒng)是否可用淺色狀態(tài)欄模式窜司。Button 用于在淺色狀態(tài)欄模式和普通模式之間切換。

以下是 MainActivity :

public class MainActivity extends AppCompatActivity {

    private boolean isLightStatusBarNow = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView sysInfoTv = (TextView) findViewById(R.id.tv_sys_info);
        TextView hintTv = (TextView) findViewById(R.id.tv_hint);
        Button switchBtn = (Button) findViewById(R.id.btn_switch);

        sysInfoTv.setText(Build.BRAND + " - " + Build.MODEL + " - SDK Version:" + Build.VERSION.SDK_INT);

        switch (RomUtils.getLightStatausBarAvailableRomType()) {
            case RomUtils.AvailableRomType.MIUI:
                hintTv.setText("當前系統(tǒng)為MIUI6或以上 淺色狀態(tài)欄可用");
                break;

            case RomUtils.AvailableRomType.FLYME:
                hintTv.setText("當前系統(tǒng)為Flyme4或以上 淺色狀態(tài)欄可用");
                break;

            case RomUtils.AvailableRomType.ANDROID_NATIVE:
                hintTv.setText("當前系統(tǒng)為Android M或以上 淺色狀態(tài)欄可用");
                break;

            case RomUtils.AvailableRomType.NA:
                hintTv.setText("當前系統(tǒng)淺色狀態(tài)欄不可用");
                switchBtn.setEnabled(false);
                switchBtn.setText("light status bar mode not available");
                break;
        }

        switchBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isLightStatusBarNow) {
                    LightStatusBarUtils.setLightStatusBar(MainActivity.this, false);
                    isLightStatusBarNow = false;
                } else {
                    LightStatusBarUtils.setLightStatusBar(MainActivity.this, true);
                    isLightStatusBarNow = true;
                }

            }
        });

    }
}

手機上運行效果:


效果圖

一些常見機型上的效果:


一些常見機型上的效果

存在的坑:

  • 使用原生安卓的方法來實現淺色狀態(tài)欄的前提是航揉,當前 Activity 所在的 Window 必須有 windowDrawsSystemBarBackgrounds 屬性塞祈,而且必須未設置過 windowTranslucentStatus 屬性。
    如果當前 Window 設置了 windowTranslucentStatus 屬性帅涂,那么調用上面設置淺色狀態(tài)欄的方法就不會生效议薪。
    詳細見 API 說明:
    https://developer.android.com/reference/android/R.attr.html#windowLightStatusBar
  • 據說安卓原生實現方法在 MIUI android 版本為6.0上無效(見參考2,手頭沒設備測試)媳友。所以我的代碼中將 MIUI 和 Flyme 的判斷放在原生安卓前面笙蒙,這樣如果是 MIUI 或 Flyme 的設備,優(yōu)先調用各自的實現方法庆锦。
Demo代碼: 地址

參考:

  1. http://blog.isming.me/2016/01/09/chang-android-statusbar-text-color/
  2. http://www.reibang.com/p/7f5a9969be53
  3. http://dev.xiaomi.com/doc/?p=254
  4. http://blog.csdn.net/devilkin64/article/details/19415717

本文章為原創(chuàng)作品,轉載請注明出處轧葛。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末搂抒,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子尿扯,更是在濱河造成了極大的恐慌求晶,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件衷笋,死亡現場離奇詭異芳杏,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門爵赵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吝秕,“玉大人,你說我怎么就攤上這事空幻。” “怎么了?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵翰意,是天一觀的道長战惊。 經常有香客問我,道長但两,這世上最難降的妖魔是什么鬓梅? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮谨湘,結果婚禮上绽快,老公的妹妹穿的比我還像新娘。我一直安慰自己悲关,他們只是感情好谎僻,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著寓辱,像睡著了一般艘绍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上秫筏,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天诱鞠,我揣著相機與錄音,去河邊找鬼这敬。 笑死航夺,一個胖子當著我的面吹牛,可吹牛的內容都是我干的崔涂。 我是一名探鬼主播阳掐,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼冷蚂!你這毒婦竟也來了缭保?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤蝙茶,失蹤者是張志新(化名)和其女友劉穎艺骂,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體隆夯,經...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡钳恕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年别伏,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片忧额。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡厘肮,死狀恐怖,靈堂內的尸體忽然破棺而出宙址,到底是詐尸還是另有隱情轴脐,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布抡砂,位于F島的核電站大咱,受9級特大地震影響,放射性物質發(fā)生泄漏注益。R本人自食惡果不足惜碴巾,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望丑搔。 院中可真熱鬧厦瓢,春花似錦、人聲如沸啤月。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谎仲。三九已至浙垫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間郑诺,已是汗流浹背夹姥。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留辙诞,地道東北人辙售。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像飞涂,于是被迫代替她去往敵國和親旦部。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內容