在Android系統(tǒng)4.4以前,狀態(tài)欄的背景色和字體顏色都是不能改變的盖溺。但是4.4以后Google增加了改變狀態(tài)欄背景透明的方法砌左,可以通過(guò)兩種方式來(lái)設(shè)置。
直接在Activity中設(shè)置Window屬性:
@Override
protected void onCreate(Bundle savedInstanceState) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
super.onCreate(savedInstanceState);
}
在xml的style文件中設(shè)置:
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowTranslucentStatus">true</item>
</style>
使用android:windowTranslucentStatus
屬性需要在res
目錄下新建values-v19
文件夾断箫,style
文件要放在里面。
盡量使用第一種方式類實(shí)現(xiàn)秋冰,據(jù)說(shuō)是第二種方式在某些國(guó)產(chǎn)手機(jī)上沒(méi)有效果仲义。
兩種方式都是讓狀態(tài)背景欄透明。但是在實(shí)際測(cè)試后發(fā)現(xiàn)剑勾,在android4.4系統(tǒng)上狀態(tài)欄的背景是漸變半透明埃撵。而在android5.0+的系統(tǒng)上又有所不同又有差異:
在Genymotion模擬器、vivo虽另、nexus6p的android5.0+系統(tǒng)上是半透明暂刘。
在xiaomi、oppo捂刺、huawei谣拣、leshi等5.0+系統(tǒng)是全透明的。
從效果圖也可以看出族展,上面兩種方式使得內(nèi)容區(qū)域延伸到狀態(tài)欄下面去了森缠。我們可以添加
android:fitsSystemWindows
來(lái)避免這樣的情況。在Activity的根布局文件中添加
android:fitsSystemWindows="true"
或者在主題style文件中添加
<item name="android:fitsSystemWindows">true</item>
上圖為了方便觀察狀態(tài)欄的背景顏色仪缸,就將Activity的布局文件的背景色設(shè)置成了紅色贵涵。可以看出Toolbar所在的內(nèi)容區(qū)域沒(méi)有沉浸到狀態(tài)欄下面了恰画,并且狀態(tài)欄的背景顏色還是之前一樣宾茂,只是將內(nèi)容區(qū)域向下偏移了狀態(tài)欄高度的距離。這是因?yàn)?code>fitsSystemWindows屬性使得布局的
paddingTop
被重新改寫(xiě)了(paddingTop
增加了狀態(tài)欄的高度)拴还。要實(shí)現(xiàn)沉浸式的狀態(tài)欄跨晴,其實(shí)就是狀態(tài)欄的背景顏色和Toolbar的顏色一樣。那么我們將Activity的背景色改為T(mén)oolbar紫色(內(nèi)容區(qū)域的背景顏色設(shè)置為白色)看下效果:
除了通過(guò)
fitsSystemWindows
這個(gè)屬性片林,我們可以自己給在Toolbar的上方添加一個(gè)和狀態(tài)欄高度一樣的Veiw坟奥,并將這個(gè)View的背景色設(shè)置成和Toolbar的背景色一樣。也可以直接給Toobar設(shè)置paddingTop
等于狀態(tài)欄高度值拇厢。為了方便爱谁,我一般是采用重寫(xiě)Toolbar,改寫(xiě)它的
paddingTop
值來(lái)實(shí)現(xiàn)孝偎。
/**
* Created by xiaoyanger on 2017/3/1.
* 沉浸式访敌、版本兼容的Toolbar,狀態(tài)欄透明.
*/
public class CompatToolbar extends Toolbar {
public CompatToolbar(Context context) {
this(context, null);
}
public CompatToolbar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CompatToolbar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setup();
}
public void setup() {
int compatPadingTop = 0;
// android 4.4以上將Toolbar添加狀態(tài)欄高度的上邊距衣盾,沉浸到狀態(tài)欄下方
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
compatPadingTop = getStatusBarHeight();
}
this.setPadding(getPaddingLeft(), getPaddingTop() + compatPadingTop, getPaddingRight(), getPaddingBottom());
}
public int getStatusBarHeight() {
int statusBarHeight = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
statusBarHeight = getResources().getDimensionPixelSize(resourceId);
}
Log.d("CompatToolbar", "狀態(tài)欄高度:" + px2dp(statusBarHeight) + "dp");
return statusBarHeight;
}
public float px2dp(float pxVal) {
final float scale = getContext().getResources().getDisplayMetrics().density;
return (pxVal / scale);
}
}
需要注意的是寺旺,網(wǎng)上有很多文章說(shuō)狀態(tài)欄的高度是25dp
爷抓,但實(shí)際測(cè)試后發(fā)現(xiàn)并不是所有的機(jī)型都是25dp
。使用getStatusBarHeight()
可以準(zhǔn)確的獲取狀態(tài)欄的高度值阻塑,看下獲取到高度的日志:
各個(gè)機(jī)型獲取到的狀態(tài)欄高度很多都不一樣蓝撇,所以最好還是重寫(xiě)Toolbar,動(dòng)態(tài)獲取到系統(tǒng)狀態(tài)欄的高度來(lái)改寫(xiě)它的上邊距陈莽。
國(guó)產(chǎn)的大部分手機(jī)在android5.0+的系統(tǒng)都將原生的半透明狀態(tài)欄改成了全透明渤昌,因此通過(guò)上面的方式基本達(dá)到了沉浸式體驗(yàn)。
其實(shí)在原生android5.0+系統(tǒng)上可以通過(guò)這個(gè)方法使?fàn)顟B(tài)欄背景色完全透明:
@Override
protected void onCreate(Bundle savedInstanceState) {
// 5.0以上系統(tǒng)狀態(tài)欄透明
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
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);
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
可以將上面兩個(gè)方法封裝到
BaseActivity
中走搁,以便在各個(gè)界面直接調(diào)用:
protected void setTranslucentStatus() {
// 5.0以上系統(tǒng)狀態(tài)欄透明
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
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);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
通過(guò)setTranslucentStatus()
方法独柑,4.4的系統(tǒng)狀態(tài)欄背景色漸變半透明,絕大多數(shù)5.0+的系統(tǒng)狀態(tài)欄背景色全透明(測(cè)試時(shí)發(fā)現(xiàn)有部分華為手機(jī)狀態(tài)欄背景是淺紫色私植,估計(jì)是定制系統(tǒng)給修改了)忌栅。
需要注意的是,不管是<item name="android:windowTranslucentStatus">true</item>
還是setTranslucentStatus()
方法曲稼,這兩種方式都會(huì)使這個(gè)設(shè)置<itemname="colorPrimaryDark">@color/colorPrimaryDark</item>
不會(huì)有任何效果索绪。
狀態(tài)欄的背景色按照上面的方式適配基本上滿足沉浸式體驗(yàn),但是有時(shí)候會(huì)遇到這樣的需求贫悄,狀態(tài)欄和Toolbar背景色都是白底黑字瑞驱,比如簡(jiǎn)書(shū)消息界面:
狀態(tài)欄的白色背景可以通過(guò)上面的方式來(lái)實(shí)現(xiàn),但是修改狀態(tài)欄的文字和圖標(biāo)顏色就比較麻煩了清女,原生的android系統(tǒng)只有在6.0+才有官方的api,但是在MIUI和FlymeUI提供了相應(yīng)的方法晰筛。
/**
* 設(shè)置Android狀態(tài)欄的字體顏色嫡丙,狀態(tài)欄為亮色的時(shí)候字體和圖標(biāo)是黑色,狀態(tài)欄為暗色的時(shí)候字體和圖標(biāo)為白色
*
* @param dark 狀態(tài)欄字體和圖標(biāo)是否為深色
*/
protected void setStatusBarFontDark(boolean dark) {
// 小米MIUI
try {
Window window = getWindow();
Class clazz = getWindow().getClass();
Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
int darkModeFlag = field.getInt(layoutParams);
Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
if (dark) { //狀態(tài)欄亮色且黑色字體
extraFlagField.invoke(window, darkModeFlag, darkModeFlag);
} else { //清除黑色字體
extraFlagField.invoke(window, 0, darkModeFlag);
}
} catch (Exception e) {
e.printStackTrace();
}
// 魅族FlymeUI
try {
Window window = getWindow();
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);
int bit = darkFlag.getInt(null);
int value = meizuFlags.getInt(lp);
if (dark) {
value |= bit;
} else {
value &= ~bit;
}
meizuFlags.setInt(lp, value);
window.setAttributes(lp);
} catch (Exception e) {
e.printStackTrace();
}
// android6.0+系統(tǒng)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (dark) {
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
}
}
狀態(tài)欄和Toobar白底黑字只能適配到android6.0+以及小米和魅族手機(jī)读第,其他手機(jī)的只能調(diào)整UI設(shè)計(jì)了曙博。可以看一下簡(jiǎn)書(shū)和掘金怜瞒,在小米父泳、魅族和android6.0+的系統(tǒng)上都是白底黑字,但是在其他的系統(tǒng)上一般是將狀態(tài)欄的背景色設(shè)置為淺灰色吴汪。
其實(shí)狀態(tài)欄顯示的顏色是灰色惠窄,但是有多種方式來(lái)實(shí)現(xiàn),參考下簡(jiǎn)書(shū)漾橙、掘金杆融、今日頭條,他們都使用了右滑返回上一界面霜运,在滑動(dòng)過(guò)程中就能看出狀態(tài)背景色的實(shí)現(xiàn)脾歇。
簡(jiǎn)書(shū)的效果可以不用上面的方式使內(nèi)容區(qū)域沉浸到狀態(tài)欄下面蒋腮,直接使用的
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
直接修改的狀態(tài)欄的顏色。
掘金的狀態(tài)欄是半透明的藕各,內(nèi)容區(qū)域已經(jīng)沉浸到狀態(tài)欄下方池摧,可以使用上面的方法,對(duì)于不是android6.0+激况、MIUI作彤、FlymeUI的系統(tǒng)通過(guò)window.setStatusbarColor
方法將狀態(tài)欄顏色改為半透明的灰色。
今日頭條的效果可以采用上面的方法實(shí)現(xiàn)沉浸式誉碴,狀態(tài)欄全透明宦棺,Toolbar沒(méi)有增加paddingTop
,而是在第二個(gè)界面中狀態(tài)欄的下方(Toolbar上方)加了一塊同狀態(tài)欄高度一樣的View黔帕,顏色為灰色代咸。
其實(shí),從上面三個(gè)app的狀態(tài)欄適配效果來(lái)看成黄,要達(dá)到真正的沉浸式呐芥,今日頭條的效果要好一些。但是今日頭條并沒(méi)有在各個(gè)機(jī)型上適配狀態(tài)欄的字體和圖標(biāo)顏色奋岁。
下面自己來(lái)實(shí)現(xiàn)不同機(jī)型的狀態(tài)欄背景色和字體圖標(biāo)顏色的適配思瘟。
首先,新建一個(gè)CompatStartusbarActivity
闻伶,繼承自BaseActivity
:
public class CompatStatusBarActivity extends BaseActivity {
private FrameLayout mFrameLayoutContent;
private View mViewStatusBarPlace;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_compat_status_bar);
mViewStatusBarPlace = findViewById(R.id.view_status_bar_place);
mFrameLayoutContent = (FrameLayout) findViewById(R.id.frame_layout_content_place);
ViewGroup.LayoutParams params = mViewStatusBarPlace.getLayoutParams();
params.height = getStatusBarHeight();
mViewStatusBarPlace.setLayoutParams(params);
}
@Override
public void setContentView(@LayoutRes int layoutResID) {
View contentView = LayoutInflater.from(this).inflate(layoutResID, null);
mFrameLayoutContent.addView(contentView);
}
/**
* 設(shè)置沉浸式狀態(tài)欄
*
* @param fontIconDark 狀態(tài)欄字體和圖標(biāo)顏色是否為深色
*/
protected void setImmersiveStatusBar(boolean fontIconDark, int statusBarColor) {
setTranslucentStatus();
if (fontIconDark) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|| OsUtil.isMIUI()
|| OsUtil.isFlyme()) {
setStatusBarFontIconDark(true);
} else {
if (statusBarColor == Color.WHITE) {
statusBarColor = 0xffcccccc;
}
}
}
setStatusBarPlaceColor(statusBarColor);
}
private void setStatusBarPlaceColor(int statusColor) {
if (mViewStatusBarPlace != null) {
mViewStatusBarPlace.setBackgroundColor(statusColor);
}
}
public int getStatusBarHeight() {
int statusBarHeight = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
statusBarHeight = getResources().getDimensionPixelSize(resourceId);
}
return statusBarHeight;
}
/**
* 設(shè)置狀態(tài)欄透明
*/
private void setTranslucentStatus() {
// 5.0以上系統(tǒng)狀態(tài)欄透明
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
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);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
/**
* 設(shè)置Android狀態(tài)欄的字體顏色滨攻,狀態(tài)欄為亮色的時(shí)候字體和圖標(biāo)是黑色,狀態(tài)欄為暗色的時(shí)候字體和圖標(biāo)為白色
*
* @param dark 狀態(tài)欄字體是否為深色
*/
private void setStatusBarFontIconDark(boolean dark) {
// 小米MIUI
try {
Window window = getWindow();
Class clazz = getWindow().getClass();
Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
int darkModeFlag = field.getInt(layoutParams);
Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
if (dark) { //狀態(tài)欄亮色且黑色字體
extraFlagField.invoke(window, darkModeFlag, darkModeFlag);
} else { //清除黑色字體
extraFlagField.invoke(window, 0, darkModeFlag);
}
} catch (Exception e) {
e.printStackTrace();
}
// 魅族FlymeUI
try {
Window window = getWindow();
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);
int bit = darkFlag.getInt(null);
int value = meizuFlags.getInt(lp);
if (dark) {
value |= bit;
} else {
value &= ~bit;
}
meizuFlags.setInt(lp, value);
window.setAttributes(lp);
} catch (Exception e) {
e.printStackTrace();
}
// android6.0+系統(tǒng)
// 這個(gè)設(shè)置和在xml的style文件中用這個(gè)<item name="android:windowLightStatusBar">true</item>屬性是一樣的
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (dark) {
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
}
}
}
布局文件activity_compat_status_bar.xml
:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.xiao.compatstatusbar.MainActivity">
<View
android:id="@+id/view_status_bar_place"
android:layout_width="match_parent"
android:layout_height="25dp"/>
<FrameLayout
android:id="@+id/frame_layout_content_place"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
setImmersiveStatusBar
方法中中判斷了不能修改狀態(tài)欄字體和圖標(biāo)顏色則將白色的狀態(tài)欄底色改成灰色蓝翰,能修改狀態(tài)欄字體和圖標(biāo)的顏色則改為給定的顏色statusBarColor
光绕。使用的時(shí)候直接調(diào)用這個(gè)方法即可。
下面是使用示例:
MainActivity
繼承自CompatStatusBarActivity
:
public class MainActivity extends CompatStatusBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_main);
int color = 0xffaa66cc;
toolbar.setBackgroundColor(color);
setImmersiveStatusBar(false, color);
}
public void go(View view) {
startActivity(new Intent(this, NextActivity.class));
}
}
布局文件activity_main.xml
:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.xiao.compatstatusbar.MainActivity">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar_main"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/colorPrimary">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="標(biāo)題"
android:textColor="@android:color/white"
android:textSize="18sp"/>
</LinearLayout>
</android.support.v7.widget.Toolbar>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#99FF4081"
android:onClick="go"
android:text="頁(yè)面1"
android:textSize="44sp"/>
</LinearLayout>
NextActivity
同樣繼承自CompatStatusBarActivity
:
public class NextActivity extends CompatStatusBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_next);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_next);
int color = 0xffffffff;
toolbar.setBackgroundColor(color);
setImmersiveStatusBar(true, color);
}
}
布局文件activity_next.xml
:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.xiao.compatstatusbar.MainActivity">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar_next"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/colorPrimary">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="標(biāo)題"
android:textColor="#353535"
android:textSize="18sp"/>
</LinearLayout>
</android.support.v7.widget.Toolbar>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#99FF4081"
android:text="頁(yè)面2"
android:textSize="44sp"/>
</LinearLayout>
最終適配效果:
源碼:https://github.com/xiaoyanger0825/CompatStatusBar