【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】狀態列相關適配(判斷MIUI,Flyme,狀態列圖示顏色切換,獲取狀態列高度,沉浸式狀態列相關等)
對於狀態列相關適配這個事情,真是讓人頭疼的一個模組。因為負責的專案主題色偏偏是白色,不但要去適配 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
【DataBase】64位作業系統適配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
【Android】debug 狀態下其簽名檔案 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