CoordinatorLayout打造酷炫的頂部欄

CoordinatorLayout是在 Google IO/15 大會(huì)發(fā)布的控件,常常與AppBarLayout和CollapsingToolbarLayout一起使用,用于打造各種炫酷效果的頂部欄。

使用前須知

CoordinatorLayout目前我僅在RecyclerView、NestedScrollView、ViewPager上面成功通過設(shè)置behavior來監(jiān)聽滑動(dòng)事件,ListView和ScrollView都是不行的。什么原因,我們來看下CoordinatorLayout的源碼:

圖片.png

可以看到CoordinatorLayout繼承了NestedScrollingParent接口,那么再看下RecyclerView和NestedScrollView吧:

圖片.png

這樣子想必都猜到是什么回事了。
Android 在發(fā)布 Lollipop版本之后,Google為Android的滑動(dòng)機(jī)制提供了NestedScrolling機(jī)制,為了給用戶帶來更好的體驗(yàn)。而CoordinatorLayout正是基于NestedScrolling來開發(fā)的。
NestedScrolling機(jī)制可以簡(jiǎn)單理解為child將它的一些滑動(dòng)事件交給了Parent去處理了。ListView和ScrollView無法和CoordinatorLayout配合使用的原因就是因?yàn)樗鼈儧]繼承這個(gè)NestedScrollingChild接口。那么ViewPager可以配合使用的原因是什么?查看ViewPager的繼承關(guān)系會(huì)發(fā)現(xiàn)它沒有繼承這個(gè)Child接口,但是我們?nèi)タ聪耾nInterceptTouchEvent方法里面,會(huì)看到有下面的注釋:

@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;
}
..............

翻譯過來大意是這個(gè)方向有嵌套的View在滾動(dòng),就讓它在那里處理吧。換言之,就是ViewPager檢測(cè)到NestedScrolling相關(guān)的事件觸發(fā),就不做攔截,讓外層的CoordinatorLayout成功監(jiān)聽到ViewPager嵌套的RecyclerViewNestedScrollView的滑動(dòng)事件。
如果你對(duì)NestedScrolling機(jī)制很感興趣,這里推薦下面的文章:
Android NestedScrolling機(jī)制完全解析 帶你玩轉(zhuǎn)嵌套滑動(dòng)
Android 嵌套滑動(dòng)機(jī)制(NestedScrolling)
Android NestedScrolling 實(shí)戰(zhàn)

接下來是本文的重點(diǎn)了。

AppBarLayout

剛才已經(jīng)提過了,CoordinatorLayout是經(jīng)常與AppBarLayout配合使用的,所以很有必要搞明白AppBarLayout到底能做哪些事情。
AppBarLayout它繼承LinearLayout,布局方向默認(rèn)為垂直方向,它可以讓你定制RecyclerView、NestedScrollView、ViewPager的滾動(dòng)手勢(shì)發(fā)生變化時(shí),其內(nèi)部的子View實(shí)現(xiàn)某個(gè)指定的動(dòng)作。
目前布局代碼如下,這里先用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="待補(bǔ)充"
                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"這個(gè)屬性,就是這個(gè)屬性讓AppBarLayout與RecyclerView之間動(dòng)作相互依賴,這也是Google幫我們定義好了一個(gè)behavior,我們直接用就是了。
AppBarLayout內(nèi)部的子View通過在布局中加app:layout_scrollFlags設(shè)置執(zhí)行的動(dòng)作,那么app:layout_scrollFlags可以設(shè)置哪些動(dòng)作呢?我們一一來看吧,下面的View值的是AppBarLayout里面的子View:

  • 1.scroll: 想要當(dāng)滑出屏幕的時(shí)候View都必須設(shè)置這個(gè)flag,沒有設(shè)置這個(gè)flag的View將被固定在屏幕頂部,其他的flag屬性也無法生效
GIF.gif
  • 2.enterAlways:flag設(shè)為scroll|enterAlways的View,當(dāng)RecyclerView上的手指往上滾動(dòng)時(shí),該View會(huì)直接往上滾動(dòng)移出屏幕;當(dāng)RecyclerView上的手指往下滾動(dòng)時(shí),該View會(huì)立刻往下滾動(dòng)恢復(fù)原位
1211.gif
  • 3.exitUntilCollapsed:flag設(shè)為scroll|exitUntilCollapsed的View,當(dāng)這個(gè)View要往上逐漸滑出屏幕時(shí),會(huì)一直往上滑動(dòng),直到剩下的的高度達(dá)到它的最小高度后,再響應(yīng)RecyclerView的內(nèi)部滑動(dòng)事件。請(qǐng)注意,如果沒有設(shè)置android:minHeight的話,那么這個(gè)屬性是不會(huì)生效的。這里我將toolbar的android:layout_height設(shè)為200dp,android:minHeight設(shè)為50dp,效果如下:
xxx.gif
  • 4.enterAlwaysCollapsed:這個(gè)是enterAlways的附加選項(xiàng),要跟enterAlways一起使用,同時(shí)需要設(shè)置android:minHeight屬性,否則不起作用的。它是指,當(dāng)RecyclerView上的手指往下滾動(dòng)時(shí),View首先是enterAlways效果,當(dāng)View的高度達(dá)到最小高度時(shí),View就暫時(shí)不去往下滾動(dòng),直到某個(gè)可滾動(dòng)View滑動(dòng)到頂部并停止滑動(dòng)時(shí),View再繼續(xù)往下滑動(dòng),直到滑到View的頂部結(jié)束。為了更好展示這個(gè)效果,我將toolbar的android:layout_height設(shè)為200dp,android:minHeight設(shè)為50dp,效果如下:
GIF.gif
  • 5.snap:這個(gè)和上面的一樣,不會(huì)單獨(dú)使用,常與exitUntilCollapsed配合使用。在滾動(dòng)結(jié)束后,如果View只是部分可見,它將滑動(dòng)到最近的邊界。比如,如果View的底部只有25%可見時(shí)停止滑動(dòng),它將繼續(xù)滑動(dòng)直至離開屏幕,而如果底部有75%可見時(shí)停止滑動(dòng),它將繼續(xù)滑動(dòng)到完全顯示。
GIF.gif

好了,AppBarLayout相關(guān)功能到這里說完了,其實(shí)用起來還是蠻簡(jiǎn)單的。接著,到另一位主角CollapsingToolbarLayout的出場(chǎng)了。

CollapsingToolbarLayout

CollapsingToolbarLayout是用來對(duì)Toolbar進(jìn)行再次包裝的FrameLayout,主要是用于實(shí)現(xiàn)折疊(個(gè)人覺得收縮比較貼實(shí))的AppBar效果。它需要放在AppBarLayout布局里面,并且作為AppBarLayout的直接子View。它常用的有以下屬性:

  • 1.app:contentScrim:CollapsingToolbarLayout折疊(收縮)完成后的背景顏色
  • 2.app:expandedTitleMarginXXX:設(shè)置CollapsingToolbarLayout展開時(shí)候title的Margin
  • 3.app:layout_collapseMode:這個(gè)是讓CollapsingToolbarLayout的子View設(shè)置的,共有3個(gè)flag可以使用,它們分別為:
    1) parallax:設(shè)置為這個(gè)flag時(shí)為視差模式,也就是子View折疊模式。在RecyclerView等View在滑動(dòng)時(shí),子View以視差(折疊)的方式來滑動(dòng)
    2)pin:設(shè)置為這個(gè)flag時(shí)為固定模式,在折疊的時(shí)候會(huì)固定在頂端
    3)none:設(shè)置為這個(gè)flag時(shí)為沒有效果,RecyclerView往上滑動(dòng)的時(shí)候,子View會(huì)首先被固定并推出去
  • 4.app:layout_collapseParallaxMultiplier:設(shè)置視差滾動(dòng)因子,值為:0~1,值越小,折疊效果就越明顯,需要搭配app:layout_collapseMode的parallax模式使用
    好了,了解上面這些知識(shí)后,我們來做一個(gè)炫酷的頂部欄吧,這里我使用了Databinding來做了一個(gè)全新的布局:
<?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>

我個(gè)人是比較喜歡DataBinding的,很多邏輯可以在xml解決,程序猿偷懶的好幫手啊。這里說明一下上面的 app:setCollapsedTitleTextColor 和 app:setExpandedTitleTextColor 這兩個(gè)屬性,其實(shí)這里DataBinding查找類里面對(duì)應(yīng)的公共方法,這兩個(gè)方法前者用于修改標(biāo)題收縮后的顏色,后者用于修改標(biāo)題展開后的顏色。
由于在toolbar里面設(shè)置的標(biāo)題,所以CollapsingToolbarLayout會(huì)自動(dòng)獲取toolbar的標(biāo)題來作為自己的標(biāo)題的。我們來看下效果吧:

voer.gif

總結(jié)

這里我只是簡(jiǎn)單介紹了CoordinatorLayout的一些常見的屬性和功能,如果大家希望了解更多的東西,可以去閱讀官方的文檔:官方文檔地址
這里說句題外話,CoordinatorLayout這個(gè)控件實(shí)現(xiàn)的效果很炫酷,它里面的NestedScrolling機(jī)制也讓人耳目一新,推薦大家都去仔細(xì)了解一下這個(gè)NestedScrolling機(jī)制,相信能從中受益不少。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容