Foreword
首先推廣下AndroidUtilCode,相信很多老司機們都見識過了,昨天完結了StatusBar的功能,釋出了最新1.8.0版本,StatusBar相關原始碼存在於BarUtils,推薦insight.io外掛來檢視原始碼更絲滑哦,想知根知底的話那就閱讀原始碼吧,後面我也會相應介紹。
該類是借鑑於StatusBarUtil,通過我對其原始碼的進一步分析,我想到了更為完美地來解決狀態列的疑難雜症,也就是所謂地站在巨人肩膀上,但其中間歷經的坎坷...欲語淚先流啊,好了,下面就讓我去前面探探路吧。
Functions
該工具類支援安卓SDK19及以上,我所設計的設定狀態列主要包括兩類,其一是設定狀態列顏色,其二是設定狀態列透明度,這兩大類在應用中主要包含六點。
- 設定狀態列顏色
- 設定佈局背景的狀態列透明度
- 設定頂部View的狀態列透明度
- 設定ViewPager中Fragment的狀態列
- 設定滑動返回的狀態列
- 設定DrawLayout的狀態列
下面我依據這六點來演示,效果圖的話附上API19和API25的。
設定狀態列顏色
啥也不說了,先上效果圖。
這是相關的Demo類,可以看到,我在其中只呼叫瞭如下兩行程式碼即可實現其效果。
private void updateStatusBar() {
BarUtils.setStatusBarColor(this, mColor, mAlpha);
BarUtils.addMarginTopEqualStatusBarHeight(mTvStatusAlpha);// 其實這個只需要呼叫一次即可
}複製程式碼
具體效果函式名已經表達得已經很清楚了,後面我會對其原理分析一遍,這裡只看相關效果及主要程式碼。
設定佈局背景的狀態列透明度
這是相關的Demo類,其主要程式碼如下所示,還是兩個函式解決。
private void updateStatusBar() {
BarUtils.setStatusBarAlpha(BarStatusAlphaActivity.this, mAlpha);
BarUtils.addMarginTopEqualStatusBarHeight(mTvStatusAlpha);// 其實這個只需要呼叫一次即可
}複製程式碼
設定頂部View的狀態列透明度
相關Demo類,由於沒有View需要新增MargionTop,所以只需一行程式碼即可解決。
private void updateStatusBar() {
BarUtils.setStatusBarAlpha(BarStatusImageViewActivity.this, mAlpha, true);
}複製程式碼
設定ViewPager中Fragment的狀態列
這個比較特殊,因為ViewPager會預載入後面的Fragment,所以每一個Fragment都需要持有自己的StatusBar,這裡我們設定假狀態列即可,根據我後面的分析,你會發現我實現的狀態列都是假的,我們在每個需要狀態列的Fragment中新增如下View到頂層最上方即可。
<View
android:id="@+id/fake_status_bar"
android:layout_width="match_parent"
android:layout_height="0dp" />複製程式碼
三個Demo類如下所示,[ColorDemo類]insight.io/github.com/…)
相關核心程式碼如下所示,由於沒有需要偏移MarginTop的,一行即可解決。
public void updateFakeStatusBar() {
BarUtils.setStatusBarColor(fakeStatusBar, mColor, mAlpha);
}
public void updateFakeStatusBar() {
BarUtils.setStatusBarAlpha(fakeStatusBar, mAlpha);
}
public void updateFakeStatusBar() {
BarUtils.setStatusBarAlpha(fakeStatusBar, mAlpha);
}複製程式碼
設定滑動返回的狀態列
這是相關Demo類,由於頂部CheckBox需要偏移,其重點程式碼只需兩行即可。
private void updateStatusBar() {
if (cbAlpha.isChecked()) {
BarUtils.setStatusBarAlpha(this, mAlpha);
} else {
BarUtils.setStatusBarColor(this, mColor, mAlpha);
}
BarUtils.addMarginTopEqualStatusBarHeight(cbAlpha);// 其實這個只需要呼叫一次即可
}複製程式碼
由於滑動返回在API19中會出現桌面而不是之前的Activity介面,但這並不是我們需要關注的問題,我們需要關注的狀態列是否也在滑動即可,如果要求狀態列固定不變,不跟隨滑動的話,我們也可以做到,只需在呼叫函式後面加個引數true
即可,也就是如下所示。
private void updateStatusBar() {
if (cbAlpha.isChecked()) {
BarUtils.setStatusBarAlpha(this, mAlpha, true);
} else {
BarUtils.setStatusBarColor(this, mColor, mAlpha, true);
}
BarUtils.addMarginTopEqualStatusBarHeight(cbAlpha);// 其實這個只需要呼叫一次即可
}複製程式碼
最後一個引數就是繪製的狀態列是否在DecorView
中,false的話就是在ContentView
中,後面會具體講解,下面我們來看最後一種情況。
設定DrawLayout的狀態列
這是相關Demo類,由於需要頂部CheckBox新增MarginTop,所以兩行程式碼即可解決。
private void updateStatusBar() {
if (cbAlpha.isChecked()) {
BarUtils.setStatusBarAlpha4Drawer(BarStatusDrawerActivity.this, rootLayout, fakeStatusBar, mAlpha, cbFront.isChecked());
} else {
BarUtils.setStatusBarColor4Drawer(BarStatusDrawerActivity.this, rootLayout, fakeStatusBar, mColor, mAlpha, cbFront.isChecked());
}
BarUtils.addMarginTopEqualStatusBarHeight(cbAlpha);// 其實這個只需要呼叫一次即可
}複製程式碼
需要注意的是,DrawerLayout需要新增android:fitsSystemWindows="true"
這個屬性,另外就是和Fragment一樣,需要自己在頂層最上方新增假的狀態列。
好了,Demo已全部展示完畢,各位司機們應該都已經瞭解得差不多了,是不是如標題所說,每種效果只需兩個函式即可終結狀態列疑難雜症,如果還想要深入瞭解原始碼的話,那就繼續往下看吧,不需要了解的話那就跳過下面這節的講解,直接到最後面看我的結語吧。
How to achieve
首先我們看一張安卓UI架構圖,如下所示。
我設計的新增顏色狀態列或者透明狀態列分為兩種,一種是新增在DecorView
中,另一種是新增在ContentView
中,我在相應函式過載的最後一個引數boolean isDecor
,就是控制是否新增到DecorView
中,預設是false
,也就是新增在ContentView
中的,這也是滑動返回需要的效果,即狀態列可隨著滑動而偏移。
以上是其核心思想,之後的操作就是為以上服務,首先我們需要把狀態列設為透明狀態,這樣我們才可以自己繪製上我們自己的狀態列,也就是如下程式碼。
private static void transparentStatusBar(final Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;
Window window = activity.getWindow();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
int option = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
window.getDecorView().setSystemUiVisibility(option);
window.setStatusBarColor(Color.TRANSPARENT);
} else {
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}複製程式碼
這段程式碼即可實現透明狀態列,實現了這一步之後就是新增我們自己假狀態列了,首先是顏色狀態列,如下所示,
private static void addStatusBarColor(final Activity activity, final int color, final int alpha, boolean isDecor) {
ViewGroup parent = isDecor ?
(ViewGroup) activity.getWindow().getDecorView() :
(ViewGroup) activity.findViewById(android.R.id.content);
View fakeStatusBarView = parent.findViewWithTag(TAG_COLOR);
if (fakeStatusBarView != null) {
if (fakeStatusBarView.getVisibility() == View.GONE) {
fakeStatusBarView.setVisibility(View.VISIBLE);
}
fakeStatusBarView.setBackgroundColor(getStatusBarColor(color, alpha));
} else {
parent.addView(createColorStatusBarView(parent.getContext(), color, alpha));
}
}複製程式碼
程式碼很簡單,根據isDecor
決定新增到哪個佈局,後面就是新增View
的操作了。
有小夥伴對顏色狀態列的alpha肯定有疑問,說這alpha不對,並不是用來控制透明度的,的確,這個alpha並不是用來控制透明度的,這個alpha是材料設計中對狀態列陰影設定,預設效果值為112,下面是透明狀態列
private static void addStatusBarAlpha(final Activity activity, final int alpha, boolean isDecor) {
ViewGroup parent = isDecor ?
(ViewGroup) activity.getWindow().getDecorView() :
(ViewGroup) activity.findViewById(android.R.id.content);
View fakeStatusBarView = parent.findViewWithTag(TAG_ALPHA);
if (fakeStatusBarView != null) {
if (fakeStatusBarView.getVisibility() == View.GONE) {
fakeStatusBarView.setVisibility(View.VISIBLE);
}
fakeStatusBarView.setBackgroundColor(Color.argb(alpha, 0, 0, 0));
} else {
parent.addView(createAlphaStatusBarView(parent.getContext(), alpha));
}
}複製程式碼
和顏色狀態列很相似,我就不細說了,這個alpha,就是我們平時的透明度了。
由於我們實現的透明狀態列,這樣在佈局中會佔滿整個螢幕,所以我這裡提供了另外兩個函式,分別是addMarginTopEqualStatusBarHeight
、subtractMarginTopEqualStatusBarHeight
,也就是增加和減少View的MarginTop為狀態列高度,程式碼如下所示。
/**
* 為view增加MarginTop為狀態列高度
*
* @param view view
*/
public static void addMarginTopEqualStatusBarHeight(@NonNull View view) {
Object haveSetOffset = view.getTag(TAG_OFFSET);
if (haveSetOffset != null && (Boolean) haveSetOffset) return;
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
layoutParams.setMargins(layoutParams.leftMargin,
layoutParams.topMargin + getStatusBarHeight(),
layoutParams.rightMargin,
layoutParams.bottomMargin);
view.setTag(TAG_OFFSET, true);
}
/**
* 為view減少MarginTop為狀態列高度
*
* @param view view
*/
public static void subtractMarginTopEqualStatusBarHeight(@NonNull View view) {
Object haveSetOffset = view.getTag(TAG_OFFSET);
if (haveSetOffset == null || !(Boolean) haveSetOffset) return;
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
layoutParams.setMargins(layoutParams.leftMargin,
layoutParams.topMargin - getStatusBarHeight(),
layoutParams.rightMargin,
layoutParams.bottomMargin);
view.setTag(TAG_OFFSET, false);
}複製程式碼
這樣就基本滿足了我們大部分情況了,但人生總有意外,比如ViewPager的預載入,這樣我們就需要新的方式來適應這種情況,也就是我們自己新增假的狀態列到佈局檔案中,然後呼叫函式即可,這種方法其實就是以上兩種方法的擴充,以上兩種是我幫你們加了假的在DecorView
或者ContentView
中,這種就是老司機們自己加到佈局中,所以其本質都是一樣,所以出來的效果也是一樣的,也就完美相容了。
總有刁民想害朕,這不,DrawerLayout
就是那個異端,為他我單獨設計了兩個函式來針對它,在Demo中我也講解了其使用方式。設計過程中需要注意的是DrawerLayout
的直屬子View
需要設定setFitsSystemWindows(false);
,具體細節可以參看原始碼。
以上基本就是設計的核心所在,老司機們想要實現什麼樣的效果隨你們自己挑選。
具體StatusBar相關API如下所示。
getStatusBarHeight : 獲取狀態列高度(px)
addMarginTopEqualStatusBarHeight : 為view增加MarginTop為狀態列高度
subtractMarginTopEqualStatusBarHeight: 為view減少MarginTop為狀態列高度
setStatusBarColor : 設定狀態列顏色
setStatusBarAlpha : 設定狀態列透明度
setStatusBarColor4Drawer : 為DrawerLayout設定狀態列顏色
setStatusBarAlpha4Drawer : 為DrawerLayout設定狀態列透明度複製程式碼
Conclusion
柯基已經為你們探完路了,實現狀態列的設計沒少費我功夫,想實現什麼效果,最終只需兩個函式即可藥到病除,我們程式設計師不就是需要這種越簡單越好的東西麼,同意的話就快砸上你們的喜歡吧。
簡書第一時間發文:只需兩個函式終結狀態列疑難雜症
歡迎關注我的簡書:Blankj的簡書