1. 程式人生 > >【Android】狀態列相關適配(判斷MIUI,Flyme,狀態列圖示顏色切換,獲取狀態列高度,沉浸式狀態列相關等)

【Android】狀態列相關適配(判斷MIUI,Flyme,狀態列圖示顏色切換,獲取狀態列高度,沉浸式狀態列相關等)

對於狀態列相關適配這個事情,真是讓人頭疼的一個模組。因為負責的專案主題色偏偏是白色,不但要去適配 MIUI ,Flyme(因為這兩個都可以實現沉浸式,並且圖示可以切換成黑色),也要分別適配 Android 6.0 以下, Android 6.0 起兩種不同情況(6.0 起原生提供了高亮狀態列模式,該模式下狀態列圖示可以切換成黑色)。一大堆坑,坑到自己都怕......

現在就把一些相關實用的方法、注意點彙集起來,並推薦一些相關實用的庫。

首先放程式碼:

BuildProperties.java

public class BuildProperties {
    private final Properties properties;

    private BuildProperties() throws IOException {
        properties = new Properties();
        properties.load(new FileInputStream(new File(Environment.getRootDirectory(), "build.prop")));
    }

    public boolean containsKey(final Object key) {
        return properties.containsKey(key);
    }

    public boolean containsValue(final Object value) {
        return properties.containsValue(value);
    }

    public Set<Map.Entry<Object, Object>> entrySet() {
        return properties.entrySet();
    }

    public String getProperty(final String name) {
        return properties.getProperty(name);
    }

    public String getProperty(final String name, final String defaultValue) {
        return properties.getProperty(name, defaultValue);
    }

    public boolean isEmpty() {
        return properties.isEmpty();
    }

    public Enumeration<Object> keys() {
        return properties.keys();
    }

    public Set<Object> keySet() {
        return properties.keySet();
    }

    public int size() {
        return properties.size();
    }

    public Collection<Object> values() {
        return properties.values();
    }

    public static BuildProperties newInstance() throws IOException {
        return new BuildProperties();
    }
}

OSHelper.java
public class OSHelper {
    private static final String KEY_MIUI_VERSION_CODE = "ro.miui.ui.version.code";
    private static final String KEY_MIUI_VERSION_NAME = "ro.miui.ui.version.name";
    private static final String KEY_MIUI_INTERNAL_STORAGE = "ro.miui.internal.storage";
    private static boolean isTranslucentStatusMiUi;
    private static boolean isFlyMe;

    static {
        isTranslucentStatusMiUi = isTranslucentStatusMiUiVersion();
        isFlyMe = isFlyMeOS();
    }

    //是否是支援沉浸式狀態列的 MiUi
    public static boolean isTranslucentStatusMiUi() {
        return isTranslucentStatusMiUi;
    }

    //判斷是否是支援沉浸式狀態列(以及狀態列圖示變黑色)的 MiUi 版本
    private static boolean isTranslucentStatusMiUiVersion() {
        try {
            Class<?> sysClass = Class.forName("android.os.SystemProperties");
            Method getStringMethod = sysClass.getDeclaredMethod("get", String.class);
            boolean isMiUiV6 = "V6".equals(getStringMethod.invoke(sysClass, "ro.miui.ui.version.name"));
            boolean isMiUiV7 = "V7".equals(getStringMethod.invoke(sysClass, "ro.miui.ui.version.name"));
            boolean isMiUiV8 = "V8".equals(getStringMethod.invoke(sysClass, "ro.miui.ui.version.name"));
            boolean isMiUiV9 = "V9".equals(getStringMethod.invoke(sysClass, "ro.miui.ui.version.name"));
            return isMiUiV6|isMiUiV7|isMiUiV8|isMiUiV9;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    //判斷是否是小米的 MiUi(所有版本)
    public static boolean isMiUiOS() {
        try {
            final BuildProperties prop = BuildProperties.newInstance();
            return prop.getProperty(KEY_MIUI_VERSION_CODE, null) != null
                    || prop.getProperty(KEY_MIUI_VERSION_NAME, null) != null
                    || prop.getProperty(KEY_MIUI_INTERNAL_STORAGE, null) != null;
        } catch (final IOException e) {
            return false;
        }
    }

    /**
     * set status bar darkMode
     *
     * MiUi 將修改MiUiWindowManager的部分LayoutParams
     * FlyMe 將呼叫其對應的API
     * Android 6.0 起將呼叫高亮狀態列模式
     *
     * @param darkMode 是否是黑色模式
     * @param activity 所要設定的activity
     */
    public static void setStatusBarDarkMode(boolean darkMode, Activity activity) {
        if (isTranslucentStatusMiUi) {
            Class<? extends Window> clazz = activity.getWindow().getClass();
            try {
                Class<?> layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
                Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
                int 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();
            }
        } else if(isFlyMe) {
            if(darkMode) StatusBarProxy.setStatusBarDarkIcon(activity.getWindow(), true);
            else StatusBarProxy.setStatusBarDarkIcon(activity.getWindow(), false);
        }

        //MiUi 和 FlyMe 均再進行高亮模式設定,為了避免系統版本在Android M起切換黑色模式無效的問題
        setLightStatusBar(darkMode, activity);
    }

    /**
     *
     * Android 6.0起設定原生狀態列高亮模式(高亮模式下狀態列文字及圖示將變成灰色)
     *
     * @param lightMode 是否是高亮模式
     * @param activity 所要設定的activity
     */
    private static void setLightStatusBar(boolean lightMode, Activity activity) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            int vis = activity.getWindow().getDecorView().getSystemUiVisibility();
            if(lightMode) vis |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
            else vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
            activity.getWindow().getDecorView().setSystemUiVisibility(vis);
        }
    }

    //判斷是否是魅族的 FlyMe OS
    private static boolean isFlyMeOS() {
        /* 獲取魅族系統操作版本標識*/
        String meiZuFlyMeOSFlag = getSystemProperty("ro.build.display.id", "");
        if (TextUtils.isEmpty(meiZuFlyMeOSFlag)){
            return false;
        } else if (meiZuFlyMeOSFlag.contains("flyme") || meiZuFlyMeOSFlag.toLowerCase().contains("flyme")){
            return true;
        } else {
            return false;
        }
    }

    //是否是魅族的 FlyMe
    public static boolean isFlyMe() {
        return isFlyMe;
    }

    /**
     * 獲取系統屬性
     * @param key  ro.build.display.id
     * @param defaultValue 預設值
     * @return 系統操作版本標識
     */
    private static String getSystemProperty(String key, String defaultValue) {
        try {
            Class<?> clz = Class.forName("android.os.SystemProperties");
            Method get = clz.getMethod("get", String.class, String.class);
            return (String)get.invoke(clz, key, defaultValue);
        } catch (Exception e) {
            return null;
        }
    }

    //獲取狀態列高度
    public static int getStatusBarHeight(Activity activity) {
        int result = 0;
        int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android");
        if (resourceId > 0) {
            result = activity.getResources().getDimensionPixelSize(resourceId);
        }
        return result;
    }

    /**
     * 設定系統沉浸式狀態列屬性(系統版本需高於等於 Android 4.4,如果是 FlyMe 並低於 Android 4.4 時呼叫其對應 API)
     *
     * @param activity 所要設定的activity
     * @param on 是否開啟沉浸式
     */
    public static void setTranslucentStatus(Activity activity, boolean on) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            Window win = activity.getWindow();
            WindowManager.LayoutParams winParams = win.getAttributes();
            final int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
            if (on) {
                winParams.flags |= bits;
            } else {
                winParams.flags &= ~bits;
            }
            win.setAttributes(winParams);
        } else {
            if(isFlyMe) StatusBarProxy.setImmersedWindow(activity.getWindow(), on);
        }
    }

    /**
     * 設定狀態列顏色(該方法只作用於 Android 5.0 起的系統版本)
     *
     * @param activity 所要設定的activity
     * @param color 要設定的顏色,非 R.color.xxx 形式
     */
    public static void setStatusBarColor(Activity activity, int color) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            activity.getWindow().setStatusBarColor(color);
        }
    }
}

下面就來分別說明:

1、判斷 MIUI 的方法,有兩個,一個是判斷全部版本的,另一個是判斷是否支援沉浸式以及狀態列圖示變黑的版本(因為 MIUI 是從 V6 開始才支援的,所以要進行判斷,然後該方法需要定期更新,當前的程式碼只是判斷到 V9 ),對外提供了直接獲取判斷結果的方法。

2、判斷 Flyme 的方法,這個就直接進行判斷了,沒有什麼特殊的版本,對外也提供了直接獲取判斷結果的方法。

3、接著是設定黑色模式狀態列的方法,其效果就是黑色模式下狀態列圖示和文字會變成灰黑色,關閉該模式則恢復成白色。該方法作用於支援狀態列圖示變黑的 MIUI 版本、Flyme、Android M起(沒有修改過原生API的系統基本都可以)的機子。相關官方 API 說明如下,Flyme 需要下載相關 SDK:

MIUI:

(我也不知道哪個是最新的...)

Flyme:

(SDK也在該連結內下載)

Android M 起則呼叫原生 API(也可以直接在 styles.xml <v23> 進行設定,對應的 item 是 android:windowLightStatusBar ,true 則是黑色模式)。

然後 Android M 起無論是 MIUI 還是 Flyme 均再呼叫原生高亮模式的 API(因為好像發現 MIUI 自己的 API 在 M 版本起無法成功調起,所以避免這種情況就再次呼叫了原生 API)。

4、獲取狀態列高度的方法。該方法可以獲取系統預設的狀態列高度(MIUI 的 狀態列會細一點),注意一點,如果你發現呼叫 setHeight() 無法正常設定對的高度的話請調整為通過 setLayoutParams 方式。

5、設定原生狀態列沉浸式屬性的方法。該方法在 Android 4.4 起可以設定/關閉 FLAG_TRANSLUCENT_STATUS 該屬性(該屬性也可以直接在 styles.xml 進行設定,對應的 item 是 android:windowTranslucentStatus),在 Android 4.4 以下版本會呼叫 Flyme 的 API(因為看了其原始碼,4.4 起是直接設定原生的沉浸式屬性,4.4 以下有它自己的處理)。

然後還要提及一點:在 Android M 起,如果關閉該屬性並在 styles.xml 設定了 colorPrimaryDark(狀態列顏色)可以實現跟 MIUI 和 Flyme 一樣效果的沉浸式;而 4.4 起開啟該屬性的話沉浸式就是會加上一層透明層的那種效果,另外開啟該屬性 styles.xml 裡面設定的 colorPrimaryDark 會無效(搞不懂為什麼)。

6、設定狀態列顏色的方法。該方法只作用於 Android 5.0 起的版本,對應於第 5 點中提及的 styles.xml 中的 colorPrimaryDark,相關注意請參考第 5 點。

7、下面推薦沉浸式狀態列的兩個庫:

兩個庫現在都在用,如果有這個需求的可以參考使用。

相關推薦

Android狀態相關判斷MIUIFlyme狀態圖示顏色切換獲取狀態高度沉浸狀態相關

對於狀態列相關適配這個事情,真是讓人頭疼的一個模組。因為負責的專案主題色偏偏是白色,不但要去適配 MIUI ,Flyme(因為這兩個都可以實現沉浸式,並且圖示可以切換成黑色),也要分別適配 Android 6.0 以下, Android 6.0 起兩種不同情況(6.0 起原

web強大的螢幕佈局rem響應 實現一套web程式碼多端自適應

強大的螢幕適配佈局rem響應式 實現一套web程式碼多端自適應適配 實現強大的螢幕適配佈局    流式的佈局、固定的寬度,還有響應式來做,但是這些方案都不是最佳的解決方法。->->r

前端庫HTML 移動端推薦

案例單擊我 上一篇文章我寫了,關於移動端適配問題,傳送門開,這一次也關於移動端適配問題,這個方式比較常用。我經常在公司得專案中使用 直接貼上適配程式碼: /* * @Author: a * @D

前端庫html 移動端meta方法

案例單擊我 js程式碼 ;(function(win, lib) { var doc = win.document; var docEl = doc.documentElement; var metaEl = doc.quer

DataBase64位作業系統32位PLSQL Developer

      除了Navicat,安裝Oracle過程中認識了一個新的視覺化工具,PL/SQL Developer,要知道PL/SQL Developer是沒有64位的,只適配32位作業系統的版本,我的電腦是64位怎麼辦?       下面介紹具體操作步驟:       前

Android_UI設計_登入頁面記住密碼

目標: Android記住密碼和自動登入介面的實現(SharedPreferences),具有一定的參考價值,感興趣的小夥伴們可以參考一下。 登入頁面設計(記住密碼版) (一) SharedPreferences用法 (二) 登入頁面Demo

android應用圖示Drawable轉Bitmap安卓8.0

相信大家在載入應用圖示的時候,經常會將得到的Drawable轉成Bitmap,程式碼如下: Drawable drawable = mPackageManager.getApplicationIcon(packageName); return ((BitmapDrawable) drawabl

Android沉浸狀態與EditText問題沉浸狀態實現及遇到的坑

Android4.4以前的版本,狀態列都是一塊黑色的,個人認為還是比較醜的。自4.4開始,Android已經支援透明狀態列了(俗稱沉浸式狀態列)。個人認為支援沉浸式狀態列的app逼格還是比較高的,為了緊跟潮流,我們專案中也準備加入沉浸式狀態列。在實現沉浸式狀態列的過程中踩了不

Android多語言:語言、名稱、與資源對應關係

語言碼_國家碼  ->  語言選擇裡的顯示語言  ->  英文下的語言(國家) -> 簡體中文下的語言(國家)  af ->     Afrikaans ->     Afrikaans ->     南非荷蘭文  af_N

Android狀態通知Notification、NotificationManager詳解

在Android系統中,發一個狀態列通知還是很方便的。下面我們就來看一下,怎麼傳送狀態列通知,狀態列通知又有哪些引數可以設定? 首先,傳送一個狀態列通知必須用到兩個類:  NotificationManager 、 Notification。 NotificationM

Androiddebug 狀態下其簽名檔案 debug.keystore 相關如何獲得該檔案其密碼獲取其sha1、MD5

Eclipse,Android studio 編譯執行 APP 的時候是生成一個 apk 的,它預設的簽名是 debug.keystore 。 有時候我們需要拿到這個簽名檔案,下面就來說說它的預設路徑: Windows下: C:\Users\<使用者名稱>\.A

Android用Studio完成多裝置多解析度的

最近研究了一下Android中的自動適配,加上Studio便捷的實時預覽功能,總結一下如何在Android Studio中快速方便適配多種螢幕。 先來補習一下基礎知識,來看幾個名詞解釋: 解析度:整個螢幕的畫素數目,為了表示方便一般用螢幕的畫素寬度(水平畫素數目)乘以畫

Android實戰沉浸狀態實現

傳統的手機狀態列是呈現出黑色條狀的,有的和手機主介面有很明顯的區別。這樣就在一定程度上犧牲了視覺寬度,介面面積變小。 沉浸模式的狀態列和主介面完全融為了一體,在設計上有不同的視覺感受。 我們先上兩張圖,很容易看出區別:        Android在4.4的時候增加了透

(解決)android不同版本的沉浸狀態4.4/5.0/5.1/6.0

介紹: 各個版本有略微的區別,下面我就根據自己的測試和除錯寫出對應的方法: 4.4以上(API>=19)的前提下,任選以下方法: 1.法1:只要呼叫一個方法,setContentView前: getWindow().addFlags(Win

web app變革之rem手機屏幕實現全

理想 那種 內嵌 自己的 大屏幕 block 行業 尺寸 是我 以往web移動適配,常規寫法是:media only screen @media only screen and (min-device-width: 320px){ //針對iP

取什麼主題好呢沉浸狀態----統一Title欄高度

新手上路 直接po程式碼(複製的時候記得加上setBar) 先是網上很好找到的“通過設定全屏,設定狀態列透明” /** * 通過設定全屏,設定狀態列透明 * @param activity */ public static void

Cordova 小米8留海螢幕之沉狀態大螢幕手機 從開始到結束 總結

 之前專案中打包的apk安裝到全面屏手機後,發現在應用下方出現了一大塊黑色區域(如:小米8),只有在系統中設定適配全面屏才能讓應用在全面屏手機中顯示正常,但是這種方式並不友好,而且有些手機廠商可能也沒有這種設定,所以還是需要我們再打包的時候就做一些相應的處理。 &l

android安卓的許可權提示及版本相關

Only dangerous permissions require user agreement. The way Android asks the user to grant dangerous permissions depends on the version of Android running o

Android 通知相關及總結

一、高版本適配之渠道        targetVersion為Android 8.0及以上的版本,需要建立通知的渠道(channel),否則就不會顯示通知。(注:渠道的建立不會影響低版本,低版本會忽略渠道) private fun createNotificationC

Android保持螢幕常亮喚醒狀態

在開發過程中有時會用到保持螢幕常亮,主要用電源控制來實現,具體實現如下: 第一步:  首先新增許可權: <uses-permission android:name="android.permiss