如果你想了解Behavior,可以移步另一篇文章
A super-powered FrameLayout—協(xié)調(diào)布局CoordinatorLayout(二):Behavior
一、CoordinatorLayout介紹
CoordinatorLayout is a super-powered FrameLayout.
CoordinatorLayout is intended for two primary use cases:
1. As a top-level application decor or chrome layout
(作為頂級應(yīng)用程序或者框架布局)
2. As a container for a specific interaction with one or more child views
(作為與一個或者多個child view 進(jìn)行特定交互的容器)
By specifying Behaviors for child views of a CoordinatorLayout you can provide many different interactions within a single parent and those views can also interact with one another. View classes can specify a default behavior when used as a child of a CoordinatorLayout using the CoordinatorLayout.DefaultBehavior annotation.
(通過為 CoordinatorLayout 的子view指定 Behaviors,您可以在單個父視圖中提供很多不同的交互,并且這些視圖也可以相互交互。當(dāng)視圖類用作 CoordinatorLayout 的子項時,可以使用CoordinatorLayout.DefaultBehavior 注釋指定默認(rèn)行為)
Behaviors may be used to implement a variety of interactions and additional layout modifications ranging from sliding drawers and panels to swipe-dismissable elements and buttons that stick to other elements as they move and animate.
(Behaviors可以提供很多很多牛批的交互,炫酷的效果)
Children of a CoordinatorLayout may have an anchor. This view id must correspond to an arbitrary descendant of the CoordinatorLayout, but it may not be the anchored child itself or a descendant of the anchored child. This can be used to place floating views relative to other arbitrary content panes.
Children can specify CoordinatorLayout.LayoutParams.insetEdge to describe how the view insets the CoordinatorLayout. Any child views which are set to dodge the same inset edges by CoordinatorLayout.LayoutParams.dodgeInsetEdges will be moved appropriately so that the views do not overlap.
二、基本使用
1. 配合AppBarLayout使用
1.1關(guān)于layout_scrollFlags屬性的解釋
layout_scrollFlags中的屬性可以相互結(jié)合使用,用于實現(xiàn)不同的效果
注意:exitUntilCollapsed、enterAlways、 enterAlwaysCollapsed 、snap、snapMargins必須結(jié)合scroll使用才會生效
| 屬性名 | xml | 說明 |
|---|---|---|
| SCROLL_FLAG_NO_SCROLL | noScroll | 禁用視圖上的滾動。此標(biāo)志不應(yīng)與任何其他滾動標(biāo)志結(jié)合使用 |
| SCROLL_FLAG_SCROLL | scroll | 視圖將與滾動事件直接相關(guān)。需要設(shè)置此標(biāo)志才能使任何其他標(biāo)志生效。如果在此之前的任何兄弟視圖沒有此標(biāo)志,則此值無效 |
| SCROLL_FLAG_EXIT_UNTIL_COLLAPSED | exitUntilCollapsed | 視圖滾動時,目標(biāo)視圖將折疊,折疊高度由視圖的最小高度決定,當(dāng)滾動視圖到頂部時展開 |
| SCROLL_FLAG_ENTER_ALWAYS | enterAlways | 隨著上下滾動事件,立即折疊或展開 |
| SCROLL_FLAG_ENTER_ALWAYS_COLLAPSED | enterAlwaysCollapsed | 初始狀態(tài)下隨著向上滾動開始折疊,向下滾動時到達(dá)初始狀態(tài)后開始展開 |
| SCROLL_FLAG_SNAP | snap | 在滾動結(jié)束時,如果視圖只是部分可見,那么它將被捕捉并滾動到它最近的邊緣 |
| SCROLL_FLAG_SNAP_MARGINS | snapMargins | 與 'snap' 一起使用的附加標(biāo)志。如果設(shè)置,視圖將捕捉到其頂部和底部邊距,而不是視圖本身的邊緣 |
1.2 layout_scrollFlags的對應(yīng)效果
文字的描述始終是沒有看圖來的直接,先貼布局
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.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=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:minHeight="50dp"
android:src="@mipmap/test"
app:layout_scrollFlags="noScroll" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:gravity="center_horizontal"
android:lineSpacingExtra="20dp"
android:text="@string/it_is_a_man"
android:textSize="23sp" />
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
1.2.1 layout_scrollFlags 之 noScroll
圖片不會隨著的滾動做出任何變化

1.2.2 layout_scrollFlags 之 scroll
圖片好像是嵌套在NestedScrollView頂部一樣,隨著滾動改變了原本的位置

1.2.3 layout_scrollFlags 之 scroll|exitUntilCollapsed
- 向上開始滑動時,圖片開始隨著滑動向上滑動,直到達(dá)到設(shè)置ImageView的最小高度時(我這里設(shè)置的是50dp),圖片不會再隨著滑動繼續(xù)滑動
-
向下滑動時(注意細(xì)節(jié)),圖片不會隨著滑動立馬展開,而是當(dāng)NestedScrollView滑動到頂部時,繼續(xù)向下滑動,此時圖片才會隨著滑動展開
scroll|exitUntilCollapsed.gif
1.2.4 layout_scrollFlags 之 scroll| enterAlways
- 向上開始滑動時,圖片開始隨著滑動向上滑動,直到完全離開可視區(qū)域,無視最小高度的屬性
-
向下滑動時(注意細(xì)節(jié)),圖片會立馬開始跟隨滑動展開,直到圖片完整展開時,NestedScrollView中的內(nèi)容才會隨著繼續(xù)滑動展示
scroll|enterAlways.gif
1.2.5 layout_scrollFlags 之 scroll| enterAlwaysCollapsed
- 向上開始滑動時,圖片開始隨著滑動向上滑動,直到完全離開可視區(qū)域,無視最小高度的屬性
-
向下滑動時(注意細(xì)節(jié)),圖片不會隨著滑動立馬展開,而是當(dāng)NestedScrollView滑動到頂部時,繼續(xù)向下滑動,此時圖片才會隨著滑動展開
scroll|enterAlwaysCollapsed.gif
1.2.6 layout_scrollFlags 之 scroll|snap
當(dāng)手指一直處于屏幕上滑動,效果和scroll| enterAlwaysCollapsed完全相同,它倆的區(qū)別在于,當(dāng)在滑動的過程中圖片還可見時手指離開屏幕的效果
- 當(dāng)圖片滑動未超過圖片高度的1/2時,回彈到全部展開狀態(tài)
-
當(dāng)超過1/2時,自動滑動到全部收縮狀態(tài)
scroll|snap.gif
1.2.7 layout_scrollFlags 之 scroll|snap| snapMargins
和scroll|snap基本一樣,唯一的區(qū)別是,自動滑動到的并非是圖片的頂部或者底部,而是會加上marginTop或者marginBottom的距離。
修改xml,給ImageView增加margin屬性
// 增加margin屬性
...
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:minHeight="50dp"
android:src="@mipmap/test"
android:layout_marginVertical="30dp"
app:layout_scrollFlags="scroll|snap|snapMargins" />
...

到此AppBarLayout的layout_scrollFlags屬性值對應(yīng)的不同的效果就介紹完了,下面再介紹一下,經(jīng)常和CoordinatorLayout&AppBarLayout一起使用的另外一個控件CollapsingToolbarLayout(折疊工具欄布局)
2. 配合CollapsingToolbarLayout使用
CollapsingToolbarLayout是一個實現(xiàn)了折疊app bar的wrapper(包裝器),它只能用作AppBarLayout的直接子View —— 來自官方類說明(靈魂翻譯)
2.1 CollapsingToolbarLayout這廝的layout_collapseMode屬性也有flag
- none —— view將正常運行,沒有折疊行為
- pin —— view將固定在那個位置
- parallax —— view將以視差方式滾動
xml布局文件如下??
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.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=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:src="@mipmap/test"
app:layout_collapseMode="parallax" />
<View
android:layout_width="match_parent"
android:layout_height="?android:actionBarSize"
android:background="@mipmap/test"
app:layout_collapseMode="pin"/>
<!-- <androidx.appcompat.widget.Toolbar-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="?android:actionBarSize"-->
<!-- app:layout_collapseMode="pin"-->
<!-- android:background="@mipmap/test"/>-->
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:gravity="center_horizontal"
android:lineSpacingExtra="20dp"
android:text="@string/it_is_a_man"
android:textSize="23sp" />
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
2.1.1 layout_collapseMode之none、pin
經(jīng)不完全測試,pin值只對Toolbar控件設(shè)置起作用,其他控件設(shè)置none或者pin沒啥區(qū)別(結(jié)論草率,細(xì)糾大佬還請賜教)


2.1.1 layout_collapseMode之parallax
以視差方式滾動,仔細(xì)看,隨著滑動,并不是單純的上下一起折疊/展開,整體圖片還伴隨的滑動,這種視差效果可以通過layout_collapseParallaxMultiplier屬性來設(shè)置,取值[0,1],默認(rèn)值為0.5

隨著CollapsingToolbarLayout簡單介紹完畢,CoordinatorLayout+ AppBarLayout + CollapsingToolbarLayout +Toolbar +NestedScrollingChild(經(jīng)常使用的NestedScrollView或RecyclerView),這一套最基礎(chǔ)的搭配使用就基本介紹完畢了。
3 應(yīng)用場景
有哪些應(yīng)用場景呢,這個只能說仁者見仁了。
-
CollapsingToolbarLayout配合Toolbar,在詳情頁里比較常見; -
還有常用到的是在頁面滑動過程中,有些控件需要懸停在頂部,比如懸停TabLayout,方便用戶切換tab
應(yīng)用場景
4 彩蛋總是在最后
4.1 重中之重——Behavior
當(dāng)你看完前面的內(nèi)容,感覺自己又可以了的時候,我要把你拉回來,那些只是皮毛上的毛皮,只是最最基本的使用。
思考一個問題,為什么使用CoordinatorLayout做為父布局,子View之間就會產(chǎn)生相互作用,這到底是誰在搞事情,它不是CoordinatorLayout,而是Behavior,理論上CoordinatorLayout的直接子view必須設(shè)置Behavior,這樣才能實現(xiàn)子view間的相互作用;當(dāng)然了你也可以不設(shè)置,那它就是個傻傻的控件。
看xml布局的<androidx.core.widget.NestedScrollView ...有個layout_behavior屬性,設(shè)置的是@string/appbar_scrolling_view_behavior
<string name="appbar_scrolling_view_behavior" translatable="false">com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior</string>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
...
那AppBarLayout并沒有設(shè)置layout_behavior屬性,為什么可以隨著滑動做出相應(yīng)的效果
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
...
莫要著急,點進(jìn)去,看看AppBarLayout的實現(xiàn),當(dāng)看到implements CoordinatorLayout.AttachedBehavior,一切都很明朗了
public class AppBarLayout
extends LinearLayout
implements CoordinatorLayout.AttachedBehavior {
...
4.2 總結(jié):設(shè)置Behavior的幾種方式
- 像
AppBarLayout一樣,View直接實現(xiàn)CoordinatorLayout.AttachedBehavior - 通過
CoordinatorLayout.LayoutParams的setBehavior(···)方法設(shè)置 - (常用)新建子類繼承自
CoordinatorLayout.Behavior,重寫其中的方法,通過layout_behavior屬性設(shè)置
通過layout_behavior屬性設(shè)置分2種方式
- 直接設(shè)置該子類的全路徑包名,例如
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
...
- 將該子類的全路徑包名寫到string.xml中,通過@string/xxxx的方式設(shè)置
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
...
關(guān)于重寫CoordinatorLayout.Behavior,我決定再開一篇單獨的文章。
本身這篇文章篇幅已經(jīng)很長了,再者我也累了,正兒八經(jīng)寫文章實則不易。
如果文章對你有幫助,點個贊再走唄
如果文章中存在錯誤,還望評論區(qū)指出
一起成長,共同進(jìn)步




