【知識必備】一文讓你搞懂design設計的CoordinatorLayout和AppbarLayout聯動,讓Design設計更簡單~


一、寫在前面

  其實博主在之前已經對design包的各個控件都做了博文說明,無奈個人覺得理解不夠深入,所以有了這篇更加深入的介紹,希望各位看官拍磚~

二、從是什么開始

  1、首先我們得知道CoordinatorLayout是什么玩意兒,到底有什么用,我們不妨看看官方文檔的描述:   

    CoordinatorLayout是一個“加強版”FrameLayout,它主要有兩個用途:

      1、用作應用的頂層布局管理器,也就是作為用戶界面中所有UI控件的容器

      2、用作相互之間具有特定交互行為的UI控件的容器

    通過為CoordinatorLayout的子View指定Behavior,就可以實現它們之間的交互行為。 Behavior可以用來實現一系列的交互行為和布局變化,比如說側滑菜單、可滑動刪除的UI元素,以及跟隨着其他UI控件移動的按鈕等。

  其實總結出來就是coordinatorLayout是一個布局管理器,相當於一個增強版的FrameLayout,但是它神奇在於可以實現它的子View之間的交互行為。

  2、交互行為?

    先看個簡單的效果圖

    

    可能大家看到這,就自然能想到觀察者模式,或者我昨日寫的Rx模式:知識必備】RxJava+Retrofit最佳結合體驗,打造懶人封裝框架~

           我們的Button就是一個被觀察者,TextView作為一個觀察者,當Button移動的時候通知TextView,TextView就跟着移動。看看其布局:  

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <android.support.design.widget.CoordinatorLayout
 3     xmlns:android="http://schemas.android.com/apk/res/android"
 4     xmlns:tools="http://schemas.android.com/tools"
 5     xmlns:app="http://schemas.android.com/apk/res-auto"
 6     android:id="@+id/activity_coordinator"
 7     android:layout_width="match_parent"
 8     android:layout_height="match_parent"
 9     tools:context="com.nanchen.coordinatorlayoutdemo.CoordinatorActivity">
10 
11     <TextView
12         android:layout_width="wrap_content"
13         android:layout_height="wrap_content"
14         android:text="觀察者"
15         app:layout_behavior=".FollowBehavior"/>
16 
17     <Button
18         android:layout_width="wrap_content"
19         android:layout_height="wrap_content"
20         android:text="被觀察者"
21         android:layout_gravity="center"
22         android:id="@+id/btn"/>
23 
24 </android.support.design.widget.CoordinatorLayout>

    很簡單,一個TextView,一個Button,外層用CoordinatorLayout,然后給我們的TextView加一個自定義的Behavior文件,內容如下:  

 1 package com.nanchen.coordinatorlayoutdemo;
 2 
 3 import android.content.Context;
 4 import android.support.design.widget.CoordinatorLayout;
 5 import android.util.AttributeSet;
 6 import android.view.View;
 7 import android.widget.Button;
 8 import android.widget.TextView;
 9 
10 /**
11  *
12  * 自定義CoordinatorLayout的Behavior,泛型為觀察者View(要跟着別人動的那個)
13  *
14  * 必須重寫兩個方法,layoutDependOn和onDependentViewChanged
15  *
16  * @author nanchen
17  * @fileName CoordinatorLayoutDemo
18  * @packageName com.nanchen.coordinatorlayoutdemo
19  * @date 2016/12/13  10:13
20  */
21 
22 public class FollowBehavior extends CoordinatorLayout.Behavior<TextView>{
23 
24     /**
25      * 構造方法
26      */
27     public FollowBehavior(Context context, AttributeSet attrs) {
28         super(context, attrs);
29     }
30 
31     /**
32      * 判斷child的布局是否依賴dependency
33      *
34      * 根據邏輯來判斷返回值,返回false表示不依賴,返回true表示依賴
35      *
36      * 在一個交互行為中,dependent view的變化決定了另一個相關View的行為。
37      * 在這個例子中,Button就是dependent view,因為TextView跟隨着它。
38      * 實際上dependent view就相當於我們前面介紹的被觀察者
39      *
40      */
41     @Override
42     public boolean layoutDependsOn(CoordinatorLayout parent, TextView child, View dependency) {
43         return dependency instanceof Button;
44     }
45 
46     @Override
47     public boolean onDependentViewChanged(CoordinatorLayout parent, TextView child, View dependency) {
48         child.setX(dependency.getX());
49         child.setY(dependency.getY() + 100);
50         return true;
51     }
52 }

    重點看看其中重寫的兩個方法layoutDependsOn()和onDependentViewChanged()。在介紹這兩個方法的作用前,我們先來介紹一下dependent view。在一個交互行為中,dependent view的變化決定了另一個相關View的行為。在這個例子中,Button就是dependent view,因為TextView跟隨着它。實際上dependent view就相當於我們前面介紹的被觀察者。知道了這個概念,讓我們看看重寫的兩個方法的作用:

  • layoutDependsOn():這個方法在對界面進行布局時至少會調用一次,用來確定本次交互行為中的dependent view,在上面的代碼中,當dependency是Button類的實例時返回true,就可以讓系統知道布局文件中的Button就是本次交互行為中的dependent view。

  • onDependentViewChanged():當dependent view發生變化時,這個方法會被調用,參數中的child相當於本次交互行為中的觀察者,觀察者可以在這個方法中對被觀察者的變化做出響應,從而完成一次交互行為。

  所以我們現在可以開始寫Activity中的代碼: 

findViewById(R.id.btn).setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                if (motionEvent.getAction() == MotionEvent.ACTION_MOVE){
                    view.setX(motionEvent.getRawX()-view.getWidth()/2);
                    view.setY(motionEvent.getRawY()-view.getHeight()/2);
                }
                return true;
            }
        }); 

  這樣一來,我們就完成了為TextView和Button設置跟隨移動這個交互行為。很簡單有木有,其實為CoordinatorLayout的子View設置交互行為只需三步:

  • 自定義一個繼承自Behavior類的交互行為類;

  • 把觀察者的layout_behavior屬性設置為自定義行為類的類名;

  • 重寫Behavior類的相關方法來實現我們想要的交互行為。 

值得注意的是,有些時候,並不需要我們自己來定義一個Behavior類,因為系統為我們預定義了不少Behavior類。在接下來的篇章中,我們會做出進一步的介紹。

  3、更進一步

  現在我們已經知道了怎么通過給CoordinatorLayout的子View設置Behavior來實現交互行為。現在,讓我們更進一步地挖掘下CoordinatorLayout,深入了解一下隱藏在表象背后的神秘細節。

  實際上,CoordinatorLayout本身並沒有做過多工作,實現交互行為的主要幕后推手是CoordinatorLayout的內部類——Behavior。通過為CoordinatorLayout的直接子View綁定一個Behavior,這個Behavior就會攔截發生在這個View上的Touch事件、嵌套滾動等。不僅如此,Behavior還能攔截對與它綁定的View的測量及布局。關於嵌套滾動,我們會在后續文章中進行詳細介紹。下面我們來深入了解一下Behavior是如何做到這一切的。

  4、深入理解Behavior

    a) 攔截Touch事件

    當我們為一個CoordinatorLayout的直接子View設置了Behavior時,這個Behavior就能攔截發生在這個View上的Touch事件,那么它是如何做到的呢?實際上,CoordinatorLayout重寫了onInterceptTouchEvent()方法,並在其中給Behavior開了個后門,讓它能夠先於View本身處理Touch事件。具體來說,CoordinatorLayout的onInterceptTouchEvent()方法中會遍歷所有直接子View,對於綁定了Behavior的直接子View調用Behavior的onInterceptTouchEvent()方法,若這個方法返回true,那么后續本該由相應子View處理的Touch事件都會交由Behavior處理,而View本身表示懵逼,完全不知道發生了什么。

 

    b)攔截測量及布局

    了解了Behavior是怎養攔截Touch事件的,想必大家已經猜出來了它攔截測量及布局事件的方式——CoordinatorLayout重寫了測量及布局相關的方法並為Behavior開了個后門。沒錯,真相就是如此。

    CoordinatorLayout在onMeasure()方法中,會遍歷所有直接子View,若該子View綁定了一個Behavior,就會調用相應Behavior的onMeasureChild()方法,若此方法返回true,那么CoordinatorLayout對該子View的測量就不會進行。這樣一來,Behavior就成功接管了對View的測量。

    同樣,CoordinatorLayout在onLayout()方法中也做了與onMeasure()方法中相似的事,讓Behavior能夠接管對相關子View的布局。

 

    c) view的依賴關系的確定

    現在,我們在探究一下交互行為中的兩個View之間的依賴關系是怎么確定的。我們稱child為交互行為中根據另一個View的變化做出響應的那個個體,而dependent view為child所依賴的View。實際上,確立child和dependent view的依賴關系有兩種方式:

  • 顯式依賴:為child綁定一個Behavior,並在Behavior類的layoutDependsOn()方法中做手腳。即當傳入的dependency為dependent view時返回true,這樣就建立了child和dependent view之間的依賴關系。

  • 隱式依賴:通過我們最開始提到的錨(anchor)來確立。具體做法可以這樣:在XML布局文件中,把child的layout_anchor屬性設為dependent view的id,然后child的layout_anchorGravity屬性用來描述為它想對dependent view的變化做出什么樣的響應。關於這個我們會在后續篇章給出具體示例。 

  無論是隱式依賴還是顯式依賴,在dependent view發生變化時,相應Behavior類的onDependentViewChanged()方法都會被調用,在這個方法中,我們可以讓child做出改變以響應dependent view的變化。

 

三、玩轉AppBarLayout

  實際上我們在應用中有CoordinatorLayout的地方通常都會有AppBarLayout的聯用,作為同樣的出自Design包的庫,我們看看官方文檔怎么說:

  AppBarLayout是一個垂直的LinearLayout,實現了Material Design中app bar的scrolling gestures特性。AppBarLayout的子View應該聲明想要具有的“滾動行為”,這可以通過layout_scrollFlags屬性或是setScrollFlags()方法來指定。
  AppBarLayout只有作為CoordinatorLayout的直接子View時才能正常工作,
  為了讓AppBarLayout能夠知道何時滾動其子View,我們還應該在CoordinatorLayout布局中提供一個可滾動View,我們稱之為scrolling view。scrolling view和AppBarLayout之間的關聯,通過將scrolling view的Behavior設為AppBarLayout.ScrollingViewBehavior來建立。

  1、一般怎么用?

  AppBar是Design的一個概念,其實我們也可以把它看做一種5.0出的ToolBar,先感受一下AppBarLayout+CoordinatorLayout的魅力。

  

  實際效果就是這樣,當向上滑動View的時候,ToolBar會小時,向下滑動的時候,ToolBar又會出現,但別忘了,這是AppBarLayout的功能,ToolBar可辦不到。由於要滑動,那么我們的AppBarLayout一定是和可以滑動的View一起使用的,比如RecyclerView,ScollView等。

  我們看看上面的到底怎么實現的:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <android.support.design.widget.CoordinatorLayout
 3     xmlns:android="http://schemas.android.com/apk/res/android"
 4     xmlns:tools="http://schemas.android.com/tools"
 5     android:id="@+id/activity_coor_app_bar"
 6     xmlns:app="http://schemas.android.com/apk/res-auto"
 7     android:layout_width="match_parent"
 8     android:layout_height="match_parent"
 9     tools:context="com.nanchen.coordinatorlayoutdemo.CoorAppBarActivity">
10 
11     <android.support.design.widget.AppBarLayout
12         android:layout_width="match_parent"
13         android:layout_height="wrap_content"
14         app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
15 
16         <android.support.v7.widget.Toolbar
17             android:id="@+id/toolbar"
18             android:layout_width="match_parent"
19             android:layout_height="?attr/actionBarSize"
20             app:layout_scrollFlags="scroll|enterAlways">
21 
22         </android.support.v7.widget.Toolbar>
23     </android.support.design.widget.AppBarLayout>
24 
25     <android.support.v7.widget.RecyclerView
26         android:layout_width="match_parent"
27         android:layout_height="match_parent"
28         android:id="@+id/recycler"
29         app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
30 
31 </android.support.design.widget.CoordinatorLayout>

  我們可以看到,上面出現了一個app:layouy_scrollFrags的自定義屬性設置,這個屬性可以定義我們不同的滾動行為。

  2、layout_scrollFlags

  根據官方文檔,layout_scrollFlags的取值可以為以下幾種。

    a)  scroll

    設成這個值的效果就好比本View和scrolling view是“一體”的。具體示例我們在上面已經給出。有一點特別需要我們的注意,為了其他的滾動行為生效,必須同時指定scroll和相應的標記,比如我們想要exitUntilCollapsed所表現的滾動行為,必須將layout_scrollFlags指定為“scroll|exitUntilCollapsed”。

    b)  exitUntilCollapsed

  當本View離開屏幕時,會被“折疊”直到達到其最小高度。我們可以這樣理解這個效果:當我們開始向上滾動scrolling view時,本View會先接管滾動事件,這樣本View會先進行滾動,直到滾動到了最小高度(折疊了),scrolling view才開始實際滾動。而當本View已完全折疊后,再向下滾動scrolling view,直到scrolling view頂部的內容完全顯示后,本View才會開始向下滾動以顯現出來。

    c) enterAlways

    當scrolling view向下滾動時,本View會一起跟着向下滾動。實際上就好比我們同時對scrolling view和本View進行向下滾動。  

    d)  enterAlwaysCollapsed

從名字上就可以看出,這是在enterAlways的基礎上,加上了“折疊”的效果。當我們開始向下滾動scrolling view時,本View會一起跟着滾動直到達到其“折疊高度”(即最小高度)。然后當scrolling view滾動至頂部內容完全顯示后,再向下滾動scrolling view,本View會繼續滾動到完全顯示出來。  

    e)  snap

    在一次滾動結束時,本View很可能只處於“部分顯示”的狀態,加上這個標記能夠達到“要么完全隱藏,要么完全顯示”的效果。

 

四、CollapsingToolBarLayout

  這個東西,我相信很多博客和技術文章都會把CollapsingToolBarLayout和CoordinatorLayout放一起講,這個東西的確很牛。我們同樣先看看官方文檔介紹:

  CollapsingToolbarLayout通常用來在布局中包裹一個Toolbar,以實現具有“折疊效果“”的頂部欄。它需要是AppBarLayout的直接子View,這樣才能發揮出效果。CollapsingToolbarLayout包含以下特性:

  1、Collasping title(可折疊標題):當布局完全可見時,這個標題比較大;當折疊起來時,標題也會變小。標題的外觀可以通過expandedTextAppearance和collapsedTextAppearance屬性來調整。

  2、Content scrim(內容紗布):根據CollapsingToolbarLayout是否滾動到一個臨界點,內容紗布會顯示或隱藏。可以通過setContentScrim(Drawable)來設置內容紗布。

  3、Status bar scrim(狀態欄紗布):也是根據是否滾動到臨界點,來決定是否顯示。可以通過setStatusBarScrim(Drawable)方法來設置。這個特性只有在Android5.0及其以上版本,我們設置fitSystemWindows為ture時才能生效。

  4、Parallax scrolling children(視差滾動子View):子View可以選擇以“視差”的方式來進行滾動。(視覺效果上就是子View滾動的比其他View稍微慢些)

  5、Pinned position children:子View可以選擇固定在某一位置上。

  上面的描述有些抽象,實際上對於Content scrim、Status bar scrim我們可以暫時予以忽略,只要留個大概印象待以后需要時再查閱相關資料即可。下面我們通過一個常見的例子介紹下CollapsingToolbarLayout的基本使用姿勢。

  我們來看看一個常用的效果:

  

  看看布局:

  

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <android.support.design.widget.CoordinatorLayout
 3     xmlns:android="http://schemas.android.com/apk/res/android"
 4     xmlns:app="http://schemas.android.com/apk/res-auto"
 5     xmlns:tools="http://schemas.android.com/tools"
 6     android:id="@+id/activity_coor_tool_bar"
 7     android:layout_width="match_parent"
 8     android:layout_height="match_parent"
 9     android:fitsSystemWindows="true"
10     tools:context="com.nanchen.coordinatorlayoutdemo.CoorToolBarActivity">
11 
12     <android.support.design.widget.AppBarLayout
13         android:id="@+id/appbar"
14         android:fitsSystemWindows="true"
15         android:layout_width="match_parent"
16         android:layout_height="wrap_content"
17         app:theme="@style/AppTheme.AppBarOverlay">
18 
19         <android.support.design.widget.CollapsingToolbarLayout
20             android:layout_width="match_parent"
21             android:layout_height="200dp"
22             app:contentScrim="@color/colorPrimary"
23             app:expandedTitleMarginStart="100dp"
24             app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
25             app:statusBarScrim="@android:color/transparent"
26             app:titleEnabled="false">
27 
28             <ImageView
29                 android:layout_width="match_parent"
30                 android:layout_height="match_parent"
31                 android:fitsSystemWindows="true"
32                 android:scaleType="centerCrop"
33                 android:src="@mipmap/logo"
34                 app:layout_collapseMode="parallax"
35                 app:layout_collapseParallaxMultiplier="0.6"/>
36 
37             <android.support.v7.widget.Toolbar
38                 android:id="@+id/toolbar"
39                 android:layout_width="match_parent"
40                 android:layout_height="?attr/actionBarSize"
41                 app:layout_collapseMode="pin"
42                 app:popupTheme="@style/AppTheme.PopupOverlay"
43                 app:title=""/>
44 
45         </android.support.design.widget.CollapsingToolbarLayout>
46 
47     </android.support.design.widget.AppBarLayout>
48 
49     <android.support.v7.widget.RecyclerView
50         android:layout_width="match_parent"
51         android:layout_height="match_parent"
52         android:id="@+id/recycler"
53         app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
54 
55     <TextView
56         android:id="@+id/toolbar_title"
57         android:layout_width="match_parent"
58         android:layout_height="?attr/actionBarSize"
59         android:layout_marginLeft="16dp"
60         android:layout_marginTop="-100dp"
61         android:alpha="0"
62         android:elevation="10dp"
63         android:gravity="center_vertical"
64         android:text="愛吖校推-你關注的,我們才推"
65         android:textColor="@android:color/white"
66         android:textSize="20sp"
67         android:textStyle="bold"
68         app:layout_behavior=".SimpleViewBehavior"
69         app:svb_dependOn="@id/appbar"
70         app:svb_dependType="y"
71         app:svb_targetAlpha="1"
72         app:svb_targetY="0dp"/>
73 
74     <android.support.design.widget.FloatingActionButton
75         android:id="@+id/fab"
76         android:layout_width="wrap_content"
77         android:layout_height="wrap_content"
78         android:layout_margin="16dp"
79         android:src="@mipmap/ic_start"
80         app:layout_anchor="@id/appbar"
81         app:layout_anchorGravity="bottom|right"/>
82 
83 </android.support.design.widget.CoordinatorLayout>

  我們在xml文件中為CollapsingToolBarLayout的layout_scrollFlags指定為“scroll|exitUntilCollapsed|snap”,這樣便實現了向上滾動的折疊效果。

  CollapsingToolbarLayout本質上同樣是一個FrameLayout,我們在布局文件中指定了一個ImageView和一個Toolbar。ImageView的layout_collapseMode屬性設為了parallax,也就是我們前面介紹的視差滾動;而Toolbar的layout_collaspeMode設為了pin,也就是Toolbar會始終固定在頂部。

 

五、寫在最后

  本次的design包下的CoordinatorLayout和AppBarLayout就講述到這里,后續還將持續更新,歡迎拍磚~

  查看源碼請移步Github:https://github.com/nanchen2251/CoordinatorAppBarDemo


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM