標籤:less 源碼 覆蓋 技術分享 fit pad function 部分 布局
- 針對狀態列的操作,只針對4.4kitKat(含)以上的機型,部分國產rom會失效,目前發現的有華為的EMUI
- Activity必須是
noActionbar主題
- 本文基於StatusBarUtils略作修改,感謝作者laobie
- 本文源碼地址
相關屬性重溫
- FitsSystemWindows
在使用FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS、FLAG_TRANSLUCENT_STATUS擠佔了狀態列的高度的時候,我們的布局檔案也跟著頂到了狀態列上。通過FitsSystemWindows,系統會把app布局檔案的paddingTop修改成狀態列的高度,達到適配的效果
false頂到狀態列
true,paddingTop有一個狀態列高度
android.R.id.content
通過它我們可以在不需要知道ID的情況下,訪問當前Activity根節點,一般是ContentFrameLayout類型,通過((ViewGroup)findViewById(R.id.content)).getChildAt(0);可以擷取到我們布局xml的根節點
getDecorView()
通過他,我們可以擷取到整個Window介面的最頂層View。我們用的很多的findViewById(),就是基於這個DecorView尋找裡面的子節點的。比如Activity主題是Theme.AppCompat.Light.NoActionBar的組織層級如下,
noActionbarView層次.png
為什麼嵌套那麼深,因為各個層次可能都會有幾個ViewStub方便注入,只是我們現在的主題用不到就是空了。
下面開始實戰了,一般我們用的比較多的是透明狀態列、設定狀態列顏色、DrawerLayout蓋在狀態列上三種。
1. 透明狀態列
這種情況一般用於把圖片延伸到狀態列上,圖片還分兩種
- 作為
background的全屏大圖
- 作為
ImageView控制項存在,一般只佔螢幕高度的一部分
這兩種情況下的處理方式還不一樣,具體看下文
/** * 使狀態列透明 */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); }}
上面的代碼針對5.0以上或者4.4以上5.0以下的機型,讓其狀態列透明、導覽列半透明,效果如下:
透明狀態列的結果是布局檔案頂到了狀態列上
dump了下節點資訊,我們發現布局的paddingTop從(50px狀態列高度)被修改成了0px。從而讓圖片延伸到了狀態列
不透明狀態列.png
透明狀態列.png
這種情況下,對app布局檔案通過設定FitsSystemWindows就可以完成適配了
/** * 擷取activity的根節點 *@param activity *@return */public static ViewGroup getAppRootView(Activity activity) { return (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);}/** *將目標View的paddingTop從0設定為狀態列的高度 */private static void setRootViewFitSystemWindow(Activity activity) { ViewGroup rootView = getAppRootView(activity); rootView.setFitsSystemWindows(true); rootView.setClipToPadding(true);}
app內容布局有了padding.png
注意!!要延伸到狀態列的圖片,如果不是根節點的背景圖,而是一個ImageView控制項,那麼就不能設定setFitsSystemWindows了,原因參考紅框。這種情況下,為了防止頂到狀態列,需要手動在xml裡通過paddingTop、marginTop調整標題列元素位置達到適配。類似
延伸到狀態列的圖片是一個ImageView2. 狀態列顏色
設定狀態列顏色的步驟和透明類似,差別在於5.0以下機型無法直接設定狀態列顏色,我們可以類比一個純色的View放到狀態列下面間接達到著色效果。放置的父層級我們選擇DecorView
如果發現狀態列顏色設定無效,有可能是布局檔案的root節點加了backgroundColor所致
/** * 設定狀態列顏色 * * @param activity 需要設定的activity * @param color 狀態列顏色值 * @param statusBarAlpha 狀態列透明度 */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個View是不是我們自訂的StatusbarView類型,如果是則設定顏色,否則建立一個純色控制項插入到DecorView內,同時布局內容上間距騰出給純色控制項。
3. DrawerLayout蓋在狀態列上
DrawerLayout主要有兩個子項目1:內容View 2:側滑欄
側滑欄必然要在內容View後面以覆蓋前者
側滑欄效果
所以我們要做的有2步
- 插入純色控制項到內容View(如果不是LinarLayout需要考慮內容View元素重新排列)
- DrawerLayout設定
fitSystemWindow=false;以覆蓋到狀態列上
代碼如下:
/** * 為DrawerLayout 布局設定狀態列變色 * * @param activity 需要設定的activity * @param drawerLayout DrawerLayout * @param color 狀態列顏色值 * @param statusBarAlpha 狀態列透明度 */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); //如果內容view不是LinearLayout,則需要重新排列內容view子項目的內容 insertMockStatusBackgroundView(contentLayout, color, statusBarAlpha); drawerLayout.setFitsSystemWindows(false);}
總結:
上面三個方法,涵蓋了app在大部分沉浸情境下的狀態。在瞭解了底層實現後,我們也可以輕鬆在安卓上實現IOS的沉浸效果了,最低版本4.4 kitkat現在已經非常普及,隨著MIUI、FlyME相繼都升級到5.0、6.0以上,魅藍、紅米等大批國產中低端機型的都可以體驗到這種酷炫的效果了。
android狀態列總結