今天跟着大神的Blog來學一下CoordinatorLayout,因為有一個想要實作的效果。
很久之前用過這個Layout,然後現在忘光了,又不想直接抄,這邊直接寫個blog來總結一下。
包括用法和一些自定義的東西。
學習blog為:
CoordinatorLayout使用詳解: 打造折疊懸浮效果Android開發之CoordinatorLayout打造滑動越界彈性放大圖檔效果Android CoordinatorLayout之自定義Behavior
1、CoordinatorLayout的定義
它是Support.design包下一個非常核心的控件。直譯叫做 “布局協調者”,用來協調子控件的布局。
CoordinatorLayout的主要作用就是 頁面主題和頁面主要内容的炫酷互動效果
比如微信朋友圈,朋友圈主要内容和朋友圈的主題照片是由互動的。
2、CoordinatorLayout的最簡單例子
我們先來實作一個最簡單的CoordinatorLayout,再從間入深。
在導入了的design包後,我們可以使用CoordinatorLayout。
首先講一下,一般情況 CoordinatorLayout都會和AppBarLayout聯用,這樣才能實作标題欄的效果,如下圖所示:
最上面那一欄就是AppLayout,我們來看看XML布局:
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.v7.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我是 蓋爾·裡卡·德沃"
android:textColor="#ffffff"
android:textSize="15sp" />
</android.support.v7.widget.Toolbar>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
這裡有幾個注意的點:
- .
是AppBarLayout
的子布局,AppBarLayout可以給子布局一個CoordinatorLayout
的屬性,這個屬性和互動有關。app:layout_scrollFlags
- RecyclerView有一個
,這個屬性特别頂,它決定了整個頭部與内容的互動效果app:layout_behavior="@string/appbar_scrolling_view_behavior"
- AppBarLayout不一定要
,換成RelativeLayout或其他布局都可以。ToolbarLayout
而
AppBarLayout
它必須要作為
CoordinatorLayout
的子控件,不然它的
app:layout_scrollFlags
和其他的屬性将用不了
我們來研究一下layout_scrollFlags這個屬性:
3、app:layout_scrollFlags屬性
它是AppBarLayout的
它有下面幾個參數
-
scroll
這個View将會響應滾動事件
如果沒有設定這個屬性,被AppBarLayout包裹的内容将不會随着滑動隐藏在螢幕上部,而是一直顯示在Layout頂部。
-
snap
在Scroll滑動事件結束以前,如果這個View部分可見,則它會停留在最接近目前View的位置。
換言之,如果沒有設定這個屬性,被AppBarLayout包裹的View要麼全部可見,要麼全部隐藏
-
enteralways
下拉的時候,這個View也會跟着滑出
-
enterAlwaysCollapsed
下拉的時候,這個View也會跟着滑出,但是隻是顯示出折疊之後的高度
這個很形象,沒折疊的時候可以能是一個圖檔,折疊之後可能是一行文字、标題
-
snapMargins
不知道
-
exitUntilCollapsed
上拉的時候,這個View也會跟着滑動,直到變成折疊後的模樣
這些屬性可以直接在xml中使用,也可在在代碼中通過
setScrollFlags(int Flag)
來加。
了解之後,有沒有發現這個屬性很嗨吊。我們平時使用的App都會有這種互動。
我們來在AppLayout裡面加一張圖檔。然後改變一下屬性:
...
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<ImageView
android:layout_width="match_parent"
android:layout_height="200dp"
android:scaleType="fitXY"
android:src="@drawable/ic_jojo3"
app:layout_scrollFlags="scroll|enterAlways" />
...
效果如下
發現這個沒有折疊效果,還是有點不是很好,那麼我們來看下和它有關的
CollapsingToolbarLayout
4、CollapsingToolbarLayout
它是一個包裝工具欄的Layout,它可以做到折疊标題、内容裝飾等功能。
它通常做為AppBarLayout的子View,為其子View設定了一個關鍵屬性:
collaspseMode
,它有這麼幾個值
-
pin
固定視圖,也就是說,不論這麼滑動,它都一定在,它就是折疊的樣子
-
parallax
這滑動的時候,這個view會出現 視覺特差的情況,什麼是視覺特差?
就是出現的時候是漸漸出來, 消失的時候漸漸消失的,不像剛剛那樣的圖檔消失的很僵硬。
-
none
不會顯示出折疊效果。
這裡直接拿來用:
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="200dp"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginEnd="64dp"
app:expandedTitleMarginStart="48dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/ic_jojo3"
app:layout_collapseMode="parallax" />
<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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="阿強"
android:textColor="#ffffff"
android:textSize="20sp" />
</android.support.v7.widget.Toolbar>
</android.support.design.widget.CollapsingToolbarLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="20dp"
android:text="我是 蓋爾·裡卡·德沃"
android:textColor="#ffffff"
android:textSize="15sp" />
</RelativeLayout>
</android.support.design.widget.AppBarLayout>
效果如下:
圖檔的消失就沒有那麼的僵硬,阿強所在的ToolBarLayout因為是pin的關系,已經被固定在了上面。
其實看到這裡,大家都肯定已經明白了,我這個效果,不就是模仿朋友圈的互動效果嗎?
我們在RecyclerView定義了它的
behavior
,這個Behavior其實也很關鍵,它是 CoordinatorLayout的精髓所在,是以學習CoordinatorLayout,不自定義一個behavior,那等于白學。
5、Behavior
我們給除了 AppBarLayout的控件添加了
app:layout_behavior="@string/appbar_scrolling_view_behavior"
當然除了這個值還有别的值,但這個用的比較多,和AppBarLayout配合的也是最好的。它的效果就是配合AppBarLayout來滑動。
因為Behavior是一個抽象類,是以我們可以自定義
Behavior
來達成我們想要的互動效果,我們來看下其樣子(必須是CoordinatorLayout的Behavior)因為有很多控件的包都有自己的Behavior - -!!
public abstract static class Behavior<V extends View> {
...
}
它的泛型是繼承自View的控件,也就是說隻要是控件,都能實作這些效果。我們來看下可以選擇重寫的方法:
-
是否攔截觸摸事件onInterceptTouchEvent()
-
處理觸摸事件onTouchEvent()
-
确定使用Behavior的View要依賴的View類型layoutDependsOn()
-
當被依賴的View狀态改變時的回調onDependentViewChanged()
-
當被依賴的View被移除時回調onDependentViewRemoved()
-
測量使用Behavior的View尺寸onMeasureChild()
-
确定使用Behavior的View的位置onLayoutChild
-
滑動開始,确定Behavior是否要監聽此次事件onStartNestedScroll()
-
嵌套滑動結束onStopNestedScroll()
-
滑動進行中,要監聽的子 View的滑動事件已經被消費onNestedScroll()
-
嵌套滑動進行中,要監聽的子 View将要滑動,滑動事件即将被消費(但最終被誰消費,可以通過代碼控制)onNestedPreScroll()
-
要監聽的子 View在快速滑動中onNestedFling()
-
要監聽的子View即将快速滑動onNestedPreFling()
學習了這些,我們來自定義一個Behavior
通常Behavior分為兩種情況。
- 某個View依賴另一個View,監聽器位置、尺寸等狀态的變化
- 某個View監聽CoordinatorLayout内實作了NestedScrollingChild接口的子View的滑動狀态變化
我們來實作一個:
public class MyBehavior extends CoordinatorLayout.Behavior<View> {
// 清單頂部和title底部重合時,清單的滑動距離。
private float deltaY;
public MyBehavior() {
}
public MyBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
//是否使用在Recycler上
@Override
public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull View child, @NonNull View dependency) {
return dependency instanceof RecyclerView;
}
@Override
public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull View child, @NonNull View dependency) {
if (deltaY == 0) {
deltaY = dependency.getY() - child.getHeight();
}
float dy = dependency.getY() - child.getHeight();
dy = dy < 0 ? 0 : dy;
float y = -(dy / deltaY) * child.getHeight();
float alpha = 1 - (dy / deltaY);
child.setAlpha(alpha);
child.setTranslationY(y);
return true;
}
}
上面的 layoutDependsOn和 onDependentViewChanged 是一定要重寫的。
child是我們要使用Behavior的View,dependency是依賴的View,我們把以來的View設定成RcyclerView。
然後在我們滑動RecyclerView的時候,來讓Child也移動,xml如下:
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<android.support.design.widget.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="200dp"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginEnd="64dp"
app:expandedTitleMarginStart="48dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/ic_jojo3"
app:layout_collapseMode="parallax" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<TextView
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="我是 蓋爾·裡卡·德沃"
android:background="#ff0000"
android:textColor="#ffffff"
app:layout_behavior="@string/behavior_mybehavior" />
</android.support.design.widget.CoordinatorLayout>
在String中設定:
<string name="behavior_mybehavior">com.rikkathewrold.myappbardemo.MyBehavior</string>
效果圖如下: