天天看點

國小CoordinatorLayout的互動

今天跟着大神的Blog來學一下CoordinatorLayout,因為有一個想要實作的效果。

很久之前用過這個Layout,然後現在忘光了,又不想直接抄,這邊直接寫個blog來總結一下。

包括用法和一些自定義的東西。

學習blog為:

​​​CoordinatorLayout使用詳解: 打造折疊懸浮效果​​​​Android開發之CoordinatorLayout打造滑動越界彈性放大圖檔效果​​​​Android CoordinatorLayout之自定義Behavior​​

1、CoordinatorLayout的定義

它是Support.design包下一個非常核心的控件。直譯叫做 “布局協調者”,用來協調子控件的布局。

CoordinatorLayout的主要作用就是 頁面主題和頁面主要内容的炫酷互動效果

比如微信朋友圈,朋友圈主要内容和朋友圈的主題照片是由互動的。

2、CoordinatorLayout的最簡單例子

我們先來實作一個最簡單的CoordinatorLayout,再從間入深。

在導入了的design包後,我們可以使用CoordinatorLayout。

首先講一下,一般情況 CoordinatorLayout都會和AppBarLayout聯用,這樣才能實作标題欄的效果,如下圖所示:

國小CoordinatorLayout的互動

最上面那一欄就是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​

    ​​是​

    ​CoordinatorLayout​

    ​​的子布局,AppBarLayout可以給子布局一個​

    ​app:layout_scrollFlags​

    ​的屬性,這個屬性和互動有關。
  • RecyclerView有一個​

    ​app:layout_behavior="@string/appbar_scrolling_view_behavior"​

    ​,這個屬性特别頂,它決定了整個頭部與内容的互動效果
  • AppBarLayout不一定要​

    ​ToolbarLayout​

    ​,換成RelativeLayout或其他布局都可以。

而​

​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" />

     ...      

效果如下

國小CoordinatorLayout的互動

發現這個沒有折疊效果,還是有點不是很好,那麼我們來看下和它有關的​

​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>      

效果如下:

國小CoordinatorLayout的互動

圖檔的消失就沒有那麼的僵硬,阿強所在的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()​

    ​ 處理觸摸事件
  • ​layoutDependsOn()​

    ​ 确定使用Behavior的View要依賴的View類型
  • ​onDependentViewChanged()​

    ​ 當被依賴的View狀态改變時的回調
  • ​onDependentViewRemoved()​

    ​ 當被依賴的View被移除時回調
  • ​onMeasureChild()​

    ​ 測量使用Behavior的View尺寸
  • ​onLayoutChild​

    ​ 确定使用Behavior的View的位置
  • ​onStartNestedScroll()​

    ​ 滑動開始,确定Behavior是否要監聽此次事件
  • ​onStopNestedScroll()​

    ​ 嵌套滑動結束
  • ​onNestedScroll()​

    ​ 滑動進行中,要監聽的子 View的滑動事件已經被消費
  • ​onNestedPreScroll()​

    ​ 嵌套滑動進行中,要監聽的子 View将要滑動,滑動事件即将被消費(但最終被誰消費,可以通過代碼控制)
  • ​onNestedFling()​

    ​ 要監聽的子 View在快速滑動中
  • ​onNestedPreFling()​

    ​ 要監聽的子View即将快速滑動

學習了這些,我們來自定義一個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>      

效果圖如下:

國小CoordinatorLayout的互動