Material Design 控件知識(shí)梳理(2) - AppBarLayout & CollapsingToolbarLayout

Material Design 控件知識(shí)梳理(1) - Android Design Support Library 是什么
Material Design 控件知識(shí)梳理(2) - AppBarLayout & CollapsingToolbarLayout
Material Design 控件知識(shí)梳理(3) - BottomSheet && BottomSheetDialog && BottomSheetDialogFragment
Material Design 控件知識(shí)梳理(4) - FloatingActionButton
Material Design 控件知識(shí)梳理(5) - DrawerLayout && NavigationView
Material Design 控件知識(shí)梳理(6) - Snackbar
Material Design 控件知識(shí)梳理(7) - BottomNavigationBar
Material Design 控件知識(shí)梳理(8) - TabLayout
Material Design 控件知識(shí)梳理(9) - TextInputLayout

一、概述

在某些App當(dāng)中,我們經(jīng)常會(huì)見(jiàn)到類似于下面的這種設(shè)計(jì):


這種設(shè)計(jì)的目的是為了在初始時(shí)刻展示重要的信息,但是當(dāng)用戶需要查看列表的時(shí)候,不至于被封面占據(jù)過(guò)多的可視的區(qū)域,Google為這種設(shè)計(jì)提供了幾個(gè)類,讓我們只用實(shí)現(xiàn)很少的代碼就能夠?qū)崿F(xiàn)這種效果,避免了我們通過(guò)去監(jiān)聽(tīng)列表的滾動(dòng)狀態(tài)來(lái)去改變頭部區(qū)域的顯示,這篇文章,我們就一步步來(lái)學(xué)習(xí)如何實(shí)現(xiàn)這種效果。
要實(shí)現(xiàn)上面這種效果,需要對(duì)下面幾方面的知識(shí)有所了解:

  • CoordinatorLayout
  • AppBarLayout
  • CollapsingToolbarLayout
  • 實(shí)現(xiàn)了NestedScrollingChild接口的滾動(dòng)布局,例如RecyclerViewNestedScrollView

這四者的關(guān)系可以用下面這張圖來(lái)表示:


其中CollapsingToolbarLayout需要依賴于AppBarLayout,因此我打算把文章的討論分為兩部分:

  • 只采用AppBarLayout實(shí)現(xiàn)
  • 采用AppBarLayout + CollapsingToolbarLayout實(shí)現(xiàn)

二、只采用AppBarLayout實(shí)現(xiàn)

當(dāng)只采用AppBarLayout時(shí),我們的布局層次一般是下面這樣:

2.1 CoordinatorLayout

CoordinatorLayout是一個(gè)重寫(xiě)的ViewGroup,它負(fù)責(zé)AppBarLayout以及可滾動(dòng)布局之間的關(guān)系,因此,它是作為這兩者的直接父控件。

2.2 AppBarLayout下的列表

一般是RecyclerView或者ViewPager,為了能讓它和AppBarLayout協(xié)同工作,需要給列表控件添加下面這個(gè)屬性,這樣列表就會(huì)顯示在AppBarLayout的下方

app:layout_behavior="@string/appbar_scrolling_view_behavior"

2.3 AppBarLayout的標(biāo)志位

需要AppBarLayout作為CoordinatorLayout的直接子View,同時(shí)它也是一個(gè)LinearLayout,因此它的所有子View都是線性排列的,而它對(duì)子View的滾動(dòng)顯示管理是通過(guò)子Viewapp:layout_scrollFlags屬性,注意,這個(gè)標(biāo)志位是在AppBarLayout的子View中聲明的,而不是AppBarLayout

app:layout_scrollFlags的值有下面五種可選:

  • scroll
  • exitUntilCollapsed
  • enterAlways
  • enterAlwaysCollapsed
  • snap

2.3.1 scroll

使得AppBarLayout的子View伴隨著滾動(dòng)而收起或者展開(kāi),有兩點(diǎn)需要注意:

  • 這個(gè)標(biāo)志位是其它四個(gè)標(biāo)志位生效的前提
  • 帶有scroll標(biāo)志位的子View必須放在AppBarLayout中的最前面

現(xiàn)在我們給RecyclerView設(shè)置了app:layout_behavior,而AppBarLayout中的兩個(gè)子View都沒(méi)有設(shè)置scroll標(biāo)志位:

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.design.widget.AppBarLayout
        android:id="@+id/al_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:gravity="center"
            android:text="layout_scrollFlags=scroll"
            android:textColor="@android:color/white"
            android:background="@android:color/holo_green_dark"
            android:layout_width="match_parent"
            android:layout_height="100dp"/>
        <TextView
            android:gravity="center"
            android:text="沒(méi)有設(shè)置layout_scrollFlags"
            android:textColor="@android:color/white"
            android:background="@android:color/holo_orange_dark"
            android:layout_width="match_parent"
            android:layout_height="100dp"/>
    </android.support.design.widget.AppBarLayout>
    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>

此時(shí)AppBarLayout中的子View都一直固定在CoordinatorLayout的頂部:


下面,我們給AppBarLayout中的第一個(gè)子View加上app:layout_scrollFlags="scroll|exitUntilCollapsed"標(biāo)志位:

 <android.support.design.widget.AppBarLayout
        android:id="@+id/al_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:gravity="center"
            android:text="layout_scrollFlags=scroll"
            android:textColor="@android:color/white"
            android:background="@android:color/holo_green_dark"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            app:layout_scrollFlags="scroll"/>  //注意看這里!
        <TextView
            android:gravity="center"
            android:text="沒(méi)有設(shè)置layout_scrollFlags"
            android:textColor="@android:color/white"
            android:background="@android:color/holo_orange_dark"
            android:layout_width="match_parent"
            android:layout_height="100dp"/>
</android.support.design.widget.AppBarLayout>

那么此時(shí)滑動(dòng)情況為:

  • 設(shè)置了scroll的子View可以在滾動(dòng)后收起,而沒(méi)有設(shè)置的則不可以。
  • 在手指向上移動(dòng)的時(shí)候,優(yōu)先收起AppBarLayout中的可收起View,當(dāng)它處于收起狀態(tài)時(shí),下面的列表內(nèi)容才開(kāi)始向尾部滾動(dòng)。
  • 在手指往下移動(dòng)的時(shí)候,優(yōu)先讓下面的列表內(nèi)容向頂部滾動(dòng),當(dāng)列表滾動(dòng)到頂端時(shí),AppBarLayout的可收起View才展開(kāi)。

2.3.2 exitUntilCollapsed

對(duì)于可收起的子View來(lái)說(shuō)有兩種狀態(tài):EnterCollapsed。手指向上移動(dòng)的時(shí)候,會(huì)優(yōu)先收起AppBarLayout中的子View,而收起的最小高度為0,假如希望它在收起時(shí)仍然保留部分可見(jiàn),那么就需要使用exitUntilCollapsed + minHeight屬性,minHeight就決定了收起時(shí)的最小高度是多少,也就是Collapsed狀態(tài)。

<android.support.design.widget.AppBarLayout
        android:id="@+id/al_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:gravity="center"
            android:text="layout_scrollFlags=scroll"
            android:textColor="@android:color/white"
            android:background="@android:color/holo_green_dark"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:minHeight="50dp"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"/>
        <TextView
            android:gravity="center"
            android:text="沒(méi)有設(shè)置layout_scrollFlags"
            android:textColor="@android:color/white"
            android:background="@android:color/holo_orange_dark"
            android:layout_width="match_parent"
            android:layout_height="100dp"/>
</android.support.design.widget.AppBarLayout>

2.3.3 enterAlways

exitUntilCollapsed決定了手指向上移動(dòng)時(shí)AppBarLayout怎么收起它的子View,而enterAlways則決定了手指向下移動(dòng)時(shí)的行為。默認(rèn)情況下,在手指向下移動(dòng)時(shí),會(huì)優(yōu)先讓列表滾動(dòng)到頂部,而如果設(shè)置了enterAlways,那么會(huì)優(yōu)先讓AppBarLayout中的子View滾動(dòng)到展開(kāi)。

<android.support.design.widget.AppBarLayout
        android:id="@+id/al_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <!-- 需要設(shè)置屬性 -->
        <TextView
            android:gravity="center"
            android:text="layout_scrollFlags=scroll|enterAlways"
            android:textColor="@android:color/white"
            android:background="@android:color/holo_green_dark"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            app:layout_scrollFlags="scroll|enterAlways"/>
        <TextView
            android:gravity="center"
            android:text="沒(méi)有設(shè)置layout_scrollFlags"
            android:textColor="@android:color/white"
            android:background="@android:color/holo_orange_dark"
            android:layout_width="match_parent"
            android:layout_height="100dp"/>
</android.support.design.widget.AppBarLayout>

2.3.4 enterAlwaysCollapsed

enterAlwaysCollapsed,enterAlways以及minHeight屬性一起配合使用,采用這個(gè)標(biāo)志位,主要是為了使得手指向下移動(dòng)時(shí)出現(xiàn)下面這個(gè)效果:

  • 優(yōu)先滾動(dòng)AppBarLayout中的子View,但是并不是滾動(dòng)到全部展開(kāi),而是只滾動(dòng)到minHeight
  • AppBarLayout中的子View滾動(dòng)到minHeight之后,開(kāi)始滾動(dòng)列表,直到列表滾動(dòng)到頭部
  • 列表滾動(dòng)到頭部之后,開(kāi)始滾動(dòng)AppBarLayout中的子View,直到它完全展開(kāi)

注意和exitUntilCollapsedminHeight區(qū)分開(kāi)來(lái);

  • enterAlways|enterAlwaysCollapsedminHeight,決定的是手指向下移動(dòng)且列表沒(méi)有滾動(dòng)到頂端最大高度
  • exitUntilCollapsedminHeight,決定的是手指向上移動(dòng)時(shí)的最小高度

這兩個(gè)標(biāo)志位和minHeight是相互依賴的關(guān)系,如果聲明對(duì)應(yīng)的標(biāo)志位,那么minHeight是沒(méi)有任何意義的;而如果只聲明了標(biāo)志位,沒(méi)有聲明minHeight,那么默認(rèn)minHeight0。

<android.support.design.widget.AppBarLayout
        android:id="@+id/al_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <!-- 需要設(shè)置屬性 -->
        <TextView
            android:gravity="center"
            android:text="layout_scrollFlags=scroll|enterAlways|enterAlwaysCollapsed, minHeight=50dp"
            android:textColor="@android:color/white"
            android:background="@android:color/holo_green_dark"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:minHeight="50dp"
            app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"/>
        <TextView
            android:gravity="center"
            android:text="沒(méi)有設(shè)置layout_scrollFlags"
            android:textColor="@android:color/white"
            android:background="@android:color/holo_orange_dark"
            android:layout_width="match_parent"
            android:layout_height="100dp"/>
</android.support.design.widget.AppBarLayout>

2.3.5 snap

默認(rèn)情況下,在手指從屏幕上抬起之后,AppBarLayout中子View的狀態(tài)就不會(huì)變化了,而如果我們?cè)O(shè)置了snap標(biāo)志位,那么在手指抬起之后,會(huì)根據(jù)子View當(dāng)前的偏移量,決定是讓它變?yōu)槭掌疬€是展開(kāi)狀態(tài)。

<android.support.design.widget.AppBarLayout
        android:id="@+id/al_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <!-- 需要設(shè)置屬性 -->
        <TextView
            android:gravity="center"
            android:text="layout_scrollFlags=scroll|snap"
            android:textColor="@android:color/white"
            android:background="@android:color/holo_green_dark"
            android:layout_width="match_parent"
            android:layout_height="100dp"
            app:layout_scrollFlags="scroll|snap"/>
        <TextView
            android:gravity="center"
            android:text="沒(méi)有設(shè)置layout_scrollFlags"
            android:textColor="@android:color/white"
            android:background="@android:color/holo_orange_dark"
            android:layout_width="match_parent"
            android:layout_height="100dp"/>
</android.support.design.widget.AppBarLayout>

2.4 監(jiān)聽(tīng)AppBarLayout的滾動(dòng)狀態(tài)

AppBarLayout提供了監(jiān)聽(tīng)滾動(dòng)狀態(tài)的接口,我們可以根據(jù)這個(gè)偏移值來(lái)改變界面的狀態(tài):

private void setAppBar() {
        final TextView moveView = (TextView) findViewById(R.id.iv_move_title);
        AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.al_title);
        appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {

            @Override
            public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
                Log.d("AppBarLayout", "offset=" + verticalOffset);
                int height = moveView.getHeight();
                int minHeight = moveView.getMinHeight();
                float fraction = verticalOffset / (float) (minHeight - height);
                moveView.setAlpha(1 - fraction);
            }

        });
}

這里,我們根據(jù)offset的值來(lái)改變alpha,最終達(dá)到下面的效果:

scroll_7.gif

在手指往上移動(dòng)的過(guò)程當(dāng)中,verticalOffset0變?yōu)樨?fù)值:

2.6 AppBarLayout小結(jié)

AppBarLayout主要是掌握兩點(diǎn):

  • app:layout_scrollFlags幾種標(biāo)志位之前的區(qū)別
  • 如何監(jiān)聽(tīng)AppBarLayout的滾動(dòng)

掌握完這兩點(diǎn)之后,我們就可以自由發(fā)揮,做出自己想要的效果了。

三、AppBarLayoutCollapsingToolbarLayout結(jié)合

CollapsingToolbarLayout用于對(duì)Toolbar進(jìn)行包裝,因此,它是AppBarLayout的直接子View,同時(shí)也是Toolbar的直接父View,當(dāng)使用CollapsingToolbarLayout時(shí)我們的界面布局一般是這樣的:


CollapsingToolbarLayout的標(biāo)志比較多,而且需要和Toolbar相結(jié)合,下面,我們就以一種比較常見(jiàn)的例子,來(lái)講解幾個(gè)比較重要的標(biāo)志位,先看效果:

AppBarLayout的布局如下:

<android.support.design.widget.AppBarLayout
        android:id="@+id/al_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/ctl_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:contentScrim="@color/colorPrimaryDark"
            app:layout_scrollFlags="scroll|exitUntilCollapsed">
            <ImageView
                android:id="@+id/iv_title"
                android:src="@drawable/ic_bg"
                android:layout_width="match_parent"
                android:scaleType="centerCrop"
                android:layout_height="150dp"
                app:layout_collapseParallaxMultiplier="0.5"
                app:layout_collapseMode="parallax"/>
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:title="@string/app_name"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                app:title="Collapse"
                app:navigationIcon="@android:drawable/ic_media_play"
                app:layout_collapseMode="pin"/>
        </android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>

下面,我們就來(lái)介紹上面這個(gè)布局中比較重要的標(biāo)志位:

  • app:contentScrim
    設(shè)置收起之后CollapsingToolbarLayout的顏色,正如例子中的那樣,在收起之后,ImageView的背景被我們?cè)O(shè)置的contentScrim所覆蓋。
  • app:layout_scrollFlags="scroll|exitUntilCollapsed"
    scroll標(biāo)志位是必須設(shè)置的,exitUntilCollapsed保證了我們?cè)谑种干弦频臅r(shí)候,CollapsingToolbarLayout最多收起到Toolbar的高度,使得它始終保持可見(jiàn)。
  • app:layout_collapseMode="parallax"app:layout_collapseParallaxMultiplier="0.5"
    layout_collapseMode有兩種模式,parallax表示視差效果,簡(jiǎn)單地說(shuō),就是當(dāng)CollapsingToolbarLayout滑動(dòng)的距離不等于背景滑動(dòng)的距離,從而產(chǎn)生一種視差效果,而視差效果的大小由app:layout_collapseParallaxMultiplier決定。
  • app:layout_collapseMode="pin"
    layout_collapseMode的另一種模式,它使得Toolbar一直固定在頂端。

除了上面這幾個(gè)重要的標(biāo)志位之外,相信大家還發(fā)現(xiàn)了CollapsingToolbar在全部展開(kāi)的時(shí)候會(huì)把標(biāo)題設(shè)置為比較大,而當(dāng)上移時(shí),標(biāo)題慢慢縮小為Toolbar的標(biāo)題大小,并移動(dòng)到Toolbar的標(biāo)題所在位置。

四、總結(jié)

以上就是對(duì)于使用CoordinatorLayout、AppBarLayout、CollapsingToolbarLayout實(shí)現(xiàn)標(biāo)題跟隨列表滾動(dòng)的簡(jiǎn)要介紹,最主要的是掌握layout_scrollFlags幾種標(biāo)志之間的區(qū)別。

五、參考文獻(xiàn)

Material Design之 AppbarLayout 開(kāi)發(fā)實(shí)踐總結(jié)


更多文章,歡迎訪問(wèn)我的 Android 知識(shí)梳理系列:

最后編輯于
?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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