歡迎你們前往騰訊雲+社區,獲取更多騰訊海量技術實踐乾貨哦~android
作APP開發的過程當中,有不少時候,咱們須要實現相似於下面這種沉浸式的體驗。框架
沉浸式體驗一開始接觸的時候,彷佛你們都會覺這種體驗實現起來,會比較困難。難點在於:機器學習
好,總結起來以上就是咱們的問題,也是須要解決的,一個一個解決了,這種需求也就實現了,那麼,咱們如何去一步一步來解決以上的問題呢?ide
首先,咱們來分析第一個問題,頭部的背景圖在推上去的過程當中,慢慢的變得不可見了,這種聽起來好像是某種collapse,所以,很容易讓人想到CollapsingToolbarLayout,若是你想要比較容易的瞭解CollapsingToolbarLayout佈局
應用,建議看這位兄臺的文章,他給也給了一個動畫,比較詳細的介紹了這個的應用,例如:學習
CollapsingToolbarLayout對於裏面的用法,我這裏不做講解了,可是若是你不瞭解這個佈局的應用,我強烈建議你好好了解一下,才能繼續下面走,只是想說明一下,走到這裏,你有一個坑須要去填,那就是咱們的標題動畫能夠不是這樣的,並且,仍是標題仍是居中的,注意,這裏的實現,標題不是居中的,是靠左的,這原本是Android設計規範,可是設計師恰恰不買Android規範的帳,所以,咱們必須躺過這個坑,而後,從Stack Overflow上了解到一個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> 複製代碼
假設,這個方式是可行的,那麼要解決居中的問題後,把返回按鈕改成咱們的按鈕樣式,而後,在耍點小詭計,讓title開始是透明的,而且改變返回按鈕的圖片:ui
collapsingToolbarLayout.setCollapsedTitleTextColor(Color.WHITE);
//collapsingToolbarLayout.setExpandedTitleColor(Color.WHITE);
collapsingToolbarLayout.setExpandedTitleColor(Color.TRANSPARENT);
複製代碼
然而,假設,始終只是一個假設,實際上,這個假設不成立,我在嘗試的時候,發現Toolbar中的TextView根本就不能使用android:layout_gravity="center"這種屬性好吧,即便強行加上,效果也是靠左的。spa
那麼,如何作,個人解決方式是這樣的
<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">
//*****請注意這個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="這裏是標題" />
<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>
複製代碼
效果就是這樣
固然,這時候,標題是須要你本身設置漸隱漸現的。那麼,咱們依據什麼呢?
appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
mTitle.setAlpha(-verticalOffset * 1.0f / appBarLayout.getTotalScrollRange());
}
});
複製代碼
依據的就是對appBarLayout的監聽。
/** * 使狀態欄透明,並覆蓋狀態欄,對API大於19的顯示正常,但小於的界面擴充到狀態欄,但狀態欄不爲透明 */
@TargetApi(Build.VERSION_CODES.KITKAT)
public static void transparentAndCoverStatusBar(Activity activity) {
//FLAG_LAYOUT_NO_LIMITS這個千萬別用,帶虛擬按鍵的機型會有特別多問題
// //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);
}
}
複製代碼
這裏是在網上找的一個方法,直接調用便可,可是API須要大於19,相信目前基本上都知足吧。請注意,個人AppBarLayout中並無這個屬性
android:fitsSystemWindows="true"
複製代碼
若是你加了這個屬性,嘿嘿,statusbar雖然空間能夠利用,可是有一個你揮之不去的顏色覆蓋在上面,
而後,你還記得上面那個佈局中
//*****請注意這個View*******///
<View
android:id="@+id/common_index_activity_view_status_bar"
android:layout_width="match_parent"
android:layout_height="0dp" />
複製代碼
這個做用可大了,就是爲了對status_bar原始空間作偏移的,在代碼中,須要動態的改變這個View的高度爲statusBar的高度,怎麼獲取:
/** * 獲取狀態欄高度 * * @param context context * @return 狀態欄高度 */
public static int getStatusBarHeight(Context context) {
// 得到狀態欄高度
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
return context.getResources().getDimensionPixelSize(resourceId);
}
複製代碼
完了以後,還須要設置咱們本身塞進去的那個toolbar的高度爲toolbar的高度加上StatusBar的高度。
這個其實須要你CollapsingToolbarLayout裏面有一個子view是要使用pin模式的,那麼這個子view是誰,顯然就是那個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> 複製代碼
能夠看到,底部的控件是覆蓋在列表上的,列表向上滑動的時候,把他隱藏,就能夠空出更多的控件看列表。那麼,如何作呢?
既然,咱們是包裹在CoordinatorLayout中,那麼,顯然,最好的方式是使用layout_behavior了,我這裏實現了一個BottomBehavior:
public class BottomBehavior extends CoordinatorLayout.Behavior {
private int id;
private float bottomPadding;
private int screenWidth;
private float designWidth = 375.0f;//設計視圖的寬度,一般是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;
}
}
複製代碼
這個裏面有兩個自定義屬性,id,bottomPadding,id表示基於哪一個控件的相對位置改變,我這打算基於viewpager
這個控件,看源碼能夠知道,只有當onDependentViewChanged返回ture時,layoutDependsOn纔會被回調。bottomPadding是表示一個初始的偏移,由於viewpager自己不是頂在屏幕頂端的(開始被圖片佔據了一部分控件),所以,須要扣除這部分佔有。
同理,加入讓你實現一個懸浮在左側,右側,滑動隱藏,中止顯示的,也均可以參考相似Behavior的方式,減小代碼耦合。
最後整個佈局是這樣子的
<?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實際上就是封裝的PullToRefreshView,IgameViewPager是咱們封裝的Viewpager,減小每次寫Viewpager的套路代碼。
按照這個框架來,相信你很容易寫出這個樣子的佈局。
此文已由做者受權騰訊雲+社區發佈,更多原文請點擊
搜索關注公衆號「雲加社區」,第一時間獲取技術乾貨,關注後回覆1024 送你一份技術課程大禮包!
海量技術實踐經驗,盡在雲加社區!