- 針對(duì)狀態(tài)欄的操作,只針對(duì)4.4kitKat(含)以上的機(jī)型,部分國(guó)產(chǎn)rom會(huì)失效马僻,目前發(fā)現(xiàn)的有華為的EMUI
- Activity必須是
noActionbar
主題 - 本文基于StatusBarUtils略作修改莫其,感謝作者laobie
- 本文源碼地址
相關(guān)屬性重溫
- FitsSystemWindows
在使用FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
、FLAG_TRANSLUCENT_STATUS
擠占了狀態(tài)欄的高度的時(shí)候粗截,我們的布局文件也跟著頂?shù)搅藸顟B(tài)欄上惋耙。通過FitsSystemWindows
,系統(tǒng)會(huì)把a(bǔ)pp布局文件的paddingTop
修改成狀態(tài)欄的高度熊昌,達(dá)到適配的效果
android.R.id.content
通過它我們可以在不需要知道ID的情況下绽榛,訪問當(dāng)前Activity根節(jié)點(diǎn),一般是ContentFrameLayout
類型婿屹,通過((ViewGroup)findViewById(R.id.content)).getChildAt(0);
可以獲取到我們布局xml的根節(jié)點(diǎn)getDecorView()
通過他灭美,我們可以獲取到整個(gè)Window界面的最頂層View。我們用的很多的findViewById()
昂利,就是基于這個(gè)DecorView
查找里面的子節(jié)點(diǎn)的冲粤。比如Activity主題是Theme.AppCompat.Light.NoActionBar
的組織層級(jí)如下美莫,
為什么嵌套那么深,因?yàn)楦鱾€(gè)層次可能都會(huì)有幾個(gè)ViewStub
方便注入梯捕,只是我們現(xiàn)在的主題用不到就是空了厢呵。
下面開始實(shí)戰(zhàn)了,一般我們用的比較多的是透明狀態(tài)欄傀顾、設(shè)置狀態(tài)欄顏色襟铭、
DrawerLayout
蓋在狀態(tài)欄上三種。
1. 透明狀態(tài)欄
這種情況一般用于把圖片延伸到狀態(tài)欄上短曾,圖片還分兩種
- 作為
background
的全屏大圖 - 作為
ImageView
控件存在寒砖,一般只占屏幕高度的一部分
這兩種情況下的處理方式還不一樣,具體看下文
/** * 使?fàn)顟B(tài)欄透明 */
private static void transparentStatusBar(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
}
else {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
}
}
上面的代碼針對(duì)5.0以上或者4.4以上5.0以下的機(jī)型嫉拐,讓其狀態(tài)欄透明哩都、導(dǎo)航欄半透明,效果如下:
dump了下節(jié)點(diǎn)信息婉徘,我們發(fā)現(xiàn)布局的paddingTop從(50px狀態(tài)欄高度)被修改成了0px漠嵌。從而讓圖片延伸到了狀態(tài)欄
這種情況下,對(duì)app布局文件通過設(shè)置
FitsSystemWindows
就可以完成適配了
/**
* 獲取activity的根節(jié)點(diǎn)
*@param activity
*@return
*/
public static ViewGroup getAppRootView(Activity activity) {
return (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
}
/**
*將目標(biāo)View的paddingTop從0設(shè)置為狀態(tài)欄的高度
*/
private static void setRootViewFitSystemWindow(Activity activity) {
ViewGroup rootView = getAppRootView(activity);
rootView.setFitsSystemWindows(true);
rootView.setClipToPadding(true);
}
注意8呛簟儒鹿!要延伸到狀態(tài)欄的圖片,如果不是根節(jié)點(diǎn)的背景圖几晤,而是一個(gè)ImageView控件约炎,那么就不能設(shè)置
setFitsSystemWindows
了,原因參考上圖紅框蟹瘾。這種情況下圾浅,為了防止頂?shù)綘顟B(tài)欄,需要手動(dòng)在xml里通過paddingTop
憾朴、marginTop
調(diào)整標(biāo)題欄元素位置達(dá)到適配贱傀。類似下圖
2. 狀態(tài)欄顏色
設(shè)置狀態(tài)欄顏色的步驟和透明類似,差別在于5.0以下機(jī)型無(wú)法直接設(shè)置狀態(tài)欄顏色伊脓,我們可以模擬一個(gè)純色的View放到狀態(tài)欄下面間接達(dá)到著色效果府寒。放置的父層級(jí)我們選擇DecorView
如果發(fā)現(xiàn)狀態(tài)欄顏色設(shè)置無(wú)效,有可能是布局文件的root節(jié)點(diǎn)加了
backgroundColor
所致
/**
*
設(shè)置狀態(tài)欄顏色
*
* @param activity 需要設(shè)置的activity
* @param color 狀態(tài)欄顏色值
* @param statusBarAlpha 狀態(tài)欄透明度
*/
public static void setColor(Activity activity, int color, int statusBarAlpha) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
activity.getWindow().setStatusBarColor(UIUtils.calcColorWithAlpha(color, statusBarAlpha));
} else {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
insertMockStatusBackgroundView(decorView, color, statusBarAlpha);
}
setRootViewFitSystemWindow(activity);
}
上面的代碼判斷DecorView下第0個(gè)View是不是我們自定義的StatusbarView類型报腔,如果是則設(shè)置顏色株搔,否則創(chuàng)建一個(gè)純色控件插入到
DecorView
內(nèi),同時(shí)布局內(nèi)容上間距騰出給純色控件纯蛾。
3. DrawerLayout蓋在狀態(tài)欄上
DrawerLayout主要有兩個(gè)子元素1:內(nèi)容View 2:側(cè)滑欄
側(cè)滑欄必然要在內(nèi)容View后面以覆蓋前者
所以我們要做的有2步
- 插入純色控件到內(nèi)容View(如果不是LinarLayout需要考慮內(nèi)容View元素重新排列)
- DrawerLayout設(shè)置
fitSystemWindow=false
纤房;以覆蓋到狀態(tài)欄上
代碼如下:
/** * 為DrawerLayout 布局設(shè)置狀態(tài)欄變色
*
* @param activity 需要設(shè)置的activity
* @param drawerLayout DrawerLayout
* @param color 狀態(tài)欄顏色值
* @param statusBarAlpha 狀態(tài)欄透明度
*/public static void setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, int color, int statusBarAlpha) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { return; }
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(Color.TRANSPARENT);
} else {
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0);
//如果內(nèi)容view不是LinearLayout,則需要重新排列內(nèi)容view子元素的內(nèi)容
insertMockStatusBackgroundView(contentLayout, color, statusBarAlpha);
drawerLayout.setFitsSystemWindows(false);
}
總結(jié):
上面三個(gè)方法翻诉,涵蓋了app在大部分沉浸場(chǎng)景下的狀態(tài)炮姨。在了解了底層實(shí)現(xiàn)后捌刮,我們也可以輕松在安卓上實(shí)現(xiàn)IOS的沉浸效果了,最低版本4.4 kitkat現(xiàn)在已經(jīng)非常普及舒岸,隨著MIUI绅作、FlyME相繼都升級(jí)到5.0、6.0以上蛾派,魅藍(lán)俄认、紅米等大批國(guó)產(chǎn)中低端機(jī)型的都可以體驗(yàn)到這種酷炫的效果了。