伴隨著 Android 5.0 發(fā)布的 Material Design,讓 Android 應(yīng)用告別了以前的工程師審美,迎來(lái)了全新的界面,靈動(dòng)的交互,也讓越來(lái)越多的 App 開始遵從 material design 設(shè)計(jì)原則,不再是以前拿著iOS設(shè)計(jì)稿,做著Android開發(fā)。本文就其中的沉浸式狀態(tài)欄這一特性,描述其兼容到4.4的實(shí)現(xiàn),以及一些使用中的小細(xì)節(jié)。
前言 在4.4之前狀態(tài)欄一直是黑色的,在4.4中帶來(lái)了 windowTranslucentStatus 這一特性,因此可以實(shí)現(xiàn)給狀態(tài)欄設(shè)置顏色,如下圖所示,狀態(tài)欄顏色不再是黑色,而是可以定制的顏色。
國(guó)內(nèi)將狀態(tài)欄變色叫做沉浸式狀態(tài)欄,時(shí)間久了,叫的人多了,大家就不再深究,默認(rèn)了這種叫法。
Window 類中的setStatusBarColor(int color) 來(lái)實(shí)現(xiàn),這兩種方式在5.0上都比較簡(jiǎn)單,但是如何兼容到4.4呢?
以上就是本文要解決的問(wèn)題,下面給出解決方案。
思路是:
FitsSystemWindows 屬性為 true,此時(shí)根布局會(huì)延伸到狀態(tài)欄,處在狀態(tài)欄位置的就是之前添加的色塊,這樣就給狀態(tài)欄設(shè)置上顏色了。
代碼如下:
/**
* 設(shè)置狀態(tài)欄顏色
*
* @param activity 需要設(shè)置的activity
* @param color 狀態(tài)欄顏色值
*/ public static void setColor(Activity activity, int color) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // 設(shè)置狀態(tài)欄透明 activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); // 生成一個(gè)狀態(tài)欄大小的矩形 View statusView = createStatusView(activity, color); // 添加 statusView 到布局中 ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView(); decorView.addView(statusView); // 設(shè)置根布局的參數(shù) ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0); rootView.setFitsSystemWindows(true); rootView.setClipToPadding(true); } }
其中生成狀態(tài)欄一樣大小的矩形色塊的代碼如下:
/**
* 生成一個(gè)和狀態(tài)欄大小相同的矩形條
*
* @param activity 需要設(shè)置的activity
* @param color 狀態(tài)欄顏色值
* @return 狀態(tài)欄矩形條
*/ private static View createStatusView(Activity activity, int color) { // 獲得狀態(tài)欄高度 int resourceId = activity.getResources().getIdentifier("status_bar_height", "dimen", "android"); int statusBarHeight = activity.getResources().getDimensionPixelSize(resourceId); // 繪制一個(gè)和狀態(tài)欄一樣高的矩形 View statusView = new View(activity); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, statusBarHeight); statusView.setLayoutParams(params); statusView.setBackgroundColor(color); return statusView; }
在 setContentView() 之后調(diào)用 setColor(Activity activity, int color) 方法即可。
這個(gè)實(shí)現(xiàn)比較簡(jiǎn)單,根布局背景設(shè)置為圖片,然后添加狀態(tài)欄透明 Flag, 然后設(shè)置根布局的 FitsSystemWindows 屬性為true 即可。代碼如下:
/**
* 使?fàn)顟B(tài)欄透明
* <p>
* 適用于圖片作為背景的界面,此時(shí)需要圖片填充到狀態(tài)欄
*
* @param activity 需要設(shè)置的activity
*/ public static void setTranslucent(Activity activity) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // 設(shè)置狀態(tài)欄透明 activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); // 設(shè)置根布局的參數(shù) ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0); rootView.setFitsSystemWindows(true); rootView.setClipToPadding(true); } }
同樣的,在 setContentView() 之后調(diào)用 setTranslucent(Activity activity) 方法即可。
注意點(diǎn):
使用 DrawerLayout 時(shí),此時(shí)不能再對(duì)根布局,即 DrawerLayout 進(jìn)行設(shè)置,而要針對(duì) DrawerLayout 的內(nèi)容布局進(jìn)行設(shè)置,即抽屜之外的另一個(gè)布局。
如下是一個(gè)典型的 DrawerLayout 的布局,其內(nèi)容布局即 FrameLayout,我們需要對(duì) FrameLayout 進(jìn)行仿狀態(tài)欄色塊的添加、FitsSystemWindows 屬性的設(shè)置。
<?xml version="1.0" encoding="utf-8"?> <android.support.v4.widget.DrawerLayout android:id="@+id/drawer_layout" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent"> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:id="@+id/main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="@color/colorPrimary" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/> </LinearLayout> </FrameLayout> <android.support.design.widget.NavigationView android:id="@+id/navigation" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" app:headerLayout="@layout/nav_header" app:menu="@menu/activity_main_drawer"/> </android.support.v4.widget.DrawerLayout>
FitsSystemWindows 屬性為 false,即上面布局中的NavigationView。
解決方案
DrawerLayout 狀態(tài)欄變色
代碼如下:
/**
* 為DrawerLayout 布局設(shè)置狀態(tài)欄變色
*
* @param activity 需要設(shè)置的activity
* @param drawerLayout DrawerLayout
* @param color 狀態(tài)欄顏色值
*/ public static void setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, int color) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); // 生成一個(gè)狀態(tài)欄大小的矩形 View statusBarView = createStatusBarView(activity, color); // 添加 statusBarView 到布局中 ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0); contentLayout.addView(statusBarView, 0); // 內(nèi)容布局不是 LinearLayout 時(shí),設(shè)置padding top if (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) { contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0); } // 設(shè)置屬性 ViewGroup drawer = (ViewGroup) drawerLayout.getChildAt(1); drawerLayout.setFitsSystemWindows(false); contentLayout.setFitsSystemWindows(false); contentLayout.setClipToPadding(true); drawer.setFitsSystemWindows(false); } }
需要注意的是,DrawerLayout 的布局只能包含兩個(gè)直接子布局,一個(gè)是內(nèi)容布局,一個(gè)是抽屜布局,結(jié)構(gòu)如前面的示例布局所示,如果內(nèi)容布局的根布局如果不是 LinearLayout 需要對(duì)其子布局設(shè)置padding top值,否則仿狀態(tài)欄色塊會(huì)被遮擋在下面,布局內(nèi)容延伸到狀態(tài)欄,如下圖所示:
(ps:就上圖中的問(wèn)題,目前的解決方案感覺并不是很好,如果你有更好的解決方案,請(qǐng)告訴我~)
/**
* 為 DrawerLayout 布局設(shè)置狀態(tài)欄透明
*
* @param activity 需要設(shè)置的activity
* @param drawerLayout DrawerLayout
*/ public static void setTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // 設(shè)置狀態(tài)欄透明 activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); // 設(shè)置內(nèi)容布局屬性 ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0); contentLayout.setFitsSystemWindows(true); contentLayout.setClipToPadding(true); // 設(shè)置抽屜布局屬性 ViewGroup vg = (ViewGroup) drawerLayout.getChildAt(1); vg.setFitsSystemWindows(false); // 設(shè)置 DrawerLayout 屬性 drawerLayout.setFitsSystemWindows(false); } }
同樣的,在 setContentView() 之后調(diào)用上述解決方案中的方法即可。
以上代碼我整理成了一個(gè)工具類,放在 github 上:StatusBarUtils.java 文件
在項(xiàng)目中推薦這樣使用,在 BaseActivity 中重寫 setContentView(int layoutResID) 方法,新建一個(gè) setStatusBar()方法,全局設(shè)置狀態(tài)欄顏色,因?yàn)橐话?App 大部分界面狀態(tài)欄都是主題色。
public class BaseActivity extends AppCompatActivity { @Override public void setContentView(int layoutResID) { super.setContentView(layoutResID); setStatusBar(); } protected void setStatusBar() { StatusBarUtils.setColor(this, getResources().getColor(R.color.colorPrimary)); } }
當(dāng)子類 Activity 的狀態(tài)欄需要特殊處理時(shí),比如設(shè)置不同的顏色,或者設(shè)置圖片為背景時(shí),重寫父類的 setStatusBar() 方法即可,例如:
public class ImageStatusBarActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_image_status_bar); } @Override protected void setStatusBar() { StatusBarUtils.setTranslucent(this); }
對(duì) DrawerLayout 布局使用時(shí),需要注意一點(diǎn),因?yàn)榉椒ㄊ窃?nbsp;setContentView() 之后立即調(diào)用的,所以傳進(jìn)來(lái)的DrawerLayout 要通過(guò) findViewById() 傳進(jìn)來(lái)。如果傳入在 setContentView() 之后通過(guò) findViewById() 得到的DrawerLayout, 則會(huì)造成空指針異常。
StatusBarUtils.setColorForDrawerLayout(this, (DrawerLayout) findViewById(R.id.drawer_layout), getResources() .getColor(R.color.colorPrimary));
本站文章版權(quán)歸原作者及原出處所有 。內(nèi)容為作者個(gè)人觀點(diǎn), 并不代表本站贊同其觀點(diǎn)和對(duì)其真實(shí)性負(fù)責(zé),本站只提供參考并不構(gòu)成任何投資及應(yīng)用建議。本站是一個(gè)個(gè)人學(xué)習(xí)交流的平臺(tái),網(wǎng)站上部分文章為轉(zhuǎn)載,并不用于任何商業(yè)目的,我們已經(jīng)盡可能的對(duì)作者和來(lái)源進(jìn)行了通告,但是能力有限或疏忽,造成漏登,請(qǐng)及時(shí)聯(lián)系我們,我們將根據(jù)著作權(quán)人的要求,立即更正或者刪除有關(guān)內(nèi)容。本站擁有對(duì)此聲明的最終解釋權(quán)。