自從開始用漸變背景之后,就發現狀態欄這個東西不能和圖片共存。。如果不能設成沉浸式的話會非常丑,所以自己找了一堆博客,把各種方法總結一下。
在介紹這個方便的輪子之前,我們先一起來回顧一下實現沉浸式狀態欄的一般套路。在Android上,關於對StatusBar(狀態欄)的操作,一直都在不斷改善,並且表現越來越好,在Android4.4 以下,我們可以對StatusBar和 NavigationBar進行顯示和隱藏操作。但是直到Android4.4,我們才能真正意義上的實現沉浸式狀態欄。從Android4.4 到現在(Android 7.1),關於沉浸式大概可以分成三個階段:
-
Android4.4(API 19) - Android 5.0(API 21): 這個階段可以實現沉浸式,但是表現得還不是很好,實現方式為: 通過
FLAG_TRANSLUCENT_STATUS
設置狀態欄為透明並且為全屏模式,然后通過添加一個與StatusBar 一樣大小的View,將View 的 background 設置為我們想要的顏色,從而來實現沉浸式。
-
Android 5.0(API 21)以上版本: 在Android 5.0的時候,加入了一個重要的屬性和方法
android:statusBarColor(對應方法為 setStatusBarColor),通過這個方法我們就可以輕松實現沉浸式。也就是說,從Android5.0開始,系統才真正的支持沉浸式。 -
Android 6.0(API 23)以上版本:其實Android6.0以上的實現方式和Android 5.0 +是一樣,為什么要將它歸為一個單獨重要的階段呢?是因為從Android 6.0(API 23)開始,我們可以改狀態欄的繪制模式,可以顯示白色或淺黑色的內容和圖標(除了魅族手機,魅族自家有做源碼更改,6.0以下就能實現)
大概就是這個三個階段,那么接下來我們就看一下這個三個階段分別是如何來實現的。
2.1 Android4.4(API 19) - Android 5.0(API 21)實現沉浸式的方式
Android 4.4 為什么能夠實現沉浸式的效果呢?因為在Android 4.4 新增了一個重要的屬性:FLAG_TRANSLUCENT_STATUS
/** * Window flag: request a translucent status bar with minimal system-provided * background protection. * * <p>This flag can be controlled in your theme through the * {@link android.R.attr#windowTranslucentStatus} attribute; this attribute * is automatically set for you in the standard translucent decor themes * such as * {@link android.R.style#Theme_Holo_NoActionBar_TranslucentDecor}, * {@link android.R.style#Theme_Holo_Light_NoActionBar_TranslucentDecor}, * {@link android.R.style#Theme_DeviceDefault_NoActionBar_TranslucentDecor}, and * {@link android.R.style#Theme_DeviceDefault_Light_NoActionBar_TranslucentDecor}.</p> * * <p>When this flag is enabled for a window, it automatically sets * the system UI visibility flags {@link View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and * {@link View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}.</p> */ public static final int FLAG_TRANSLUCENT_STATUS = 0x04000000;
SYSTEM_UI_FLAG_LAYOUT_STABLE和
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 。
有兩種方式實現這個屬性:
可以在代碼中設置,如下:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
當然也可以在theme 中設置屬性windowTranslucentStatus,如下:
android:windowTranslucentStatus
效果如上圖,可以看出,沉浸式的效果是出來了,但是也有一個問題,我們的標題欄和狀態欄重疊了,相當於整個布局上移了StatusBar 的高度,當然如果只是一張圖片的話就不用考慮這個問題。
為了讓標題欄回到原來的位置,我們在標題欄的上方添加一個大小和StatusBar大小一樣的View,View 的BackgroundColor 為標題欄一樣的顏色,這個View起到一個占位的作用。這個時候,標題欄就會下移StatusBar的高度,回到正常的位置。
添加如下代碼:
//獲取windowphone下的decorView ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); int count = decorView.getChildCount(); //判斷是否已經添加了statusBarView if (count > 0 && decorView.getChildAt(count - 1) instanceof StatusBarView) { decorView.getChildAt(count - 1).setBackgroundColor(calculateStatusColor(color, statusBarAlpha)); } else { //新建一個和狀態欄高寬的view StatusBarView statusView = createStatusBarView(activity, color, statusBarAlpha); decorView.addView(statusView); } ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0); //rootview不會為狀態欄留出狀態欄空間 ViewCompat.setFitsSystemWindows(rootView,true); rootView.setClipToPadding(true);
創建和status bar 一樣大小的View的代碼如下:
private static StatusBarView createStatusBarView(Activity activity, int color, int alpha) { // 繪制一個和狀態欄一樣高的矩形 StatusBarView statusBarView = new StatusBarView(activity); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity)); statusBarView.setLayoutParams(params); statusBarView.setBackgroundColor(calculateStatusColor(color, alpha)); return statusBarView; }
其中StatusBarView 就是一個普通的View。
小結:Android4.4上實現沉浸式狀態欄的套路是:為window添加
FLAG_TRANSLUCENT_STATUSFlag,然后添加一個和status bar 一樣大小的View 站位,從而讓讓標題欄不會與status bar 重疊。而圖片延伸到狀態欄只需要設置FLAG_TRANSLUCENT_STATUS就OK。
前面說過,沉浸式在Android4.4 - Android5.0 之間的版本表現得不是很好,從上面貼的幾張圖就可以看出,狀態欄的頂部有一個漸變,會顯示出黑色的陰影(底部的導航欄也是一樣的效果),在Android 5.0 版本已經被修復了。
2.2 Android 5.0(API 21)以上實現沉浸式的方式
Android 5.0 是一個里程碑式的版本,從Android 5.0開始,Google 推出了全新的設計規范 Material Design,並且原生控件就可以實現一些炫酷的UI動效。從這個版本開始,google 加入了一個比較重要的方法setStatusBarColor (對應屬性:android:statusBarColor),通過這個方法,可以很輕松地實現沉浸式狀態欄。方法如下:
/** * Sets the color of the status bar to {@code color}. * * For this to take effect, * the window must be drawing the system bar backgrounds with * {@link android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS} and * {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_STATUS} must not be set. * * If {@code color} is not opaque, consider setting * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and * {@link android.view.View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}. * <p> * The transitionName for the view background will be "android:status:background". * </p> */ public abstract void setStatusBarColor(@ColorInt int color);
注意看這個方法的注釋,想要這個方法生效,必須還要配合一個Flag一起使用,必須設置FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS ,並且不能設置FLAG_TRANSLUCENT_STATUS(Android 4.4才用這個)
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); //注意要清除 FLAG_TRANSLUCENT_STATUS flag getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); getWindow().setStatusBarColor(getResources().getColor(android.R.color.holo_red_light));
不過這個方法不能完全沉浸。。
圖片延伸到狀態欄
在Android 5.0 使圖片延伸到狀態欄,只需設置windowTranslucentStatus,將 statusBarColor 設置為透明即可:
<style name="ImageTranslucentTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<item name="android:windowTranslucentNavigation">true</item>
<item name="android:windowTranslucentStatus">true</item>
<!-- 設置statusBarColor 為透明-->
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
代碼中通過版本號的判斷兼容 Android5.0以下和Android 5.0以上:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); activity.getWindow().setStatusBarColor(calculateStatusColor(color, statusBarAlpha)); } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); int count = decorView.getChildCount(); if (count > 0 && decorView.getChildAt(count - 1) instanceof StatusBarView) { decorView.getChildAt(count - 1).setBackgroundColor(calculateStatusColor(color, statusBarAlpha)); } else { StatusBarView statusView = createStatusBarView(activity, color, statusBarAlpha); decorView.addView(statusView); } ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0); rootView.setFitsSystemWindows(true); rootView.setClipToPadding(true); setRootView(activity); }
下面這塊還沒有具體實踐過:
2.3 Android 6.0 + 實現狀態欄字色和圖標淺黑色
使用沉浸式的時候會遇到一個問題,那就是Android 系統狀態欄的字色和圖標顏色為白色,當我的主題色或者圖片接近白色或者為淺色的時候,狀態欄上的內容就看不清了。 ,這個問題在Android 6.0的時候得到了解決。Android 6.0 新添加了一個屬性SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDSflag ,同時清除了
FLAG_TRANSLUCENT_STATUSflag 才會生效。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); }
除了在代碼中添加以外,還可以直接在主題中使用屬性:
<style name="MDTheme" parent="Theme.Design.Light.NoActionBar">
<item name="android:windowTranslucentStatus">false</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/holo_red_light</item>
<!-- Android 6.0以上 狀態欄字色和圖標為淺黑色-->
<item name="android:windowLightStatusBar">true</item>
</style>
注意:主題要放在values-v23文件夾下:
三、輪子StatusBarUtil
通過上面的介紹,其實將各個版本實現沉浸式的方式和原理都講完了。但是或許當你真正去實踐沉浸式狀態欄的時候,你會感覺到無從下手,因此,我給大家推薦一個輪子StatusBarUtil,Github:https://github.com/laobie/StatusBarUtil。
為什么會推薦這個庫呢?因為這個庫就只有一個類StatusBarUtil,使用起來很方便,就像一個工具類一樣使用。里面封裝了很多靜態方法,直接使用就好。自己添加也很方便。
