CoordinatorLayout是在 Google IO/15 大會發布的控件,常常與AppBarLayout和CollapsingToolbarLayout一起使用,用于打造各種炫酷效果的頂部欄。
使用前須知
CoordinatorLayout目前我僅在RecyclerView、NestedScrollView、ViewPager上面成功通過設置behavior來監聽滑動事件,ListView和ScrollView都是不行的。什么原因,我們來看下CoordinatorLayout的源碼:
可以看到CoordinatorLayout繼承了NestedScrollingParent接口,那么再看下RecyclerView和NestedScrollView吧:
這樣子想必都猜到是什么回事了。
Android 在發布 Lollipop版本之后,Google為Android的滑動機制提供了NestedScrolling機制,為了給用戶帶來更好的體驗。而CoordinatorLayout正是基于NestedScrolling來開發的。
NestedScrolling機制可以簡單理解為child將它的一些滑動事件交給了Parent去處理了。ListView和ScrollView無法和CoordinatorLayout配合使用的原因就是因為它們沒繼承這個NestedScrollingChild接口。那么ViewPager可以配合使用的原因是什么?查看ViewPager的繼承關系會發現它沒有繼承這個Child接口,但是我們去看下onInterceptTouchEvent方法里面,會看到有下面的注釋:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//省去部分代碼
if (dx != 0 && !isGutterDrag(mLastMotionX, dx)
&& canScroll(this, false, (int) dx, (int) x, (int) y)) {
// Nested view has scrollable area under this point. Let it be handled there.
mLastMotionX = x;
mLastMotionY = y;
mIsUnableToDrag = true;
return false;
}
..............
翻譯過來大意是這個方向有嵌套的View在滾動,就讓它在那里處理吧。換言之,就是ViewPager檢測到NestedScrolling相關的事件觸發,就不做攔截,讓外層的CoordinatorLayout成功監聽到ViewPager嵌套的RecyclerView或NestedScrollView的滑動事件。
如果你對NestedScrolling機制很感興趣,這里推薦下面的文章:
Android NestedScrolling機制完全解析 帶你玩轉嵌套滑動
Android 嵌套滑動機制(NestedScrolling)
Android NestedScrolling 實戰
接下來是本文的重點了。
AppBarLayout
剛才已經提過了,CoordinatorLayout是經常與AppBarLayout配合使用的,所以很有必要搞明白AppBarLayout到底能做哪些事情。
AppBarLayout它繼承LinearLayout,布局方向默認為垂直方向,它可以讓你定制RecyclerView、NestedScrollView、ViewPager的滾動手勢發生變化時,其內部的子View實現某個指定的動作。
目前布局代碼如下,這里先用RecyclerView來說明:
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.fritz.layout.MainActivity">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="50dp"
app:layout_scrollFlags="待補充"
app:title="CoordinatorLayout" />
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>
這里需要注意的是app:layout_behavior="@string/appbar_scrolling_view_behavior"這個屬性,就是這個屬性讓AppBarLayout與RecyclerView之間動作相互依賴,這也是Google幫我們定義好了一個behavior,我們直接用就是了。
AppBarLayout內部的子View通過在布局中加app:layout_scrollFlags設置執行的動作,那么app:layout_scrollFlags可以設置哪些動作呢?我們一一來看吧,下面的View值的是AppBarLayout里面的子View:
- 1.scroll: 想要當滑出屏幕的時候View都必須設置這個flag,沒有設置這個flag的View將被固定在屏幕頂部,其他的flag屬性也無法生效
- 2.enterAlways:flag設為scroll|enterAlways的View,當RecyclerView上的手指往上滾動時,該View會直接往上滾動移出屏幕;當RecyclerView上的手指往下滾動時,該View會立刻往下滾動恢復原位
- 3.exitUntilCollapsed:flag設為scroll|exitUntilCollapsed的View,當這個View要往上逐漸滑出屏幕時,會一直往上滑動,直到剩下的的高度達到它的最小高度后,再響應RecyclerView的內部滑動事件。請注意,如果沒有設置android:minHeight的話,那么這個屬性是不會生效的。這里我將toolbar的android:layout_height設為200dp,android:minHeight設為50dp,效果如下:
- 4.enterAlwaysCollapsed:這個是enterAlways的附加選項,要跟enterAlways一起使用,同時需要設置android:minHeight屬性,否則不起作用的。它是指,當RecyclerView上的手指往下滾動時,View首先是enterAlways效果,當View的高度達到最小高度時,View就暫時不去往下滾動,直到某個可滾動View滑動到頂部并停止滑動時,View再繼續往下滑動,直到滑到View的頂部結束。為了更好展示這個效果,我將toolbar的android:layout_height設為200dp,android:minHeight設為50dp,效果如下:
- 5.snap:這個和上面的一樣,不會單獨使用,常與exitUntilCollapsed配合使用。在滾動結束后,如果View只是部分可見,它將滑動到最近的邊界。比如,如果View的底部只有25%可見時停止滑動,它將繼續滑動直至離開屏幕,而如果底部有75%可見時停止滑動,它將繼續滑動到完全顯示。
好了,AppBarLayout相關功能到這里說完了,其實用起來還是蠻簡單的。接著,到另一位主角CollapsingToolbarLayout的出場了。
CollapsingToolbarLayout
CollapsingToolbarLayout是用來對Toolbar進行再次包裝的FrameLayout,主要是用于實現折疊(個人覺得收縮比較貼實)的AppBar效果。它需要放在AppBarLayout布局里面,并且作為AppBarLayout的直接子View。它常用的有以下屬性:
- 1.app:contentScrim:CollapsingToolbarLayout折疊(收縮)完成后的背景顏色
- 2.app:expandedTitleMarginXXX:設置CollapsingToolbarLayout展開時候title的Margin
- 3.app:layout_collapseMode:這個是讓CollapsingToolbarLayout的子View設置的,共有3個flag可以使用,它們分別為:
1) parallax:設置為這個flag時為視差模式,也就是子View折疊模式。在RecyclerView等View在滑動時,子View以視差(折疊)的方式來滑動
2)pin:設置為這個flag時為固定模式,在折疊的時候會固定在頂端
3)none:設置為這個flag時為沒有效果,RecyclerView往上滑動的時候,子View會首先被固定并推出去 - 4.app:layout_collapseParallaxMultiplier:設置視差滾動因子,值為:0~1,值越小,折疊效果就越明顯,需要搭配app:layout_collapseMode的parallax模式使用
好了,了解上面這些知識后,我們來做一個炫酷的頂部欄吧,這里我使用了Databinding來做了一個全新的布局:
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="adapter"
type="android.support.v7.widget.RecyclerView.Adapter" />
<variable
name="manager"
type="android.support.v7.widget.RecyclerView.LayoutManager" />
</data>
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.fritz.layout.MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="200dp"
app:contentScrim="?attr/colorPrimaryDark"
app:expandedTitleMarginEnd="20dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:setCollapsedTitleTextColor="@{@color/color_while}"
app:setExpandedTitleTextColor="@{@color/colorAccent}">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/ic_bg"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.5" />
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="50dp"
app:layout_collapseMode="pin"
app:navigationIcon="@drawable/ic_action_back"
app:title="OverLoad" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:setAdapter="@{adapter}"
app:setLayoutManager="@{manager}" />
</android.support.design.widget.CoordinatorLayout>
</layout>
我個人是比較喜歡DataBinding的,很多邏輯可以在xml解決,程序猿偷懶的好幫手啊。這里說明一下上面的 app:setCollapsedTitleTextColor 和 app:setExpandedTitleTextColor 這兩個屬性,其實這里DataBinding查找類里面對應的公共方法,這兩個方法前者用于修改標題收縮后的顏色,后者用于修改標題展開后的顏色。
由于在toolbar里面設置的標題,所以CollapsingToolbarLayout會自動獲取toolbar的標題來作為自己的標題的。我們來看下效果吧:
總結
這里我只是簡單介紹了CoordinatorLayout的一些常見的屬性和功能,如果大家希望了解更多的東西,可以去閱讀官方的文檔:官方文檔地址
這里說句題外話,CoordinatorLayout這個控件實現的效果很炫酷,它里面的NestedScrolling機制也讓人耳目一新,推薦大家都去仔細了解一下這個NestedScrolling機制,相信能從中受益不少。