Android 系統狀態列沉浸式/透明化完整解決方案
前言
網上已經有很多有關於系統狀態列的解決方案,這篇文章也不會有什麼新奇的解決方案,都是本人經過自己試驗,統計提煉出來的相對靠譜的一套解決方案.
關於術語
網上有很多爭論:
你這狀態列是變色龍狀態列,不是沉浸式的
這應該是沉浸式的狀態列吧,系統欄與actionbar顏色設為一致
我只想說去你妹的,老子只要自己的app的狀態列能和主題顏色一致就行了,定義那麼多術語,讓我等小白情以何堪?
吐槽歸吐槽,但還是不得不去試著理解下這些術語怎麼來的,引用這裡的一段話:
- 沉浸式全屏模式
隱藏status bar(狀態列)使螢幕全屏,讓Activity接收所有的(整個螢幕的)觸控事件。 - 透明化系統狀態列
透明化系統狀態列,使得佈局侵入系統欄的後面,必須啟用fitsSystemWindows屬性來調整佈局才不至於被系統欄覆蓋。
因此,我就這樣理解了:
沉浸式不就是隱藏狀態列嘛,狀態列不見了?這不就是app全屏模式嘛?wtf?
而透明式式狀態列就是讓app的內容佈局可以擴充套件到系統狀態列?這裡有個問題就是為什麼能在系統狀態列還顯示的情況下,將內容佈局擴充套件到系統狀態列?恩,這應該很好理解,就是Z座標系的作用了,系統狀態列是覆蓋在內容佈局上面的,並且是透明的。
貌似這裡所謂透明化系統狀態列才是本菜想要的,不管了,現在開始一一試驗,至於這概念理解的對不對,管他呢?那到底應該叫什麼,那我就叫自適應狀態列
前提條件
讓系統狀態列顏色隨app主題顏色變化而變化這一設計,毫無疑問,也是向ios學習的:從android4.4開始引進的,並且在5.0進行了改進。因此,也只能將這一特性應用在android4.4以上的手機,無法做到全部適配。記得stormzhang(貌似是)曾說過:
作為一個android程式設計師,還能有什麼比做出ios風格的app更感到悲哀的呢?哎...
兩種情況下的解決方案:
- 使用toolbar
這種方案相對簡單,個人喜歡這種方案,本菜雖菜,但喜歡緊跟潮流。toolbar太好用了, - 不使用toolbar
1. 使用toolbar的解決方案
這個方法參照了這裡,薄荷app的toolbar適配方案
其基本原理就是:
theme裡新增style: <item name="android:windowTranslucentStatus"> true </item>後,包含toolbar的內容佈局就可以擴充套件至系統狀態列,狀態列會覆蓋在toolbar上,如果此時使用android:fitsSystemWindows="true",就可以調整內容佈局(估計也是在根佈局上加padding)恢復到原來位置.但是,上面的解決方案確是給toolbar加上一個padding-top="25dp",這樣就可以做到系統狀態列的顏色和toolbar的顏色保持一致.具體方案可以參照上面的薄荷app的方案連結.
簡述下步驟(只是簡述,有疑問請參照上面薄荷app的連結即可):
- 引入v7包,並在佈局裡新增toolbar
compile ‘com.android.support:appcompat-v7:22.2.1’
- 在程式碼中設定透明化:
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
-
WindowManager.LayoutParams localLayoutParams = getWindow().getAttributes();
-
local LayoutParams.flags = (WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | localLayoutParams.flags);
-
}
-
- 給toolbar加上padding-top,toolbar程式碼如下
-
<android.support.v7.widget.Toolbar android:id="@+id/toolbar"
-
android:layout_width="match_parent"
-
android:layout_height="wrap_content"
-
android:paddingTop="@dimen/toolbar_padding_top"
-
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
-
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
-
android:background="#30469b">
-
<TextView android:layout_gravity="center"
-
android:layout_width="wrap_content"
-
android:layout_height="wrap_content"
-
android:textSize="20sp"
-
android:text="@string/app_name"/>
-
</android.support.v7.widget.Toolbar>
-
這樣就可以達成了我們的目標,如果只是這樣也就罷了,按照上面做就可以了,關鍵是本菜是喜歡緊跟潮流的,使用MD風格的DrawerLayout+NavigationView時,在android4.4的手機下,就會變這樣了:
android4.4上的效果
很明顯,drawerlayout並沒用被擴充套件至系統狀態列,但在android5.0以上效果還是可以的,這讓我很奇怪,只能歸咎於5.0的優化了
android5.0上的效果
經過各種折騰終於想起來,可以把fitsSystemWindows的特性用在drawerlayout上試試,最後發現居然可以,最終將設定windowTranslucentStatus的程式碼調整如下:
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
-
WindowManager.LayoutParams localLayoutParams = getWindow().getAttributes();
-
local LayoutParams.flags = (WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | localLayoutParams.flags);
-
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP){
-
//將側邊欄頂部延伸至status bar
-
mDrawerLayout.setFitsSystemWindows(true);
-
//將主頁面頂部延伸至status bar;雖預設為false,但經測試,DrawerLayout需顯示設定
-
mDrawerLayout.setClipToPadding(false);
-
}
-
}
最終android4.4上也可以顯示正常:
android4.4上修正後的效果
2. 不使用toolbar的解決方案
不使用toolbar時,而是actionbar時,因為actionbar不好定製,所以無法採用上面那個方法,只能採用其它方法,這裡的方案主要參考這裡:Translucent System Bar 的最佳實踐
這篇簡書看的本菜暈乎乎的,仔細看下來,其實都是基於一個原理:
不管有沒有actionbar,內容佈局的背景顏色一律設為主題顏色,然後有actionbar的話,就將actionbar與內容佈局的背景顏色同時設為主題顏色,然後,每個內容佈局的根佈局都要設上fitsSystemWindows="true"進行調整,感覺超麻煩有沒有?
不說多少,簡述步驟:
- 在程式碼中設定透明化,步驟同上
- 設定內容佈局的根佈局的背景顏色為主題顏色,同時設定fitsSystemWindows="true"
-
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-
android:layout_width="match_parent"
-
android:layout_height="match_parent"
-
android:background="@color/colorPrimary"
-
android:fitsSystemWindows="true"
-
android:orientation="vertical">
-
- 在內容佈局的下面再設定一層內容佈局,設背景顏色為白色(或其它顏色):
-
<LinearLayout android:layout_width="match_parent"
-
android:layout_height="match_parent"
-
android:background="@color/c_light_white"
-
android:gravity="center"
-
android:orientation="vertical">
-
至此,這種方案也完成了,看下效果:
android4.4上的效果
可以看出,這種方案一般情況下,還是可行的,但是有三個問題:
- 如果用上drawerlayout+navigationview,actionbar就會覆蓋在側邊欄上(如上圖),暫時未找到解決方案,但是我想說你都用drawerlayout+navigationview了,為何不用toolbar,因此這個問題應該不是問題,況且還可以使用其它的側邊欄實現方式,各位道友可以試試
- 這種方案在每個根佈局上都要設fitsSystemWindows="true"進行調整,當然上面也有優化方案,可仍然覺得很麻煩,
- 每個根佈局裡都要多加的一層佈局來覆蓋根佈局的背景主題顏色
因此,這種方案的確不是上上之選.
總結
本文主要在考慮使用標題欄(actionbar/toolbar)的情況下,做出的方案,當然你也可以自定義標題欄,或者不使用標題欄;其實都可以基於上面一樣的道理:
在狀態列透明化的前提下,調整頂部view的padding-top,來達到狀態列自適應一體化的目的
網上還有其它蠻多的解決方案,如:
- 使用開源庫SystemBarTint,這個庫也挺不錯的,可以動態改變系統狀態列顏色,但是作者已經2年沒有維護了,現在技術更新迭代這麼快,鬼知道這個庫會不會出現什麼問題,因此可以放棄使用了
- 如果不怕麻煩,還可以new一個高度和狀態列一樣高的view,插入到內容佈局的上面,但是,想想都覺得麻煩,我也懶得試了
---------------------------------------------------------------
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
這個屬性4.4算是全透明(有的機子是過渡形式的透明),5.0就是半透明瞭 我的模擬器、真機都是半透明,
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){//4.4 全透明狀態列
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {//5.0 全透明實現
Window window = getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);//calculateStatusColor(Color.WHITE, (int) alphaValue)
}
以上程式碼基本解決適配各種版本全透明狀態列(如導航欄有需求可以再加導航欄)