Android 5.0新控件 CoordinatorLayout | 協調佈局 介紹及使用詳情
extends ViewGroup
implements NestedScrollingParent
CoordinatorLayout is a super-powered FrameLayout
CoordinatorLayout is intended for two primary use cases:
1.As a top-level application decor or chrome layout
2.As a container for a specific interaction with one or more child views
Google官方對這個控件的大概意思這是一個加強型的幀佈局,主要有兩個兩個用例方面:
1.作爲頂層佈局
2.調度協調子佈局
那麼到底CoordinatorLayout有什麼功能呢!說得再好不如來張圖
使用方法
CoordinatorLayout來自design兼容包,使用需要添加依賴。android studio 添加依賴如下:
dependencies {
compile ‘com.android.support:design:24.2.0‘
}
1.首先,XML中添加布局文件
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
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="wrap_content"
android:background="@color/colorTheme"
android:contentInsetStart="0dp"
app:layout_scrollFlags="scroll|enterAlways|snap">
<ImageView
android:id="@+id/imageview_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="20dp"
android:paddingLeft="5dp"
android:paddingRight="20dp"
android:paddingTop="20dp"
android:src="@drawable/back"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CoordinatorLayout"
android:textColor="@android:color/white"/>
</android.support.v7.widget.Toolbar>
</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>
2.僅僅這些佈局文件,就可以實現當RecyclerView向上滑動時Toolbar隱藏,向下滑動時Toolbar顯示,下面來介紹下其屬性
CoordinatorLayout一般直接作爲根佈局使用,否則會有一些功能失效
CoordinatorLayout的使用核心是Behavior,Behavior就是執行你定製的動作。在講Behavior之前必須先理解兩個概念:Child和Dependency,什麼意思呢?Child當然是子View的意思了,是誰的子View呢,當然是CoordinatorLayout的子View;其實Child是指要執行動作的CoordinatorLayout的子View。而Dependency是指Child依賴的View。比如上面的gif圖中,RecyclerView就是Child,Toolbar就是Dependency,因爲Toolbar的動作是依賴於RecyclerView。簡而言之,就是如果RecyclerView發生了變化,Toolbar就要相應發生變化。發生變化是具體發生什麼變化呢?這裏就要引入Behavior,Child發生變化的具體執行的代碼都是放在Behavior這個類裏面,一般用系統定義好的Behavor就可以(也可以自定義),這裏我們在RecyclerView裏面添加
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滾動事件發生的時候被觸發
app:layout_scrollFlags屬性裏面必須至少啓用scroll這個flag,這樣這個view纔會滾動出屏幕,否則它將一直固定在頂部。可以使用的其他flag有:
snap:當滑動停止時但未達到展開或者收縮的狀態,這個屬性會判斷距離誰最近,自動展開或者收縮(加上這個屬性效果比較順暢)
enterAlways: 一旦向上滾動這個view就可見
enterAlwaysCollapsed: 顧名思義,這個flag定義的是何時進入(已經消失之後何時再次顯示)。假設你定義了一個最小高度(minHeight)同時enterAlways也定義了,那麼view將在到達這個最小高度的時候開始顯示,並且從這個時候開始慢慢展開,當滾動到頂部的時候展開完。
exitUntilCollapsed: 同樣顧名思義,這個flag時定義何時退出,當你定義了一個minHeight,這個view將在滾動到達這個最小高度的時候消失。
更多方法請看官方文檔
自定義Behavior
爲了更好的理解,我將系統自帶的Behavior通過自定義來實現,同時再給FloatActionBuuton添加一個自定義的Behavior(沒有添加顯示和隱藏的動畫效果,看起來不是很美觀,但是重點不在這,對吧!嘿嘿),先看效果圖
1.首先,我們定義一個類,繼承CoordinatorLayout.Behavior,其中,泛型參數T是我們要執行動作的View類,也就是Child
- AppbarLayout的Behavior
public class AppBehavior extends CoordinatorLayout.Behavior<AppBarLayout> {
private int directionChange;
public AppBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
//判斷滑動的方向 返回垂直滑動
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes) {
return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
}
//根據滑動的距離顯示和隱藏 child
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed) {
if (dy > 0 && directionChange < 0 || dy < 0 && directionChange > 0) {
directionChange = 0;
}
directionChange += dy;
if (directionChange > child.getHeight() && child.getVisibility() == View.VISIBLE) {
child.setVisibility(View.GONE);
} else if (directionChange < 0 && child.getVisibility() == View.GONE) {
child.setVisibility(View.VISIBLE);
}
}
}
- Fab的Behavior和AppbarLayout的Behavior一樣,因爲泛型中的類型不同,所以還得再寫一遍
public class FabBehavior extends CoordinatorLayout.Behavior<FloatingActionButton> {
private int directionChange;
public FabBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
//判斷滑動的方向 返回垂直滑動
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View directTargetChild, View target, int nestedScrollAxes) {
return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
}
//根據滑動的距離顯示和隱藏 child
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View target, int dx, int dy, int[] consumed) {
if (dy > 0 && directionChange < 0 || dy < 0 && directionChange > 0) {
directionChange = 0;
}
directionChange += dy;
if (directionChange > child.getHeight() && child.getVisibility() == View.VISIBLE) {
child.setVisibility(View.GONE);
} else if (directionChange < 0 && child.getVisibility() == View.GONE) {
child.setVisibility(View.VISIBLE);
}
}
}
- 注意: public FabBehavior(Context context, AttributeSet attrs) {super(context, attrs);} 這個構造方法必須重寫,因爲CoordinatorLayout源碼中parseBehavior()函數中直接反射調用這個構造函數
2.在XML中給dependency添加對應的Behavior
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="com.coder.guoy.recyclerview.ui.coordinator.AppBehavior"><!--自定義的Behavior-->
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorTheme"
android:contentInsetStart="0dp">
<ImageView
android:id="@+id/imageview_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingBottom="20dp"
android:paddingLeft="5dp"
android:paddingRight="20dp"
android:paddingTop="20dp"
android:src="@drawable/back"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CoordinatorLayout"
android:textColor="@android:color/white"/>
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_marginBottom="40dp"
android:layout_marginRight="40dp"
android:backgroundTint="@color/red"
android:src="@drawable/back"
app:borderWidth="0dp"
app:elevation="10dp"
app:fabSize="auto"
app:layout_behavior="com.coder.guoy.recyclerview.ui.coordinator.FabBehavior"<!--自定義的Behavior-->
app:pressedTranslationZ="20dp"
app:rippleColor="@color/colorWrite"
/>
</android.support.design.widget.CoordinatorLayout>
OK,這就完成了自定義Behavior對AppbarLayout和Fab進行控制,不需要那些繁瑣的監聽和Touch事件,這裏主要講怎樣自定義Behavior,並沒有對UI效果進行友善的處理,如果對你有幫助,Do it by yourself!
- 注意:自定義的Behavior添加需要填寫絕對路徑(包名+類名)
Behavior中更多方法請看官方文檔
總結
我們要明確三個概念及他們之間的關係:
Behavior(行爲),child(子View:)和dependency(從屬者:根據子view的改變執行相應的行爲)
從屬者添加定義好的行爲,當子view發生變化時,從屬者按照定義好的行爲去發生變化,
完整代碼點我下載
Thank you
- 以上僅本人學習中遇到的問題,如有更多意見歡迎隨時交流 issues
- email:[email protected]