1. 程式人生 > >終於搞懂令人迷惑的 StatusBar 了

終於搞懂令人迷惑的 StatusBar 了

隨著Android版本的迭代,開發者對狀態列等控制元件有了更多的控制, google 一直在嘗試引入新的Api來滿足開發者的需求,但Api卻一直不夠完美,介面添加了很多,卻都不夠簡單或者說完美,算上第三方廠商的特色行為,怎一個“亂”字了得。

1、效果
當前主流(2017)Android app StatusBar 效果有以下幾種:


簡單分個類:

Material Design 風格,狀態列顏色比 toolbar 顏色略深。google 全家桶主要採用這個方式。
與 DrawerLayout 結合使用,抽屜劃出後,狀態列新增一個半透明蒙層,抽屜位於蒙層和原狀態列之間。 google 全家桶採用。
與 toolbar 同色,大部分國內 app 使用。
大黑邊 國內少部分 app,5.0以下大部分app都是這個效果。
隱藏狀態列,導航欄,滑動時出現。歡迎,登入,全屏視訊播放等介面使用。
漸變效果,少部分app使用
狀態列字型預設為白色,部分 app 修改為黑色
圖片延伸到狀態列
2、相關API
與狀態列相關的 Api 主要有以下幾個:

2.1、遠古時期 API1
// 全屏佈局且隱藏狀態列:
//4.4 上,頂部下滑,出現半透明狀態列,過一會兒狀態列消失
//4.1 上,頂部下滑,沒反應
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
 
// 全屏佈局,不隱藏狀態列(可能已失效):
//實測 Support libaray 26.1.0 下(使用 support 庫中的主題和 AppCompatActivity),未見狀態列。
getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
2.2、Android3.0
在Android3.0中,View添加了一個重要的方法:View.setSystemUiVisibility(int),用於控制一些視窗裝飾元素的顯示,並添加了

View.STATUS_BAR_VISIBLE
View.STATUS_BAR_HIDDEN :實測statusbar不會消失,僅隱藏statusbar 部分內容
兩個Flag用於控制Status Bar的顯示與隱藏。

一般是這麼用的

 View decorView = getWindow().getDecorView();
 int uiOptions =  View.STATUS_BAR_VISIBLE;
 decorView.setSystemUiVisibility(uiOptions);
2.3、Android4.0
View.STATUS_BAR_VISIBLE 改為 View.SYSTEM_UI_FLAG_VISIBLE,
View.STATUS_BAR_HIDDEN 更名為 View.SYSTEM_UI_FLAG_LOW_PROFILE,該 flag 同時影響 StatusBar 和 NavigationBar ,但並不會使得 SystemUI 消失,而只會使得背景很淺,並且去掉 SystemUI 的一些圖示或文字。
添加了一個flag:SYSTEM_UI_FLAG_HIDE_NAVIGATION,會隱藏NavigationBar,但是由於NavigationBar是非常重要的,因此只要有使用者互動(例如點選一個 button),系統就會清除這個flag使NavigationBar就會再次出現。
2.4、Android4.1
View.SYSTEM_UI_FLAG_FULLSCREEN: 這個標誌與WindowManager.LayoutParams.FLAG_FULLSCREEN作用相同(全屏佈局且隱藏狀態列)
4.1 上頂部下滑沒反應
4.4 上頂部下滑從新出現狀態列,且擠壓 Activity 的佈局。
View.SYSTEM_UI_FLAG_LAYOUT_STABLE: 與其它flag配合使用,防止系統欄隱藏時內容區域發生變化。
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN: Activity全屏顯示,但狀態列不會被隱藏覆蓋,狀態列依然可見,Activity頂端佈局部分會被狀態遮住
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION: 使內容佈局到NavigationBar之下。
2.5、Android 4.4
View.SYSTEM_UI_FLAG_IMMERSIVE
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
View.SYSTEM_UI_FLAG_IMMERSIVE 和 SYSTEM_UI_FLAG_HIDE_NAVIGATION 搭配使用,使用 SYSTEM_UI_FLAG_HIDE_NAVIGATION 後,導航欄消失,當用戶互動時(例如點選一個 button),導航欄又會出現,新增 View.SYSTEM_UI_FLAG_IMMERSIVE 後,互動時導航欄不會出現,但是會底部滑動導航欄仍會出現。

View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY 和 View.SYSTEM_UI_FLAG_FULLSCREEN 配合使用:狀態列半透明,頂部向下滑動出現,過一段時間消失。

View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY 和 SYSTEM_UI_FLAG_HIDE_NAVIGATION 配合使用:導航欄半透明,互動時,導航欄不出現,底部向上滑動出現,過一段時間消失。

FLAG_TRANSLUCENT_STATUS

當使用這個flag時SYSTEM_UI_FLAG_LAYOUT_STABLE和SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN會被自動新增,同時,設定FLAG_TRANSLUCENT_STATUS也會影響到StatusBar的背景色,但並沒有固定的表現:

對於7.0以上的機型,設定此flage會使得StatusBar半透明
對於6.0以上的機型,設定此flage會使得StatusBar完全透明
對於5.x的機型,大部分是使背景色半透明,小米和魅族以及其它少數機型會全透明
對於4.4的機型,小米和魅族是透明色,而其它系統上就只是黑色到透明色的漸變。
google 你到底要鬧哪樣?

FLAG_TRANSLUCENT_NAVIGATION

當使用這這個個flag時SYSTEM_UI_FLAG_LAYOUT_STABLE和SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION會被自動新增。同時,設定FLAG_TRANSLUCENT_STATUS也會影響到 NavigationBar 的背景色,效果與 FLAG_TRANSLUCENT_STATUS 相同

2.6、Android 5.0
主題裡通過colorPrimaryDark來指定 StatusBar 背景色
可以呼叫 window.setStatusBarColor(@ColorInt int color) 來修改狀態列顏色,但是讓這個方法生效有一個前提條件:你必須給window新增FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS並且取消FLAG_TRANSLUCENT_STATUS
2.7、Android 6.0
在Android6以後,我們只要給SystemUI加上SYSTEM_UI_FLAG_LIGHT_STATUS_BAR這個flag,就可以讓字型和圖示變為黑色。

2.8、fitSystemWindows
在佈局內容延伸到狀態列和導航欄時,我們可以給 View 設定 fitSystemWindows 屬性,它是一個 boolean 值,可以在xml裡直接設定android:fitsSystemWindows="true",也可以通過View#setFitsSystemWindows(boolean fitSystemWindows)在java程式碼中設定。設定後,空調會調整自己的 padding 用於避開系統控制元件。

工作原理: Android系統元件例如狀態列、NavBar、鍵盤所佔據的空間稱為介面的WindowInsets,Android系統會在特定的時機從根View派發WindowInsets(深度優先)。一旦有一個View 的 fitSystemWindows 設定為 true,它就會根據WindowInsets來調整自己的 padding,並消耗WindowInsets,不在向下分發 WindowInsets,那麼WindowInsets的派發過程就結束了。

需要注意的是:

fitSystemWindows 設定為 true,會讓View原本的padding失效。
可以覆寫 View 的 onApplyWindowInsets(WindowInsets insets) 方法來自己處理 WindowInsets。
dispatchApplyWindowInsets 方法可以繼續傳遞 WindowInsets.
3、實現
3.1、Material Design 風格
採用這個效果的應用主要是 Material Design 設計風格的 App,表現形式為:

Android 5.0+,狀態列顏色略深與 toolbar,顏色值可以來這裡找
Android 5.0以下,狀態列變現為大黑邊
實現這樣的風格很簡單:

如果狀態列顏色不變,可以直接在主題(5.0 style)中定義:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <!-- 預設使用 colorPrimaryDark 做狀態列顏色 -->
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        
         <!-- 如果使用了下面兩個屬性,狀態列顏色變為android:statusBarColor,colorPrimaryDark 被覆蓋 -->
        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
        <item name="android:statusBarColor">@android:color/transparent</item>
    </style>
</resources>
需要注意的是:

<item name="android:windowDrawsSystemBarBackgrounds">true</item>

<item name="android:windowTranslucentStatus">true</item>
不能同時設定為 true,否則設定的顏色失效。

如果狀態列顏色在執行過程中需要變化:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
    window.setStatusBarColor(color); 
}
3.2、Toolbar 同色
效果和 Material Design 風格類似,設定方法和Material Design 風格相同。

3.3、側邊欄
效果:

5.0+:抽屜劃出後,狀態列新增一個半透明蒙層,抽屜位於蒙層和原狀態列之間。
5.0-:狀態列為大黑邊,不參與互動。
曾經,想做個 material design 風格的側邊欄效果,那是相當的複雜,可以看看這篇文章,現在(support 庫最新版本 26.1.0),給 DrawerLayout 設定 fitSystemWindows 為 true,系統就幫你把所有的事辦好了,當然包括了老版本的相容(google 大法好)。

3.4、漸變效果
效果:
5.0+:漸變
5.0-:大黑邊

實現:

狀態列設定為透明:
v21 的style

<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
或者

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
    getWindow().setStatusBarColor(getResources().getColor(android.R.color.transparent));
}
設定從狀態列開始佈局
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    View decorView = getWindow().getDecorView();
    int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
    decorView.setSystemUiVisibility(uiOptions);
 }
修改佈局
不同版本設定不同的佈局(程式碼,xml 均可)
5.0+:在佈局頂部加上有漸變效果背景的 View,漸變效果可以使用 shape 實現。
5.0-:正常佈局。

3.5、隱藏狀態列,導航欄,滑動時出現
歡迎,登入,全屏視訊播放等介面可能會使用這樣的效果。

常見的效果有:

視訊全屏播放:

4.4+:全屏佈局,狀態列,導航欄消失,一般互動時不出現,滑動頂部或底部時出現,過一段時間消失。
getWindow().getDecorView().setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
4.0+:這個版本就不要指望隱藏導航欄了(這個版本一互動,導航欄必然要出現),狀態列消失,頂部滑動導航欄不出現。
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
全屏歡迎頁面:

4.4+:全屏佈局,狀態列,導航欄消失,一般互動時不出現,滑動頂部或底部時出現且狀態列導航欄均為半透明,過一段時間消失。
getWindow().getDecorView().setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
4.0+:導航欄,狀態列消失。歡迎頁沒有互動,導航欄不會因為互動出現。
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
類似qq的登入頁面

4.4+:全屏佈局,狀態列消失,滑動頂部時出現,過一段時間消失。
getWindow().getDecorView().setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
4.0+:狀態列消失,頂部滑動導航欄不出現。
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
3.6、狀態列字型修改為黑色
一般這麼幹的都是將狀態列設計為淺色的。因為僅 6.0+ 和 小米 魅族支援修改狀態列顏色。5.0 版本會因為淺色狀態列看不清狀態列資訊。最好還是讓設計改設計稿吧!非要這麼做的話,就只有為 5.0 單獨設計了,適配 5.0-, 5.0+, 6.0+,這酸爽。

3.7、圖片延伸到狀態列
同漸變效果,僅最後一步將新增漸變 view 改為 新增 ImageView 即可。

注意
github 有一些很火熱的庫,使用了 FLAG_TRANSLUCENT_STATUS 特性,將狀態列適配到了 4.4,上文已指出 FLAG_TRANSLUCENT_STATUS 在不同平臺顯示效果存在差異,不能保證較為一致的視覺效果,所以狀態列要玩出花樣最合適的版本是 5.0+, 5.0- 統一大黑邊就可以了。
--------------------- 
作者:Red風信子 
來源:CSDN 
原文:https://blog.csdn.net/xiaozhude/article/details/79152149 
版權宣告:本文為博主原創文章,轉載請附上博文連結!