/? 概述? /
現(xiàn)在市面上絕大多數(shù)APP都實(shí)現(xiàn)了沉浸式狀態(tài)欄的效果糟描,該效果可以極大地提升用戶的使用體驗(yàn)蛙奖。但是奖亚,APP默認(rèn)的情況下只是設(shè)定一個固定的狀態(tài)欄顏色然眼,那么就需要我們通過代碼去動態(tài)修改狀態(tài)欄顏色的讳推。那么顶籽,如何去實(shí)現(xiàn)呢?
首先來說银觅,從Android 4.4開始才能實(shí)現(xiàn)沉浸式狀態(tài)欄的礼饱,所以如果您的APP也支持Android 4.4以下版本,那還需要對Android 4.4以下版本做“不支持沉浸式狀態(tài)欄”處理究驴。
那么镊绪,從Android 4.4開始,大概可以分成三個階段來實(shí)現(xiàn)沉浸式狀態(tài)欄:
Android4.4(API 19) - Android 5.0(API 21):這個階段的實(shí)現(xiàn)方式為:通過FLAG_TRANSLUCENT_STATUS設(shè)置狀態(tài)欄為透明并且為全屏模式洒忧,然后通過添加一個與StatusBar一樣大小的View估灿,將View的背景設(shè)置為要設(shè)置的顏色抠忘,從而實(shí)現(xiàn)沉浸式。
Android 5.0(API 21) - Android 6.0(API 23):從Android 5.0開始讼育,加入了一個重要的屬性android:statusBarColor和方法setStatusBarColor(),通過這個方法我們就可以輕松實(shí)現(xiàn)沉浸式狀態(tài)欄联四。但是在Android 6.0以下版本官方不支持設(shè)置狀態(tài)欄的文字和圖標(biāo)顏色,目前只有小米和魅族的ROM提供了支持。
Android 6.0(API 23)以上版本:其實(shí)Android 6.0以上的實(shí)現(xiàn)方式和Android 5.0+ 是一樣的剃诅,區(qū)別是從Android 6.0開始,官方支持改變狀態(tài)欄的文字和圖標(biāo)的顏色减余。
/? 實(shí)現(xiàn)方案? /
為了實(shí)現(xiàn)沉浸式狀態(tài)欄的效果综苔,我們需要做一些前期工作,如下:
修改應(yīng)用主題
為了更好地演示沉浸式狀態(tài)欄的效果位岔,我們修改應(yīng)用的主題如筛,使其不要顯示Android默認(rèn)的標(biāo)題欄。打開styles.xml文件抒抬,可以改成如下代碼杨刨,并且將colorPrimary等顏色設(shè)置刪掉:
新建一個StatusBarUtils.java文件,在里面添加一個獲取狀態(tài)欄高度的方法擦剑,以后我們都會用的到這個方法妖胀。之后我們的核心代碼也在這個工具類里實(shí)現(xiàn)。代碼如下:
publicclassStatusBarUtils{
publicstaticintgetHeight(Context context){
intstatusBarHeight =0;
try{
intresourceId = context.getResources().getIdentifier("status_bar_height","dimen",
"android");
if(resourceId >0) {
statusBarHeight = context.getResources().getDimensionPixelSize(resourceId);
}
}catch(Exception e) {
e.printStackTrace();
}
returnstatusBarHeight;
}
}
下面我們就按照從高版本到低版本的順序來講解如何更優(yōu)雅地實(shí)現(xiàn)沉浸式狀態(tài)欄惠勒。
/? Android 5.0+? /
在StatusBarUtils類里添加如下方法赚抡,實(shí)現(xiàn)設(shè)置狀態(tài)欄顏色的功能:
publicstaticvoidsetColor(@NonNull Window window, @ColorIntintcolor){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.setStatusBarColor(color);
}
}
修改狀態(tài)欄顏色的功能其實(shí)就是對Window進(jìn)行操作,而該Window可以是Activity或Dialog等持有的Window纠屋,所以我們就封裝了一個傳遞Window的方法涂臣。為了便于對Activity直接操作,可以再增加一個如下方法:
publicstaticvoidsetColor(Context context, @ColorIntintcolor){
if(contextinstanceofActivity) {
setColor(((Activity) context).getWindow(), color);
}
}
下面測試一下售担,新建一個Activity赁遗,將其布局背景設(shè)置為某個顏色,例如主題色族铆,布局代碼如下:
<?xml version="1.0" encoding="utf-8"?>
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/colorPrimaryDark">
在沒設(shè)置狀態(tài)欄顏色時岩四,展示效果如下:
接下來在Activity的onCreate()方法里調(diào)用設(shè)置狀態(tài)欄顏色的方法,代碼如下:
@Override
protectedvoidonCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_color);
StatusBarUtils.setColor(this, getResources().getColor(R.color.colorPrimaryDark));
}
展示效果如下:
這樣狀態(tài)欄和內(nèi)容的顏色就可以一樣啦哥攘!
下面我們把背景和狀態(tài)欄顏色改成純綠色(#00FF00)剖煌,看看效果:
發(fā)現(xiàn)顏色好亮啊,狀態(tài)欄里的文字都看不清了献丑。在這種情況下末捣,我們是可以將狀態(tài)欄文字的顏色改成深色的,官方也僅支持設(shè)置狀態(tài)欄文字和圖標(biāo)的深色模式和淺色模式创橄,但是官方僅在Android 6.0以上版本提供支持箩做。設(shè)置代碼如下:
privatestaticvoidsetTextDark(Window window,booleanisDark){
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
View decorView = window.getDecorView();
intsystemUiVisibility = decorView.getSystemUiVisibility();
if(isDark) {
decorView.setSystemUiVisibility(systemUiVisibility | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}else{
decorView.setSystemUiVisibility(systemUiVisibility & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
}
}
同樣再增加一個對Activity的支持:
publicstaticvoidsetTextDark(Context context,booleanisDark){
if(contextinstanceofActivity) {
setTextDark(((Activity) context).getWindow(), isDark);
}
}
為了能夠根據(jù)狀態(tài)欄背景顏色的深淺而自動設(shè)置文字的顏色,我們再新增一個判斷顏色深淺的方法:
publicstaticbooleanisDarkColor(@ColorIntintcolor){
returnColorUtils.calculateLuminance(color) <0.5;
}
然后在setColor()方法里新增一行設(shè)置狀態(tài)欄文字顏色的代碼妥畏,如下:
publicstaticvoidsetColor(@NonNull Window window, @ColorIntintcolor){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.setStatusBarColor(color);
setTextDark(window, !isDarkColor(color));
}
}
運(yùn)行一下邦邦,就可以看到效果了:
上面是Android 6.0以上版本的實(shí)現(xiàn)安吁,那么對于Android 6.0以下的手機(jī)怎么辦呢?目前Android 5.0-6.0的手機(jī)只有小米MIUI和魅族Flyme系統(tǒng)提供了支持燃辖。小米MIUI的設(shè)置方法如下:
privatestaticvoidsetMIUIDark(Window window,booleanisDark){
try{
Class clazz = window.getClass();
intdarkModeFlag;
Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
darkModeFlag = field.getInt(layoutParams);
Method extraFlagField = clazz.getMethod("setExtraFlags",int.class,int.class);
extraFlagField.invoke(window, isDark ? darkModeFlag :0, darkModeFlag);
}catch(Exception e) {
e.printStackTrace();
}
}
魅族Flyme的設(shè)置方法如下:
privatestaticvoidsetFlymeDark(Window window,booleanisDark){
if(window !=null) {
try{
WindowManager.LayoutParams lp = window.getAttributes();
Field darkFlag = WindowManager.LayoutParams.class
.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
Field meizuFlags = WindowManager.LayoutParams.class
.getDeclaredField("meizuFlags");
darkFlag.setAccessible(true);
meizuFlags.setAccessible(true);
intbit = darkFlag.getInt(null);
intvalue = meizuFlags.getInt(lp);
if(isDark) {
value |= bit;
}else{
value &= ~bit;
}
meizuFlags.setInt(lp, value);
window.setAttributes(lp);
}catch(Exception e) {
e.printStackTrace();
}
}
}
然后在setTextDark()方法中添加如下代碼:
privatestaticvoidsetTextDark(Window window,booleanisDark){
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
View decorView = window.getDecorView();
intsystemUiVisibility = decorView.getSystemUiVisibility();
if(isDark) {
decorView.setSystemUiVisibility(systemUiVisibility | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}else{
decorView.setSystemUiVisibility(systemUiVisibility & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
}elseif(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
switch(OSUtils.getRomType()) {
caseMIUI:
setMIUIDark(window, isDark);
break;
caseFlyme:
setFlymeDark(window, isDark);
break;
default:
}
}
}
Android 6.0以下運(yùn)行效果同上鬼店,我就不給大家截圖了。
當(dāng)我們APP的背景是一張圖片時黔龟,未設(shè)置沉浸式狀態(tài)欄的效果如下:
這時一般我們需要將圖片頂?shù)綘顟B(tài)欄里妇智,也就是整個內(nèi)容布局頂?shù)綘顟B(tài)欄里,并設(shè)置狀態(tài)欄的顏色透明氏身,才能實(shí)現(xiàn)沉浸式狀態(tài)欄的效果巍棱。
那么,在我們的StatusBarUtils類里添加如下代碼:
publicstaticvoidsetTransparent(@NonNull Window window){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
window.setStatusBarColor(Color.TRANSPARENT);
}
}
同樣針對Activity蛋欣,增加如下方法:
publicstaticvoidsetTransparent(Context context){
if(contextinstanceofActivity) {
setTransparent(((Activity) context).getWindow());
}
}
最后在Activity的onCreate()方法里設(shè)置一下:
@Override
protectedvoidonCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_transparent);
StatusBarUtils.setTransparent(this);
}
運(yùn)行航徙,顯示效果如下:
針對這種情況,大家在實(shí)現(xiàn)布局時不要忘了把狀態(tài)欄的高度也考慮進(jìn)去陷虎。以上就是Android 5.0以上沉浸式狀態(tài)欄的實(shí)現(xiàn)了到踏。
/? Android 4.4+? /
下面針對Android 4.4-5.0的手機(jī)進(jìn)行實(shí)現(xiàn)。實(shí)現(xiàn)原理是將內(nèi)容布局設(shè)為全屏尚猿,然后在布局的頂部添加一個和狀態(tài)欄一樣高度的View窝稿,將該View的背景設(shè)置成我們想要的顏色。當(dāng)需要將狀態(tài)欄設(shè)置純顏色時凿掂,為了和Android 5.0以上版本保持一致讹躯,我們對內(nèi)容布局的上邊設(shè)置一個padding,大小為狀態(tài)欄的高度缠劝。
為了能復(fù)用這個View,我們新增一個自定義的ID骗灶,在values文件夾下新建ids.xml文件惨恭,新增代碼如下:
<?xml version="1.0" encoding="utf-8"?>
然后在StatusBarUtils類里添加如下代碼:
privatestaticfinalintFAKE_STATUS_BAR_VIEW_ID = R.id.fake_status_bar_view;
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
publicstaticvoidsetColor(@NonNull Window window, @ColorIntintcolor,
booleanisTransparent){
Context context = window.getContext();
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
ViewGroup decorView = (ViewGroup) window.getDecorView();
View contentView = decorView.findViewById(android.R.id.content);
if(contentView !=null) {
contentView.setPadding(0, isTransparent ?0: getHeight(context),0,0);
}
View fakeStatusBarView = decorView.findViewById(FAKE_STATUS_BAR_VIEW_ID);
if(fakeStatusBarView !=null) {
fakeStatusBarView.setBackgroundColor(color);
if(fakeStatusBarView.getVisibility() == View.GONE) {
fakeStatusBarView.setVisibility(View.VISIBLE);
}
}else{
// 繪制一個和狀態(tài)欄一樣高的矩形
View statusBarView =newView(context);
FrameLayout.LayoutParams layoutParams =
newFrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
getHeight(context));
statusBarView.setLayoutParams(layoutParams);
statusBarView.setBackgroundColor(color);
statusBarView.setId(FAKE_STATUS_BAR_VIEW_ID);
decorView.addView(statusBarView);
}
}
設(shè)置狀態(tài)欄顏色
在設(shè)置純顏色時,我們還需要將該顏色與黑色進(jìn)行1:1的混合耙旦。為什么要這么設(shè)置呢脱羡?因?yàn)闋顟B(tài)欄的文字和圖標(biāo)顏色默認(rèn)是白色的,并且在Android 5.0以下是不能修改的免都,所以如果修改成較淺的顏色锉罐,就會導(dǎo)致狀態(tài)欄文字看不清的現(xiàn)象,因此做一個比較暗的浮層效果更好一些绕娘。
那么將setColor()方法改成如下代碼:
publicstaticvoidsetColor(@NonNull Window window, @ColorIntintcolor){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.setStatusBarColor(color);
setTextDark(window, !isDarkColor(color));
}elseif(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
setColor(window, ColorUtils.blendARGB(Color.TRANSPARENT, color,0.5f),false);
}
}
展示效果如下:
在設(shè)置狀態(tài)欄透明時脓规,為了也能清楚地看清狀態(tài)欄的文字,我們直接設(shè)置狀態(tài)欄的顏色為50%透明度的黑色险领。
于是侨舆,修改setTransparent()方法如下:
publicstaticvoidsetTransparent(@NonNull Window window){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
window.setStatusBarColor(Color.TRANSPARENT);
}elseif(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
setColor(window,0x80000000,true);
}
}
展示效果如下:
這樣秒紧,Android 4.4以上的手機(jī)就適配完畢了。
/? 特殊場景? /
現(xiàn)在來針對一些特殊場景進(jìn)行適配挨下,比如啟動頁熔恢、劉海屏和彈窗等等。
對于啟動頁臭笆,一般都會把狀態(tài)欄收上去叙淌,這需要適配劉海屏,否則劉海區(qū)域會顯示黑的一片愁铺。其實(shí)Android P以上提供了適配劉海屏的方法鹰霍,在啟動頁Activity添加如下代碼:
@Override
protectedvoidonCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
StatusBarUtils.setTransparent(this);
// 適配劉海屏
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
layoutParams.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
getWindow().setAttributes(layoutParams);
}
}
但是一些手機(jī)廠商的劉海屏手機(jī)系統(tǒng)版本是低于Android P的,不過也都提供了適配的方法帜讲。適配方式是在AndroidManifest.xml文件里的application標(biāo)簽下添加如下代碼:
<!-- 允許繪制到小米劉海屏機(jī)型的劉海區(qū)域 -->
android:name="notch.config"
android:value="portrait"/>
<!-- 允許繪制到華為劉海屏機(jī)型的劉海區(qū)域 -->
android:name="android.notch_support"
android:value="true"/>
<!-- 允許繪制到oppo衅谷、vivo劉海屏機(jī)型的劉海區(qū)域 -->
android:name="android.max_aspect"
android:value="2.2"/>
另外,對于Android 5.0以下的手機(jī)似将,適配完劉海屏后會在頂部多一塊黑色半透明的View获黔,那我們將其改成全透明的,修改onCreate()方法如下:
@Override
protectedvoidonCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
StatusBarUtils.setTransparent(this);
// 適配劉海屏
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
layoutParams.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
getWindow().setAttributes(layoutParams);
}
// 適配Android 4.4
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
StatusBarUtils.setColor(getWindow(), Color.TRANSPARENT,true);
}
}
這樣在验,啟動頁的效果就適配好了玷氏,效果如下:
彈窗
對于彈窗,只有全屏彈窗才能修改狀態(tài)欄文字的顏色腋舌。詳見:
https://jimmysun.blog.csdn.net/article/details/88892557