寫在前面
- 如圖某音樂播放器,這個(gè)效果就是所謂的"沉浸式"效果捏题。
- Android與ios效果互仿早已不是什么稀奇的事玻褪,我猜大概這個(gè)效果來自ios吧,講真的公荧,這體驗(yàn)感覺真不錯(cuò)噢带射。
- 有爭議說這種效果不能叫做沉浸式,叫透明狀態(tài)欄更合適循狰,我也感覺這和沉浸式的含義不太一致庸诱。
- 但是大家都這么叫了,那就這樣唄晤揣。
原理
- 從4.4后系統(tǒng)增加了透明狀態(tài)欄的特性
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
一旦添加上這個(gè)屬性后,那么布局中的內(nèi)容DecorView
就會(huì)自動(dòng)填充到狀態(tài)欄朱灿。所有的實(shí)現(xiàn)都是基于這個(gè)特性昧识,就相當(dāng)于這個(gè)時(shí)候狀態(tài)欄會(huì)默認(rèn)空出來,然后開發(fā)者可以自定義view來填充這個(gè)高度的. - 實(shí)現(xiàn)的過程中可能還要用到
android:fitsSystemWindows="true"
,這個(gè)屬性很重要盗扒。
其含義:view可以根據(jù)系統(tǒng)窗口(如status bar跪楞,軟鍵盤)來調(diào)整自己的布局缀去,如果值為true,就會(huì)調(diào)整view的paingding屬性來給system windows留出空間....
那么現(xiàn)在來看看具體實(shí)現(xiàn)方式吧
實(shí)現(xiàn)
從實(shí)現(xiàn)效果上,這里大致分為兩種
單獨(dú)給狀態(tài)欄著色
-
使用這個(gè)開源庫SystemBarTint
/** * 狀態(tài)欄顏色設(shè)置方法 * @param context * @param color */ public static void smartTintManager(Activity context, int color){ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { Window window = context.getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); // 創(chuàng)建狀態(tài)欄的管理實(shí)例 SystemBarTintManager tintManager = new SystemBarTintManager(context); // 激活狀態(tài)欄設(shè)置 tintManager.setStatusBarTintEnabled(true); tintManager.setStatusBarTintColor(color); } }
- 在對應(yīng)的頁面的根布局中添加
android:fitsSystemWindows="true"
,且根布局中不能設(shè)置整體的大背景色甸祭,否則狀態(tài)欄著色就會(huì)被覆蓋 - 調(diào)用上面方法設(shè)置具體的顏色(依據(jù)開源庫缕碎,其中就一個(gè)核心類,可以直接把那個(gè)類拷貝到項(xiàng)目中)
- 在對應(yīng)的頁面的根布局中添加
-
這里主要講一下具體的實(shí)現(xiàn)原理
private void setupStatusBarView(Context context, ViewGroup decorViewGroup) { mStatusBarTintView = new View(context); LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, mConfig.getStatusBarHeight()); params.gravity = Gravity.TOP; if (mNavBarAvailable && !mConfig.isNavigationAtBottom()) { params.rightMargin = mConfig.getNavigationBarWidth(); } mStatusBarTintView.setLayoutParams(params); mStatusBarTintView.setBackgroundColor(DEFAULT_TINT_COLOR); mStatusBarTintView.setVisibility(View.GONE); decorViewGroup.addView(mStatusBarTintView); }
通讀這段代碼池户,很容易看出咏雌,通過動(dòng)態(tài)生成一個(gè)view,然后這個(gè)view寬是
MATCH_PARENT
,高度是系統(tǒng)狀態(tài)欄的高度校焦;然后為這個(gè)動(dòng)態(tài)生成的view設(shè)置一個(gè)背景顏色赊抖;最后將這個(gè)view添加到decorViewGroup
這個(gè)view容器中,那再看看這個(gè)view到底是誰/** * Constructor. Call this in the host activity onCreate method after its * content view has been set. You should always create new instances when * the host activity is recreated. * * @param activity The host activity. */ @TargetApi(19) public SystemBarTintManager(Activity activity) { Window win = activity.getWindow(); //獲得DecorView根布局容器 ViewGroup decorViewGroup = (ViewGroup) win.getDecorView(); ..... if (mStatusBarAvailable) { // 這個(gè)view容器是decorViewGroup setupStatusBarView(activity, decorViewGroup); } if (mNavBarAvailable) { setupNavBarView(activity, decorViewGroup); } }
注意看上面兩個(gè)我手動(dòng)添加的注釋寨典,可見氛雪,這種做法思想就是
狀態(tài)欄透明后,向根布局decorViewGroup
中添加一個(gè)和狀態(tài)欄等高度的view腾它。至于你讓這個(gè)view是什么顏色呻纹,那就隨你心情了缀踪。
用標(biāo)題欄的背景色來填充狀態(tài)欄
將狀態(tài)欄設(shè)置為半透明的,此時(shí)出現(xiàn)的問題是下面的內(nèi)容會(huì)占據(jù)了狀態(tài)欄弦追。
如果我們在activity的根布局添加 android:fitsSystemWindows="true"
那么此時(shí)狀態(tài)欄還是可以看見的,并沒有占據(jù)毙沾。那這個(gè)屬性的作用就在此了骗卜。
此時(shí)我們借助狀態(tài)欄的高度,為下面的內(nèi)容設(shè)置一個(gè)padding-top距離(因?yàn)闋顟B(tài)欄半透明后左胞,
下面的內(nèi)容會(huì)占據(jù)原有的狀態(tài)欄寇仓,那么將其設(shè)置一個(gè)padding的狀態(tài)欄高度即可)
這樣設(shè)置后,在這個(gè)view的背景的padding下烤宙,原有的狀態(tài)欄高度填充了同樣的背景色遍烦,那么這樣的話就貌似所謂的沉浸式了
這種方式說白了,就是狀態(tài)欄半透明后躺枕,用下面的內(nèi)容來合適的填充(因?yàn)槟J(rèn)半透明會(huì)是被占據(jù))
代碼如下:
@SuppressLint("InlinedApi")
public static void setImmerseLayout(Activity context, View view) {
if (context == null || view == null) {
return;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Window window = context.getWindow();
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
int statusBarHeight = getStatusBarHeight(context.getBaseContext());
view.setPadding(0, statusBarHeight, 0, 0);
}
}
/**
* 用于獲取狀態(tài)欄的高度服猪。 使用Resource對象獲取(推薦這種方式)
*
* @return 返回狀態(tài)欄高度的像素值拐云。
*/
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;
}
對這個(gè)view設(shè)置一個(gè)padding_top罢猪,而這個(gè)padding的距離剛好是狀態(tài)欄的高度,那么這個(gè)view的背景就填充到了狀態(tài)欄了叉瘩。
值得注意的是:這個(gè)標(biāo)題欄的高度一定要是
wrap_content
,因?yàn)槿绻蔷唧w的高度膳帕,然后在設(shè)置個(gè)paddingtop
的話,那么就會(huì)把部分標(biāo)題欄的內(nèi)容擠出去了薇缅,不完整了危彩。-
所以通常的做法是將原有的標(biāo)題欄外面在嵌套一個(gè)
<FrameLayout />
攒磨,然后將標(biāo)題欄背景設(shè)置成<FrameLayout />
的背景色<FrameLayout android:id="@+id/title" android:layout_width="match_parent" android:background="@color/common_theme_color" android:layout_height="wrap_content">
后記
關(guān)于沉浸式效果的實(shí)現(xiàn)方式就啰嗦這么多了.
另外,關(guān)于EditText及全屏Dialog等實(shí)現(xiàn)沉浸式問題汤徽,后期在更新娩缰,其實(shí)就是那個(gè)android:fitsSystemWindows="true"
的靈活設(shè)置。歡迎交流谒府。
下一篇會(huì)實(shí)現(xiàn)一個(gè)效果:頁面頂部是圖片,滑動(dòng)漸變標(biāo)題欄