Android實(shí)現(xiàn)超級棒的沉浸式體驗(yàn)教程
前言
大家在做APP開發(fā)的過程中,有很多時(shí)候,我們需要實(shí)現(xiàn)類似于下面這種沉浸式的體驗(yàn)。

沉浸式體驗(yàn)
一開始接觸的時(shí)候,似乎大家都會覺這種體驗(yàn)實(shí)現(xiàn)起來,會比較困難。難點(diǎn)在于:
- 頭部的背景圖在推上去的過程中,慢慢的變得不可見了,整個(gè)區(qū)域的顏色變成的暗黑色,然后標(biāo)題出現(xiàn)了。
- StatusBar變的透明,且空間可以被利用起來,看我們的圖片就頂?shù)搅隧?了。
- 我們的viewpager推到actionbar的下方的時(shí)候,就固定在了actionbar的下方,不能在往上面推了。
- 底部有一個(gè)控件,隨著列表的向上滑動,它退出視角范圍,以便于給出更多的空間來展示列表,其實(shí)整個(gè)沉浸式體驗(yàn)都是為了給列表留出更多的空間來展示。
好,總結(jié)起來以上就是我們的問題,也是需要解決的,一個(gè)一個(gè)解決了,這種需求也就實(shí)現(xiàn)了,那么,我們?nèi)绾稳ヒ徊揭徊絹斫鉀Q以上的問題呢?
1、頭部背景和標(biāo)題的漸隱漸現(xiàn)
首先,我們來分析第一個(gè)問題,頭部的背景圖在推上去的過程中,慢慢的變得不可見了,這種聽起來好像是某種collapse,因此,很容易讓人想到CollapsingToolbarLayout,如果你想要比較容易的了解CollapsingToolbarLayout
應(yīng)用,建議看這位兄臺的文章,他給也給了一個(gè)動畫,比較詳細(xì)的介紹了這個(gè)的應(yīng)用,例如:

CollapsingToolbarLayout
對于里面的用法,我這里不作講解了,但是如果你不了解這個(gè)布局的應(yīng)用,我強(qiáng)烈建議你好好了解一下,才能繼續(xù)下面走,只是想說明一下,走到這里,你有一個(gè)坑需要去填,那就是我們的標(biāo)題動畫可以不是這樣的,而且,還是標(biāo)題還是居中的,注意,這里的實(shí)現(xiàn),標(biāo)題不是居中的,是靠左的,這本來是Android設(shè)計(jì)規(guī)范,但是設(shè)計(jì)師偏偏不買Android規(guī)范的賬,因此,我們必須躺過這個(gè)坑,然后,從Stack Overflow上了解到一個(gè)issue:
<android.support.v7.widget.Toolbar android:id="@+id/toolbar_top" android:layout_height="wrap_content" android:layout_width="match_parent" android:minHeight="?attr/actionBarSize" android:background="@color/action_bar_bkgnd" app:theme="@style/ToolBarTheme" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Toolbar Title" android:layout_gravity="center" android:id="@+id/toolbar_title" /> </android.support.v7.widget.Toolbar>
假設(shè),這個(gè)方式是可行的,那么要解決居中的問題后,把返回按鈕改為我們的按鈕樣式,然后,在耍點(diǎn)小詭計(jì),讓title開始是透明的,并且改變返回按鈕的圖片:
collapsingToolbarLayout.setCollapsedTitleTextColor(Color.WHITE); //collapsingToolbarLayout.setExpandedTitleColor(Color.WHITE); collapsingToolbarLayout.setExpandedTitleColor(Color.TRANSPARENT);
然而,假設(shè),始終只是一個(gè)假設(shè),實(shí)際上,這個(gè)假設(shè)不成立,我在嘗試的時(shí)候,發(fā)現(xiàn)Toolbar中的TextView根本就不能使用android:layout_gravity="center"這種屬性好吧,即使強(qiáng)行加上,效果也是靠左的。
那么,如何做,我的解決方式是這樣的
<android.support.design.widget.AppBarLayout android:id="@+id/appbarlayout" android:layout_width="match_parent" android:layout_height="wrap_content" app:elevation="0dp"> <android.support.design.widget.CollapsingToolbarLayout android:id="@+id/collapsing_tool_bar" android:layout_width="match_parent" android:layout_height="wrap_content" app:contentScrim="@color/b_G6" app:expandedTitleMarginEnd="10dp" app:expandedTitleMarginStart="10dp" app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"> <android.support.constraint.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/igame_arena_rank_class_header_bg" android:layout_width="match_parent" android:layout_height="0dp" android:scaleType="centerCrop" android:src="@drawable/bg_arena_rank_class" app:layout_constraintDimensionRatio="375:156" /> ......... </android.support.constraint.ConstraintLayout> <android.support.v7.widget.Toolbar android:id="@+id/common_index_activity_tb_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="?android:attr/actionBarSize" android:visibility="visible" app:contentInsetLeft="0dp" app:contentInsetStart="0dp" app:layout_collapseMode="pin"> <include layout="@layout/igame_common_tool_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" /> </android.support.v7.widget.Toolbar> </android.support.design.widget.CollapsingToolbarLayout> </android.support.design.widget.AppBarLayout>
然后,include里面的布局是這樣的
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> //*****請注意這個(gè)View*******/// <View android:id="@+id/common_index_activity_view_status_bar" android:layout_width="match_parent" android:layout_height="0dp" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="50dp"> <TextView android:id="@+id/tv_toolbar_bg" android:layout_width="match_parent" android:layout_height="50dp" android:layout_centerInParent="true" tools:background="@color/b_G6" /> <TextView android:id="@+id/common_index_header_tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:gravity="center" android:textColor="@color/b_G99" android:textSize="@dimen/igame_textsize_xl" tools:text="這里是標(biāo)題" /> <RelativeLayout android:id="@+id/common_index_header_rl_back" android:layout_width="48dp" android:layout_height="48dp" android:layout_centerVertical="true" android:layout_gravity="center_vertical" android:visibility="visible"> <ImageView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true" android:contentDescription="@string/image_desc" android:scaleType="centerInside" android:src="@drawable/igame_actionbar_arrow_left" /> </RelativeLayout> </RelativeLayout> </LinearLayout>

效果就是這樣
當(dāng)然,這時(shí)候,標(biāo)題是需要你自己設(shè)置漸隱漸現(xiàn)的。那么,我們依據(jù)什么呢?
appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
mTitle.setAlpha(-verticalOffset * 1.0f / appBarLayout.getTotalScrollRange());
}
});
依據(jù)的就是對appBarLayout的監(jiān)聽。
2、將statusBar變?yōu)橥该?,且利用他的空間來放我們的布局內(nèi)容。
/**
* 使?fàn)顟B(tài)欄透明,并覆蓋狀態(tài)欄,對API大于19的顯示正常,但小于的界面擴(kuò)充到狀態(tài)欄,但狀態(tài)欄不為透明
*/
@TargetApi(Build.VERSION_CODES.KITKAT)
public static void transparentAndCoverStatusBar(Activity activity) {
//FLAG_LAYOUT_NO_LIMITS這個(gè)千萬別用,帶虛擬按鍵的機(jī)型會有特別多問題
// //FLAG_TRANSLUCENT_STATUS要求API大于19
// activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
// activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
// //FLAG_LAYOUT_NO_LIMITS對API沒有要求
// activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = activity.getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
window.setNavigationBarColor(Resources.getSystem().getColor(android.R.color.background_dark));
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Window window = activity.getWindow();
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
這里是在網(wǎng)上找的一個(gè)方法,直接調(diào)用即可,但是API需要大于19,相信目前基本上都滿足吧。請注意,我的AppBarLayout中并沒有這個(gè)屬性
android:fitsSystemWindows="true"
如果你加了這個(gè)屬性,嘿嘿,statusbar雖然空間可以利用,但是有一個(gè)你揮之不去的顏色覆蓋在上面,
然后,你還記得上面那個(gè)布局中
//*****請注意這個(gè)View*******/// <View android:id="@+id/common_index_activity_view_status_bar" android:layout_width="match_parent" android:layout_height="0dp" />
這個(gè)作用可大了,就是為了對status_bar原始空間做偏移的,在代碼中,需要?jiǎng)討B(tài)的改變這個(gè)View的高度為statusBar的高度,怎么獲?。?/p>
/**
* 獲取狀態(tài)欄高度
*
* @param context context
* @return 狀態(tài)欄高度
*/
public static int getStatusBarHeight(Context context) {
// 獲得狀態(tài)欄高度
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
return context.getResources().getDimensionPixelSize(resourceId);
}
完了之后,還需要設(shè)置我們自己塞進(jìn)去的那個(gè)toolbar的高度為toolbar的高度加上StatusBar的高度。
3、ViewPager推到actionbar下面就不讓在推了
這個(gè)其實(shí)需要你CollapsingToolbarLayout里面有一個(gè)子view是要使用pin模式的,那么這個(gè)子view是誰,顯然就是那個(gè)toolbar了
<android.support.v7.widget.Toolbar android:id="@+id/common_index_activity_tb_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="?android:attr/actionBarSize" android:visibility="visible" app:contentInsetLeft="0dp" app:contentInsetStart="0dp" app:layout_collapseMode="pin"> <include layout="@layout/igame_common_tool_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" /> </android.support.v7.widget.Toolbar>
4、底部控件隨著列表的滑動漸漸隱藏
可以看到,底部的控件是覆蓋在列表上的,列表向上滑動的時(shí)候,把他隱藏,就可以空出更多的控件看列表。那么,如何做呢?
既然,我們是包裹在CoordinatorLayout中,那么,顯然,最好的方式是使用layout_behavior了,我這里實(shí)現(xiàn)了一個(gè)BottomBehavior:
public class BottomBehavior extends CoordinatorLayout.Behavior {
private int id;
private float bottomPadding;
private int screenWidth;
private float designWidth = 375.0f;//設(shè)計(jì)視圖的寬度,通常是375dp,
public BottomBehavior() {
super();
}
public BottomBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
screenWidth = getScreenWidth(context);
TypedArray typedArray = context.getResources().obtainAttributes(attrs, R.styleable.BottomBehavior);
id = typedArray.getResourceId(R.styleable.BottomBehavior_anchor_id, -1);
bottomPadding = typedArray.getFloat(R.styleable.BottomBehavior_bottom_padding, 0f);
typedArray.recycle();
}
@Override
public void onAttachedToLayoutParams(@NonNull CoordinatorLayout.LayoutParams params) {
params.dodgeInsetEdges = Gravity.BOTTOM;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
return dependency.getId() == id;
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
child.setTranslationY(-(dependency.getTop() - (screenWidth * bottomPadding / designWidth)));
Log.e("BottomBehavior", "layoutDependsOn() called with: parent = [" + dependency.getTop());
return true;
}
public static int getScreenWidth(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = null;
if (wm != null) {
display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
// int height = size.y;
return width;
}
return 0;
}
}
這個(gè)里面有兩個(gè)自定義屬性,id,bottomPadding,id表示基于哪個(gè)控件的相對位置改變,我這打算基于viewpager
這個(gè)控件,看源碼可以知道,只有當(dāng)onDependentViewChanged返回ture時(shí),layoutDependsOn才會被回調(diào)。bottomPadding是表示一個(gè)初始的偏移,因?yàn)関iewpager本身不是頂在屏幕頂端的(開始被圖片占據(jù)了一部分控件),因此,需要扣除這部分占有。
同理,加入讓你實(shí)現(xiàn)一個(gè)懸浮在左側(cè),右側(cè),滑動隱藏,停止顯示的,也都可以參考類似Behavior的方式,減少代碼耦合。
總結(jié)
最后整個(gè)布局是這樣子的
<?xml version="1.0" encoding="utf-8"?> <com.tencent.igame.view.common.widget.IGameRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/igame_competition_detail_fragment_refresh" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.design.widget.AppBarLayout android:id="@+id/appbarlayout" android:layout_width="match_parent" android:layout_height="wrap_content" app:elevation="0dp"> <android.support.design.widget.CollapsingToolbarLayout android:id="@+id/collapsing_tool_bar" android:layout_width="match_parent" android:layout_height="wrap_content" app:contentScrim="@color/b_G6" app:expandedTitleMarginEnd="10dp" app:expandedTitleMarginStart="10dp" app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"> <android.support.constraint.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/igame_arena_rank_class_header_bg" android:layout_width="match_parent" android:layout_height="0dp" android:scaleType="centerCrop" android:src="@drawable/bg_arena_rank_class" app:layout_constraintDimensionRatio="375:156" /> ............ </android.support.constraint.ConstraintLayout> <android.support.v7.widget.Toolbar android:id="@+id/common_index_activity_tb_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="?android:attr/actionBarSize" android:visibility="visible" app:contentInsetLeft="0dp" app:contentInsetStart="0dp" app:layout_collapseMode="pin"> <include layout="@layout/igame_common_tool_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" /> </android.support.v7.widget.Toolbar> </android.support.design.widget.CollapsingToolbarLayout> </android.support.design.widget.AppBarLayout> <com.tencent.igame.widget.viewpager.IgameViewPager android:id="@+id/igame_arena_rank_class_vp_content" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> <android.support.constraint.ConstraintLayout android:layout_width="match_parent" android:layout_height="60dp" android:layout_gravity="bottom" android:background="@color/b_G6" android:paddingLeft="12dp" android:paddingRight="12dp" app:anchor_id="@+id/igame_arena_rank_class_vp_content" app:bottom_padding="156.0" app:layout_behavior="com.tencent.igame.common.widget.BottomBehavior"> ..........底部布局 </android.support.constraint.ConstraintLayout> </android.support.design.widget.CoordinatorLayout> </com.tencent.igame.view.common.widget.IGameRefreshLayout>
注:IGameRefreshLayout實(shí)際上就是封裝的PullToRefreshView,IgameViewPager是我們封裝的Viewpager,減少每次寫Viewpager的套路代碼。
按照這個(gè)框架來,相信你很容易寫出這個(gè)樣子的布局。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
- Android實(shí)現(xiàn)沉浸式通知欄通知欄背景顏色跟隨app導(dǎo)航欄背景顏色而改變
- Android 實(shí)現(xiàn)沉浸式狀態(tài)欄的方法
- 解決Android 沉浸式狀態(tài)欄和華為虛擬按鍵沖突問題
- Android 沉浸式狀態(tài)欄與隱藏導(dǎo)航欄實(shí)例詳解
- Android沉浸式狀態(tài)欄微技巧(帶你真正理解沉浸式模式)
- Android之沉浸式狀態(tài)欄的實(shí)現(xiàn)方法、狀態(tài)欄透明
- Android 4.4以上"沉浸式"狀態(tài)欄效果的實(shí)現(xiàn)方法
- Android App仿QQ制作Material Design風(fēng)格沉浸式狀態(tài)欄
- Android實(shí)現(xiàn)沉浸式導(dǎo)航欄實(shí)例代碼
- Android 高仿QQ 沉浸式狀態(tài)欄
相關(guān)文章
Android攔截并獲取WebView內(nèi)部POST請求參數(shù)的實(shí)現(xiàn)方法
這篇文章主要介紹了Android攔截并獲取WebView內(nèi)部POST請求參數(shù) 的實(shí)現(xiàn)方法,本文通過兩種方案給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-04-04
Android開發(fā)仿bilibili刷新按鈕的實(shí)現(xiàn)代碼
這篇文章主要介紹了Android 仿bilibili刷新按鈕的實(shí)現(xiàn)代碼,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-10-10
RecyclerView實(shí)現(xiàn)列表倒計(jì)時(shí)
這篇文章主要為大家詳細(xì)介紹了RecyclerView實(shí)現(xiàn)列表倒計(jì)時(shí),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09
詳解Android的MVVM框架 - 數(shù)據(jù)綁定
這篇文章主要介紹了詳解Android的MVVM框架 - 數(shù)據(jù)綁定,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05
官網(wǎng)項(xiàng)目Jetpack?Startup庫學(xué)習(xí)
這篇文章主要為大家介紹了官網(wǎng)項(xiàng)目Jetpack?Startup庫學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02

