CoordinatorLayout顧名思義協調佈局,是用來協調該佈局下的子控件,最簡單地使用就是頭部伸縮和摺疊了,配合着TabLayout,只需要設置一下AppBarLayout子控件的layout_scrollFlags以及底下滑動控件的layout_behavior就行了,組合起來效果非常不錯,網上這種詳細的教程有很多,這片文章主要講述一下簡單的自定義behavior。
在behavior中可以設置觸摸事件攔截onInterceptTouchEvent(),觸摸事件響應onTouchEvent(),View依賴另一個ViewlayoutDependsOn()、onDependentViewChanged(),嵌套滑動事件處理onStartNestedScroll()、onStopNestedScroll()、onNestedScroll()、onNestedPreScroll()以及還有兩個快速滑動的方法,
這篇文章使用的是behavior的依賴方法,ViewlayoutDependsOn()和onDependentViewChanged()。
ViewlayoutDependsOn(CoordinatorLayout parent, View child, View dependency):child是使用這個behavior的view,dependency則是child所依賴的view。何爲依賴?在我看來,就是dependency改變的時候,child能根據dependency的變化,做出反應,如何反應?當 ViewlayoutDependsOn 返回true的時候就可以觸發 onDependentViewChanged。
反應動作寫在onDependentViewChanged(CoordinatorLayout parent, View child, View dependency),比方說,可以通過dependency.getX dependency.getY獲取的座標,設置給child,該View就會跟着依賴View動起來,或者讓它反方向動,都可以,非常靈活,最後return true就會生效。
那麼來嘗試一下吧,UC中首頁漂亮的交互動畫非常的吸引人,自己也想學着做一個簡單的頁面。單獨看這個頁面的話,有三塊佈局,一個是底下的RecyclerView,一個是逐漸被遮住的導航欄,最後一個是在RecyclerView逐漸上移時候出現的UC頭條和TabLayout。如下圖(視頻用UC轉gif就變得好糊,好在還能看出動畫):
在我看來,實現方法有兩個:1.通過監聽RecyclerView的觸摸,然後判斷是否讓他滑動,以及他在位置改變的時候,TabLayout跟着變動;2.那就是今天的主題了,自定義behavior。第一種方法以前寫的時候嘗試過,比較難的在於,滑動過程中RecyclerView高度和位置的變化,它並不是簡單的上移,單純上移的話,底下會缺一塊;再來就是觸摸事件的分發和滑動的攔截了,以前寫的時候失敗了,bug很嚴重,對於事件分發的確不是很紮實,還得多學學。採用第二種,其實也會碰到這些難點,但是今天在於簡單的實踐,最簡單地使用CoordinatorLayout。相信大家都有所瞭解,在寫了AppBarLayout,滑動佈局添加
app:layout_behavior="@string/appbar_scrolling_view_behavior"
滑動佈局高度和滑動處理,就解決了,谷歌已經幫我們實現了。我們只需要把UC頭條和tablayout依賴RecyclerView,根據RecyclerView的座標做出改變就行了。下面給出代碼,先是自定義Behavior,我使用了NestedScrollView代替RecyclerView,插數據比較好插,要依賴RecyclerView的自己替代一下:
public class MyBehavior extends CoordinatorLayout.Behavior<View> {
private float allDistance = 0; // Head從和RecyclerView同樣位置,移到頂部的距離
private float dependencyDistance = 0; //RecyclerView需要移動的距離
public MyBehavior() {
}
public MyBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return dependency instanceof NestedScrollView;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
if (allDistance == 0) {
allDistance = dependency.getY();//獲取child要移動的總距離,是dependency到頂部的距離
Log.d("allDistance", String.valueOf(allDistance));
dependencyDistance = allDistance - child.getHeight();//獲取滑動控件所要移動的總距離,移動到childView下方
Log.d("recyclerDistance", String.valueOf(dependencyDistance));
child.setY(allDistance);//初始化child的位置,和dependency位置一樣,不然child會在頂部
} else {
float distance = (allDistance - dependency.getY()) / dependencyDistance * allDistance;//child所需要移動的距離!
child.setY(allDistance - distance <= 0 ? 0 : allDistance - distance);//通過距離,計算出child的座標,最後爲0
}
return true;
}
}
這個behavior比較簡單,根據dependency的Y座標,計算出child的Y座標。接下來是佈局文件,比較重要:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.env.cloud.showview.ui.CoordinatorActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#00ffffff">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="150dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
<TextView
android:layout_width="match_parent"
android:layout_height="250dp"
android:gravity="center"
android:text="導航欄"
android:textColor="#000"
android:textSize="25sp"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="2.7" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="150dp"
android:background="#fff000"
android:orientation="vertical"
app:layout_behavior="com.env.cloud.showview.views.behavior.MyBehavior">
<TextView
android:layout_width="match_parent"
android:layout_height="80dp"
android:gravity="center"
android:text="UC頭條和搜索框"
android:textSize="25sp" />
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="關注" />
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="推薦" />
</android.support.design.widget.TabLayout>
</LinearLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:gravity="center"
android:text="內容1"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:gravity="center"
android:text="內容2"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:gravity="center"
android:text="內容3"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:gravity="center"
android:text="內容4"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:gravity="center"
android:text="內容5"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:gravity="center"
android:text="內容6"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:gravity="center"
android:text="內容"
android:textSize="20sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="100dp"
android:gravity="center"
android:text="內容7"
android:textSize="20sp" />
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
這邊有幾個點,需要說一下,一個是AppBarLayout設置了一個background爲全透明,原因是因爲,在摺疊完成後,AppBarLayout本來應該在LinearLayout下方,它會跑上來,變成頂層,不設置透明的話,自帶的顏色會遮住LinearLayout。LinearLayout中的點擊事件應該是不會和CollapsingToolBarLayout衝突的,我沒有嘗試,需要的小夥伴可以試一下。
然後就是在CollapsingToolbarLayout中設置,設置layout_scrollFlags爲scroll|exitUntilCollapsed,snap可選可不選,作用是回彈效果,、android:minHeight="150dp"來控制滑動View能滑動到的距離,這個高度應該和TabLayout+Uc導航欄和搜索框所在的LinearLayout高度一致,這樣的話,滑動的View就剛好能在LinearLayout下方。
設置CollapsingToolBarLayout的子控件的摺疊:
app:layout_collapseMode="parallax" app:layout_collapseParallaxMultiplier="2.7"
重點在於layout_collapseParallaxMultiplier,網上叫做視覺差係數,就是摺疊快慢的控制,之所以設置成這麼大,是由於,不夠大的情況下,CollapsingToolBarLayout沒有收縮完全,裏面控件會到上層顯示,把LinearLayout遮住,設置大一些的話,會完全摺疊,這就是我覺得點擊事件不會衝突的原因,因爲完全消失了,並不在LinearLayout所在的區域,自然就不會有點擊衝突。
關鍵點:摺疊佈局CollapsingToolbarLayout的最小高度,要和滑動出現的LinearLayout高度一致,這是爲了控制滑動控件,比如RecyclerView,NestedScrollView的滑動距離;設置layout_collapseParallaxMultiplier係數大一點,可以自己設置小一點看看效果,上面的佈局文件的話,導航欄三個字就會出現在LinearLayout所在的區域,下面上效果圖:
額,大致是這樣,後面嘗試不使用AppBarLayout,再試試吧