CoordinatorLayout實現了多種Material Design中提到的滾動效果。目前這個框架提供了幾種不用寫動畫代碼就能工作的方法,這些效果包括:
- 讓浮動操作按鈕上下滑動,為Snackbar留出空間。
- 擴大或縮小Toolbar或者頭部,讓主內容區域有更多的空間。
- 控制哪個view應該擴展還是收縮,以及其顯示大小比例,包括視差滾動效果動畫。
設置:
首先確保遵循了Design Support Library的使用說明。
浮動操作按鈕與Snackbar
CoordinatorLayout可以用來配合浮動操作按鈕的layout_anchor和layout_gravity屬性創造出浮動效果,詳情請參見浮動操作按鈕按鈕。
當Snackbar在顯示的時候,往往出現在屏幕的底部。為了給Snackbar留出空間,浮動操作按鈕需要向上移動。
只要使用CoordinatorLayout作為基本布局,將自動產生向上移動的動畫。浮動操作按鈕有一個 默認的 behavior來檢測Snackbar的添加并讓按鈕在Snackbar之上呈現上移與Snackbar等高的動畫。
<android.support.design.widget.CoordinatorLayout
android:id="@+id/main_content"
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.v7.widget.RecyclerView
android:id="@+id/rvToDoList"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_margin="16dp"
android:src="@mipmap/ic_launcher"
app:layout_anchor="@id/rvToDoList"
app:layout_anchorGravity="bottom|right|end"/>
</android.support.design.widget.CoordinatorLayout>
Toolbar的擴展與收縮
首先需要確保你不是使用已經過時的ActionBar。務必遵循 使用ToolBar作為actionbar這篇文章的指南。同樣,這里也需要CoordinatorLayout作為主布局容器。
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</android.support.design.widget.CoordinatorLayout>
響應滾動事件
接下來,我們必須使用一個容器布局:AppBarLayout來讓Toolbar響應滾動事件。
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="@dimen/detail_backdrop_height"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:fitsSystemWindows="true">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</android.support.design.widget.AppBarLayout>
注意:根據官方的谷歌文檔,AppBarLayout目前必須是第一個嵌套在CoordinatorLayout里面的子view。
然后,我們需要定義AppBarLayout與滾動視圖之間的聯系。在RecyclerView或者任意支持嵌套滾動的view比如NestedScrollView上添加app:layout_behavior。support library包含了一個特殊的字符串資源@string/appbar_scrolling_view_behavior,它和AppBarLayout.ScrollingViewBehavior相匹配,用來通知AppBarLayout 這個特殊的view何時發生了滾動事件,這個behavior需要設置在觸發事件(滾動)的view之上。
<android.support.v7.widget.RecyclerView
android:id="@+id/rvToDoList"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
當CoordinatorLayout發現RecyclerView中定義了這個屬性,它會搜索自己所包含的其他view,看看是否有view與這個behavior相關聯。AppBarLayout.ScrollingViewBehavior描述了RecyclerView與AppBarLayout之間的依賴關系。RecyclerView的任意滾動事件都將觸發AppBarLayout或者AppBarLayout里面view的改變。
AppBarLayout里面定義的view只要設置了app:layout_scrollFlags屬性,就可以在RecyclerView滾動事件發生的時候被觸發:
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways"/>
</android.support.design.widget.AppBarLayout>
app:layout_scrollFlags屬性里面必須至少啟用scroll這個flag,這樣這個view才會滾動出屏幕,否則它將一直固定在頂部。可以使用的其他flag有:
- enterAlways: 一旦向上滾動這個view就可見。
- enterAlwaysCollapsed: 顧名思義,這個flag定義的是何時進入(已經消失之后何時再次顯示)。假設你定義了一個最小高度(minHeight)同時enterAlways也定義了,那么view將在到達這個最小高度的時候開始顯示,并且從這個時候開始慢慢展開,當滾動到頂部的時候展開完。
- exitUntilCollapsed: 同樣顧名思義,這個flag時定義何時退出,當你定義了一個minHeight,這個view將在滾動到達這個最小高度的時候消失。
記住,要把帶有scroll flag的view放在前面,這樣收回的view才能正常退出,而固定的view繼續留在底部。
制造折疊效果
如果想制造Toolbar的折疊效果,我們必須把Toolbar放在CollapsingToolbarLayout中:
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginEnd="64dp"
app:expandedTitleMarginStart="48dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways"></android.support.v7.widget.Toolbar>
</android.support.design.widget.CollapsingToolbarLayout>
通常,我們呢都是設置Toolbar的title,而現在,我們需要把title設置在CollapsingToolBarLayout上,而不是Toolbar。
CollapsingToolbarLayout collapsingToolbar =
(CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar);
collapsingToolbar.setTitle("Title");
制造視差效果
CollapsingToolbarLayout還能讓我們做出更高級的動畫,比如在里面放一個ImageView,然后在它折疊的時候漸漸淡出。同時在用戶滾動的時候title的高度也會隨之改變。
為了制造出這種效果,我們添加了一個定義了app:layout_collapseMode = "parallax"屬性的ImageView
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginEnd="64dp"
app:expandedTitleMarginStart="48dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways"></android.support.v7.widget.Toolbar>
<ImageView
android:src="@drawable/cheese_1"
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax"
android:minHeight="100dp"/>
</android.support.design.widget.CollapsingToolbarLayout>
CoordinatorLayout的工作原理是搜索定義了CoordinatorLayout Behavior的子view,不管是通過在XML中使用app:layout_behavior標簽還是通過在代碼中對view類使用@DefaultBehavior修飾符來添加注解。當滾動發生的時候,CoordinatorLayout會嘗試觸發那些聲明了依賴的子view。
要自己定義CoordinatorLayout Behavior,你需要實現layoutDependsOn()和onDependentViewChanged()兩個方法。比如APPBarLayout.Behavior就定義了這兩個關鍵方法。這個behavior用于當滾動發生的時候讓APPBarLayout發生改變。
eg:Android CoordinatorLayout+AppBarLayout
向上滾動隱藏指定的View
實現效果:向下滾動RecyclerView,Tab會被隱藏,向上滾動RecyclerView,Tab恢復出現,這么做的好處在于,用戶能有更多的空間位置去看列表里面的內容。
<?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:orientation="vertical">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/third_activity_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"/>
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|enterAlways"
app:tabIndicatorColor="@color/medium_blue"
app:tabSelectedTextColor="@color/medium_blue"
app:tabTextAppearance="@style/TabText"
app:tabTextColor="@color/gray_text"/>
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>
1)首先需要用CoordinatorLayout包住AppBarLayout;
2)頂部區域的view都放在AppBarLayout里面
3)AppBarLayout外面,CoordinatorLayout里面,放一個帶有可滾動的View,如上的例子,放的是一個ViewPager,而ViewPager里面放了RecyclerView的,即是可以滾動的View。
4)在AppBarLayout里面的View,通過app:layout_scrollFlags屬性來控制,滾動的時候表現。其中有4中Flag的類型。
- scroll: this flag should be set for all views that want to scroll off the screen - for views that do not use this flag, they’ll remain pinned to the top of the screen
- enterAlways: this flag ensures that any downward scroll will cause this view to become visible, enabling the ‘quick return’ pattern
- enterAlwaysCollapsed: When your view has declared a minHeight and you use this flag, your View will only enter at its minimum height (i.e., ‘collapsed’), only re-expanding to its full height when the scrolling view has reached it’s top.
- exitUntilCollapsed: this flag causes the view to scroll off until it is ‘collapsed’ (its minHeight) before exiting
上面的例子種用的是 **scroll 和 enterAlways.
** Scroll 表示向下滾動時,這個View會被滾出屏幕范圍直到隱藏.
enterAlways 表示向上滾動時,這個View會隨著滾動手勢出現,直到恢復原來的位置.
- 在可以滾動的View上設置屬性app:layout_behavior.
該屬性的值實際上是一個完整的class名字,而上面例子中的 @string/appbar_scrolling_view_behavior 是Android Support Library 定義后的值,可以被直接使用.
這個Behavior的class是真正控制滾動時候View的滾動行為.我們也可以繼承Behavior這個class去實現特有的滾動行為.
6)代碼部分,只需要實現RecyclerView的邏輯就可以了。
CorordinatorLayout高級用法--自定義Behavior
在新的support design中,CoordinatorLayout可以說是最重要的一個控件了,CoordinatorLayout給我們帶來了一種新的處理方式--behavior,
eg:在使用CoordinatorLayout的時候,一些子View需要一段
app:layout_behavior=@"string/appbar_scrolling_view_behavior"
這樣的xml配置?其實這是一個類,而且還可以自定義!
認識behavior
behavior是CoordinatorLayout的一個抽象內部類
public abstract static class Behavior {
public Behavior(){
}
public Behavior(Context context, AttributeSet attrs) {
}
...
}
有一個泛型是指定我們應用這個Behavior的view的類型,例如上面的appbar_scrolling_view_behavior對應的字符串其實是android.support.design.widget.AppBarLayout$ScrollingViewBehavior,這個ScrollingViewBehavior內部類指定的泛型是View,所以理論上這個Behavior我們任何的View都可以使用,我們在自定義的時候,如果不是特殊的行為,也可以直接指定泛型View。
在自定義Behavior的時候,我們需要關心的兩組四個方法。
某個View監聽另一個view的狀態變化,例如大小、位置、顯示狀態等
某個view監聽CoordinatorLayout里的滑動狀態
對于第一種情況,我們關心的是:
layoutDependendsOn和onDependentViewChanged方法;
對于第二種情況,我們關心的是:
onStartNestedScroll和onNestedPreScroll方法。
Android Support Design 中 CoordinatorLayout 與 Behaviors 初探
CoordinatorLayout默認情況下可以理解是一個FrameLayout,它的布局方式默認是一層一層疊上去。
CoordinatorLayout的神奇之處就在于Behavior對象了。Behavior對象是用來給CoordinatorLayout的子view們進行交互用的。
Behavior接口擁有很多個方法,我們拿AppBarLayout為例。AppBarLayout中有兩個Behavior,一個是拿來給他自己用的,另一個是拿來給它的兄弟結點用的,我們重點關注下AppBarLayout.ScrollingViewBehavior這個類。
eg Android CollapsingToolbarLayout
大的Banner圖,能第一時間吸引用戶的眼球,用不一樣的Banner大圖更具個性化的展示內容.圖總是比文字要吸引人.
當向下滾動時,Banner大圖會跟隨滾動手勢而Collapse.最后收折成一個普通的ActionBar(實際是個Toolbar,Android官方在最新的Support Library都推薦把ActionBar替換成Toolbar).
通過屬性Flag的組合,也能實現把ActionBar直接推出屏幕,讓其消失
<?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:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginEnd="64dp"
app:expandedTitleMarginStart="48dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:id="@+id/backdrop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:layout_collapseMode="parallax"
android:scaleType="centerCrop"
android:src="@drawable/mu"
android:transitionName="mu"/>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:theme="@style/MyToolbarTheme"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
</android.support.design.widget.CoordinatorLayout>
這是Layout布局.CoordinatorLayout和AppBarLayout的組合在這篇隨筆中有介紹,實現了滾動隱藏Toolbar的效果,這里就不在重復了.
CollapsingToolbarLayout是實現GIF效果的關鍵.
CollapsingToolbarLayout有兩個Children.ImageView用來顯示Banner大圖,即Gif中曼聯隊徽的大圖.而Toolbar就是折疊后看到的頂欄Toolbar.
app:contentScrim="?attr/colorPrimary",CollapsingToolbarLayout這個屬性是設置折疊后Toolbar的顏色.
app:layout_scrollFlags="scroll|exitUntilCollapsed",這是兩個Flag控制滾動時候CollapsingToolbarLayout的表現.
- **Scroll, **表示向下滾動列表時候,CollapsingToolbarLayout會滾出屏幕并且消失(原文解釋:this flag should be set for all views that want to scroll off the screen - for views that do not use this flag, they’ll remain pinned to the top of the screen)
- **exitUntilCollapsed, **表示這個layout會一直滾動離開屏幕范圍,直到它收折成它的最小高度.(原文解釋:this flag causes the view to scroll off until it is ‘collapsed’ (its minHeight) before exiting)
app:layout_collapseMode="parallax",這是控制滾出屏幕范圍的效果的
1) parallax,表示滾動過程中,會一直保持可見區域在正中間.
2) pin,表示不會被滾出屏幕范圍.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fourth_activity);
final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
final CollapsingToolbarLayout collapsingToolbar = (CollapsingToolbarLayout) findViewById(
R.id.collapsing_toolbar);
collapsingToolbar.setTitle(getString(R.string.fourth_activity));
final RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(new MyAdapter(this));
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.mu);
Palette.from(bitmap).generate(new Palette.PaletteAsyncListener() {
@Override
public void onGenerated(final Palette palette) {
int defaultColor = getResources().getColor(R.color.medium_blue);
int defaultTitleColor = getResources().getColor(R.color.white);
int bgColor = palette.getDarkVibrantColor(defaultColor);
int titleColor = palette.getLightVibrantColor(defaultTitleColor);
collapsingToolbar.setContentScrimColor(bgColor);
collapsingToolbar.setCollapsedTitleTextColor(titleColor);
collapsingToolbar.setExpandedTitleColor(titleColor);
}
});
}
這是Activity的onCreate方法,有兩處地方需要關注的
- setSupportActionBar()方法,告訴AppCompatActivity哪一個是ActionBar(實際是Toolbar).不然的話,做透明Status Bar(電池,手機信號那一區域)效果時候,ActionBar會位置不正確.
- Palette,調色板的意思,也是Android Support Library提供的.用來抓取Bitmap的顏色.在此處的用處是,當ActionBar被收折后,背景顏色能保持和Banner大圖的色調一致,而Title文字的顏色保證和Banner大圖的色調形成強對比.
Android Design Support Library簡介
AppBarLayout
AppBarLayout是繼承LinearLayout實現的一個ViewGroup容器組件,它是為了Material Design設計的App Bar,支持手勢滑動操作。
默認的AppBarLayout是垂直方向的,它的作用是把AppBarLayout包裹的內容都作為AppBar.
此處將Toolbar和Tablayout組合部分共同構成AppBar的效果。
注意:AppBarLayout必須作為Toolbar的父布局容器。
AppBarLayout是支持手勢滑動效果的,不過要跟CoordinatorLayout配合使用。
CoordinatorLayout
CoordinatorLayout是一個增強型的FrameLayout。它的作用有兩個,作為一個布局的根布局。子視圖之間相互協調手勢效果的一個協調布局。
CoordinatorLayout作為“super-powered FrameLayout”基本實現兩個功能:
1、作為頂層布局
2、調度協調子布局
CoordinatorLayout使用新的思路通過協調調度子布局的形式實現觸摸影響布局的形式產生動畫效果。CoordinatorLayout通過設置子View的 Behaviors來調度子View。系統(Support V7)提供了AppBarLayout.Behavior, AppBarLayout.ScrollingViewBehavior, FloatingActionButton.Behavior, SwipeDismissBehavior<V extends View> 等。
總結: 為了使得Toolbar有滑動效果,必須做到如下三點:
CoordinatorLayout必須作為整個布局的父布局容器。 給需要滑動的組件設置 app:layout_scrollFlags=”scroll|enterAlways” 屬性。 給你的可滑動的組件,也就是RecyclerView 或者 NestedScrollView 設置如下屬性:
app:layout_behavior = @string/appbar_scrolling_view_behavior
3.給需要有折疊效果的組件設置 layout_collapseMode屬性。
NavigationView
用于側滑菜單中的menu布局。之前Google在V4包中推出自己的 DrawerLayout作為抽屜側滑菜單。其實這次谷歌只是將上面的ListView布局替換成NavigationView了。簡化了之前ListView寫適配器的繁瑣。
這里最主要的兩個屬性分別是:
1.app:headerLayout: 給NavigationView添加頭部布局2.app:menu:給NavigationView添加menu菜單布局
Snackbar
Snackbar提供了一個介于Toast和AlertDialog之間輕量級控件,它可以很方便的提供消息提示和動作反饋。
TextInputLayout
TextInputLayout作為一個父容器控件,包裝了新的EditText。通常,單獨的EditText會在用戶輸入第一個字母之后隱藏hint提示信息,但是現在你可以使用TextInputLayout 來將EditText封裝起來,提示信息會變成一個顯示在EditText之上的floating label,這樣用戶就始終知道他們現在輸入的是什么。同時,如果給EditText增加監聽,還可以給它增加更多的floating label。
Floating Action Button
floating action button 是一個負責顯示界面基本操作的圓形按鈕。Design library中的FloatingActionButton 實現了一個默認顏色為主題中colorAccent的懸浮操作按鈕
FloatingActionButton繼承自ImageView,你可以使用android:src或者ImageView的任意方法,比如setImageDrawable()來設置FloatingActionButton里面的圖標。
TabLayout
Design library的TabLayout 既實現了固定的選項卡 - view的寬度平均分配,也實現了可滾動的選項卡 - view寬度不固定同時可以橫向滾動。選項卡可以在程序中動態添加:
但大部分時間我們都不會這樣用,通常滑動布局都會和ViewPager配合起來使用,所以,我們需要ViewPager來幫忙:
通過一句話setupWithViewPager,我們就把ViewPager和TabLayout結合了起來。