終於搞懂令人迷惑的 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 
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章