Android狀態列適配原始碼解析。
阿新 • • 發佈:2019-01-29
前言
Android狀態列透明,狀態列著色與沉浸是我們必須要會弄的東西。 這裡告訴你各個版本怎麼適配,為什麼需要分版本來適配!
怎麼使用
狀態列透明:
- 5.0以上:
if (Build.VERSION.SDK_INT >= 21) {
View decorView = getWindow().getDecorView();
int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
// View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN:
//Activity佈局全屏顯示,但狀態列不會被隱藏覆蓋,
//狀態列依然可見,Activity頂端佈局部分會被狀態遮住。
//SYSTEM_UI_FLAG_LAYOUT_STABLE :
//防止狀態列隱藏,保證你使用fitSystemWindows時候,系統UI邊界
//始終不會變,依然存在可見.即使你隱藏了所有,他依然存在,增加了UI穩定性,
decorView.setSystemUiVisibility(option);
getWindow().setStatusBarColor(Color.TRANSPARENT);//狀態列顏色設定為透明。
}
- 4.4到5.0之間
if (Build.VERSION.SDK_INT>=19){
WindowManager.LayoutParams myLayoutParams = getWindow().getAttributes();
myLayoutParams.flags =(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS|myLayoutParams.flags);
}
//這裡大家可能不懂 | 操作因為這裡的flags都是隻佔一位的1的(如000001,000010,000100,001000),其實這裡的位或操作就是將FLAG_TRANSLUCENT_STATUS的flags相加起來。詳情可看
狀態列上色:
- 5.0以上:
官方新加入的API
getWindow().setStatusBarColor(int Color);
//想要什麼顏色就用什麼顏色,一步到位
- 4.4到5.0之間:
這裡由於官方沒有給出設定狀態列的顏色API,所以我們需要使用先透明狀態列,然後fitSystemWindows==true,然後填充一個等高的純色View,來模擬狀態列已經上色了。(fitSystemWindows為true時候,系統UI會佔據填充屬於他自己的螢幕高度,false時候為使得整個Activity佔據螢幕)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
{
ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content);
View statusBarView = new View(activity);
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
getStatusBarHeight(activity));//獲得高和等寬的佈局屬性
statusBarView.setBackgroundColor(int color);//設定需要的顏色作為背景填充
contentView.addView(statusBarView, lp);
}
}
public static int getStatusBarHeight(Context context)
{
int result = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0)
{
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}//這裡參考hongyang的程式碼
- 狀態列顏色與頭佈局顏色相同適配(類似QQ)
設定狀態列為透明,讓頭佈局侵入狀態列高度,fitSystemWindows==false預設也為false,設定第一個佈局的paddingtop為狀態列高度即可實現這個效果。比如toolbar或者一些自定義的title,(padding為元素邊框與元素內容之間的空間距離)
可以看到這裡我們設定Textview為頭佈局,已經侵入狀態列,如果我們設定一個panddingtop就不是OK了!
參考
如果看到這一步:相信我們能夠完全適配各種需求對於我們的狀態列。但是為什麼要分這麼多版本來適配呢?這裡我分原始碼 !!15,19,22!!,3個版本來跟進原始碼檢視結果如下
- 各種版本都可以得到getwindow.getdecorview() 得到decorview,(decorview
為最底層介面) 觀察這裡View.java的最低版本15也支援setsystemUIvisiblity()。但是為什麼4.4才能支援透明呢?跟進
4.4以上
/** @param visibility Bitwise-or of flags {@link #SYSTEM_UI_FLAG_LOW_PROFILE},
* {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, {@link #SYSTEM_UI_FLAG_FULLSCREEN},
* {@link #SYSTEM_UI_FLAG_LAYOUT_STABLE}, {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION},
* {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}, {@link #SYSTEM_UI_FLAG_IMMERSIVE},
* and {@link #SYSTEM_UI_FLAG_IMMERSIVE_STICKY}.
*/
public void setSystemUiVisibility(int visibility) {
if (visibility != mSystemUiVisibility) {
mSystemUiVisibility = visibility;
if (mParent != null && mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
mParent.recomputeViewAttributes(this);
}
}
}
4.4以下
/**
* @param visibility Bitwise-or of flags {@link #SYSTEM_UI_FLAG_LOW_PROFILE} or
* {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}.
*/
public void setSystemUiVisibility(int visibility) {
if (visibility != mSystemUiVisibility) {
mSystemUiVisibility = visibility;
if (mParent != null && mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
mParent.recomputeViewAttributes(this);
}
}
}
從這裡就可知,這一些SystemUI_flag是4.4之後Android官方才做的適配flags;之前並沒有這麼多設定沉浸透明相關的系統flag。
- getwinodw().setStatuBarColor()
檢視3個版本的window實現的實現類phonewindow內部可得只有API大於21才有明確的API能夠設定狀態列顏色。低於5.0不能直接通過API設定顏色。
@Override
public void setStatusBarColor(int color) {
mStatusBarColor = color;
mForcedStatusBarColor = true; //用於判斷是否需要上色狀態列。
if (mDecor != null) { //mDecor也就是Decorview物件。
mDecor.updateColorViews(null, false /* animate */);
}
}
跟進方法
private WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
WindowManager.LayoutParams attrs = getAttributes();
//getAttributes()跟進發現就是 WindowManager.LayoutParams();
int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
//首先得到我們佈局屬性與系統預設的uivisibility,什麼不設定的話起初為getWindowSystemUiVisibility()為0。
// 這裡就算不是0其實也無所謂。因為systemUiVisibility這一類引數都是隻佔一位的比如 000001。000010。000100。....進行或操作只能是全部增加,!這裡其實也就是回到我們4.4版本設定UI透明可以用來設定引數的條件。
//。。。。。。。
//中間一段程式碼省略,是判斷高低端裝置對是否使用硬體影象加速處理什麼的,
//。。。。。。。。
updateColorViewInt(mStatusColorViewState, sysUiVisibility, mStatusBarColor,
mLastTopInset, animate && !disallowAnimate);
//最後傳入我們得到的sysUiVisibility 來更改狀態列顏色
//此方法從方法名可知道就是修改顏色的不深究.內部作一些判斷顏色是否透明,是否需要隱藏判斷,以及具體的顏色處理。
//
//這一個完整的過程也就完成了我們的狀態列修改完成了。
}
最後貼出幾個引數的意義
View.SYSTEM_UI_FLAG_VISIBLE :狀態列和Activity共存,Activity不全屏顯示。也就是應用平常的顯示畫面
View.SYSTEM_UI_FLAG_FULLSCREEN :Activity全屏顯示,且狀態列被覆蓋掉
.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN :Activity全屏顯示,但是狀態列不會被覆蓋掉,而是正常顯示,只是Activity頂端布 局會被覆蓋住
View.SYSTEM_UI_FLAG_LAYOUT_STABLE :
防止狀態列隱藏,保證你使用fitSystemWindows時候,系統UI邊界
始終不會變,依然存在可見.即使你隱藏了所有,他依然存在,增加了UI穩定性,迫使確認使用者瞭解如何讓UI控制元件回來。
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY:向內滑動的操作會讓系統欄臨時顯示,並處於半透明的狀態。此時沒有標籤會被清除,系統UI可見性監聽器也不會被觸發。如果使用者沒有進行操作,系統欄會在一段時間內自動隱藏。