很多時候我們會有改變狀態欄顏色或者用背景圖侵入狀態欄的這種需求,所謂侵入式佈局實際上就是將狀態欄背景色改爲透明然後將背景圖從狀態欄的頂部開始繪製,比如下圖
廢話不多說,我們從新建Activity開始一步一步來實現
第一步,添加主題,去掉默認的ActionBar
<activity android:name=".views.StatusActivity"
android:theme="@style/Theme.AppCompat.NoActionBar"
android:screenOrientation="portrait"
android:launchMode="singleTop"/>
第二步,添加侵入式佈局代碼
Lollipop以上的設備
window.apply {
clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
statusBarColor = Color.TRANSPARENT
}
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
window到底是什麼
當你打開一個StandardApp的時候,從上到下你會依次看到手機狀態欄(StatusBar)、導航欄(NavigationBar)、然後是Activity的實際頁面。它們每一個組件都有一個不同的Window用於它們自身的繪製。Activity的Window負責顯示我們設計的視圖,導航欄的Window負責繪製我們的菜單、返回鍵等等。狀態欄的Window負責繪製我們的時間、電池信息、通知圖標等等。所有這些Window都在同一個屏幕內被一個叫做WindowManager的東西管理着
window設置的那些Flag有什麼用
FLAG_TRANSLUCENT_STATUS:如果這個被設置,狀態欄會被設置爲透明,然而Activity還是按照原來的情況從狀態欄下方開始繪製,所以我們會看到一個灰色的狀態欄被顯示出來,這不是我們想要的,所以我們先移除了這個Flag。
FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS:如果這個被設置,系統會把Activity的Window用來繪製狀態欄,所以我們需要添加這個Flag。
SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN:這個Flag幫助我們在窗口進入\退出全屏模式而引起狀態欄可見\隱藏而導致的窗口Resizing的時候保持元素位置。
setStatusBarColor():這個無需多解釋,Android自帶Api改變狀態欄顏色,然而只能在Api21以上使用。
Api23以上的問題
在Api23以上的設備中狀態欄文字、圖標的默認顏色是白色,所以如果你的背景圖也是亮色的話會導致看不清楚的問題。
解決方法:
設置SYSTEM_UI_FLAG_LIGHT_STATUS_BAR:它會讓系統狀態欄的顏色、圖標適配亮色背景
適配頁面元素
現在我們終於可以在絕大多數Android設備上實現侵入式背景佈局了,然而現在還剩下一個問題:我們的頁面元素也跟着背景一起進入到了狀態欄裏。所以我們應該把頁面元素整體下移一個狀態欄的高度,那麼如何個下移呢?比如設一個MarginTop。那麼,在各種類型的Android設備裏,狀態欄的分佈和樣式是千奇百怪的,那麼如何獲取這個高度就變成了我們要探討的主要問題。
使用Window inset適配
Window insets 會給我們返回我們需要移動的距離,在這裏也就是指狀態欄高度。所以我們要移動的距離就是topWindowInset這個字段的值。
如何獲取當前的Window inset
ViewCompat.setOnApplyWindowInsetsListener(
findViewById(R.id.content_container)) { _, insets ->
Log.d("topInset", insets.systemWindowInsetTop)
insets.consumeSystemWindowInsets()
}
ViewCompat.setOnApplyWindowInsetsListener(container, new OnApplyWindowInsetsListener() {
@Override
public WindowInsetsCompat onApplyWindowInsets(View view, WindowInsetsCompat windowInsetsCompat) {
Log.d("topInset", windowInsetsCompat.getSystemWindowInsetTop());
return windowInsetsCompat.consumeSystemWindowInsets();
}
});
拿到TopInset的值之後,把頁面元素整體下移就可以了,下移的方法也貼出來
fun View.setMarginTop(marginTop: Int) {
val menuLayoutParams = this.layoutParams as ViewGroup.MarginLayoutParams
menuLayoutParams.setMargins(0, marginTop, 0, 0)
this.layoutParams = menuLayoutParams
}
public static void setMarginTop(View view, int marginTop){
ViewGroup.MarginLayoutParams menuLayoutParams =
(ViewGroup.MarginLayoutParams)view.getLayoutParams();
menuLayoutParams.setMargins(0, marginTop, 0, 0);
view.setLayoutParams(menuLayoutParams);
}
如此,一個侵入式的頁面佈局就適配完成了,我們把這些方法封裝起來以供日後使用
第三步,封裝及使用
/**
* Created by Buzz on 2019/10/21.
* Email :[email protected]
*/
object ScreenUtil {
/**
* 背景侵入式佈局,狀態欄透明
* @param container 頁面容器View
* @param content 整體下移的View
*/
fun AppCompatActivity.makeStatusBarTransparent(container:View, content:View) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
window.apply {
clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
} else {
decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
}
statusBarColor = Color.TRANSPARENT
}
ViewCompat.setOnApplyWindowInsetsListener(container){
_, insets ->
content.setMarginTop(insets.systemWindowInsetTop)
insets.consumeSystemWindowInsets()
}
}
}
/**
* 設定View的上邊距
* @param marginTop 上邊距的值
*/
private fun View.setMarginTop(marginTop: Int) {
val menuLayoutParams = this.layoutParams as ViewGroup.MarginLayoutParams
menuLayoutParams.setMargins(0, marginTop, 0, 0)
this.layoutParams = menuLayoutParams
}
}
/**
* Created by Buzz on 2019/10/21.
* Email :[email protected]
*/
class ScreenUtil {
static void makeStatusBarTransparent(AppCompatActivity activity, View container, final View content){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
} else {
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
}
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
}
ViewCompat.setOnApplyWindowInsetsListener(container, new OnApplyWindowInsetsListener() {
@Override
public WindowInsetsCompat onApplyWindowInsets(View view, WindowInsetsCompat windowInsetsCompat) {
setMarginTop(content, windowInsetsCompat.getSystemWindowInsetTop());
return windowInsetsCompat.consumeSystemWindowInsets();
}
});
}
private static void setMarginTop(View view, int marginTop){
ViewGroup.MarginLayoutParams menuLayoutParams =
(ViewGroup.MarginLayoutParams)view.getLayoutParams();
menuLayoutParams.setMargins(0, marginTop, 0, 0);
view.setLayoutParams(menuLayoutParams);
}
}
封裝完畢,在Activity裏使用只需要一行代碼即可
//rlContainer爲Activity的根佈局id,llContent爲需要下移的內容佈局的id
makeStatusBarTransparent(findViewById(R.id.rlContainer), findViewById(R.id.llContent))
//rlContainer爲Activity根佈局的id, llContent是需要下移的佈局的id
StatusBarUtil.makeStatusBarTransparent(this, findViewById(R.id.rlContainer), findViewById(R.id.llContent));