CoordinatorLayout結合Behavior高級用法

在新的support design中,CoordinatorLayout可以說是最重要的一個控件了,CoordinatorLayout給我們帶來了一種新的事件的處理方式——behavior,你是不是還記得我們在使用CoordinatorLayout的時候,一些子view需要一段,

app:layout_behavior="@string/appbar_scrolling_view_behavior"
  • 1
  • 1

這樣的xml配置?當時我們不知道這是幹嘛的,直接照用就行了,後來發現這玩意是一個類!而且我們還可以自定義!所以,今天這篇博客我們首先來學習一下如何自定義Behavior,之後的博客可能會看一下CoordinatorLayout是怎麼處理這個Behavior的。

認識Behavior

Behavior是CoordinatorLayout的一個抽象內部類

public abstract static class Behavior<V extends View> {
      public Behavior() {
      }

      public Behavior(Context context, AttributeSet attrs) {
      }
      ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

有一個泛型是指定的我們應用這個Behavior的View的類型,例如上面的appbar_scrolling_view_behavior對應的字符串其實是Android.support.design.widget.AppBarLayout$ScrollingViewBehavior,這個ScrollingViewBehavior內部類指定的泛型是View,所以理論上這個Behavior我們任何的View都可以使用,我們在自定義的時候,如果不是特殊的行爲,也可以直接指定泛型View

在自定義Behavior的時候,我們需要關心的兩組四個方法,爲什麼分爲兩組呢?看一下下面兩種情況

  1. 某個view監聽另一個view的狀態變化,例如大小、位置、顯示狀態等
  2. 某個view監聽CoordinatorLayout裏的滑動狀態

對於第一種情況,我們關心的是:
layoutDependsOnonDependentViewChanged方法,
對於第二種情況,我們關心的是:
onStartNestedScrollonNestedPreScroll方法。
對於這幾個方法什麼意思,我們需要幹什麼,稍候我們就能瞭解到。

初步自定義

現在我們就來根據第一種情況嘗試自定義一個Behavior,這裏我們實現一個簡單的效果,讓一個View根據另一個View上下移動。
首先我們來自定義一個Behavior,起名爲DependentBehavior

public class DependentBehavior extends CoordinatorLayout.Behavior<View> {

    public DependentBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
        return super.layoutDependsOn(parent, child, dependency);
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
        ViewCompat.offsetLeftAndRight();
        return super.onDependentViewChanged(parent, child, dependency);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

注意一下,帶有參數的這個構造必須要重載,因爲在CoordinatorLayout裏利用反射去獲取這個Behavior的時候就是拿的這個構造。我們覆寫了兩個方法layoutDependsOnonDependentViewChanged,這兩個方法的參數都是一樣的,解釋一下,第一個不用說,就是當前的CoordinatorLayout,第二個參數是我們設置這個Behavior的View,第三個是我們關心的那個View。如何知道關心的哪個呢?layoutDependsOn的返回值決定了一切!

這裏我們關心一個TextView好了,所以layoutDependsOn可以這麼寫,

@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
    return dependency instanceof TextView;
}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

現在設置好了關心誰,接下來就是在這個View狀態發生變化的時候,我們現在的View該做些什麼了,恩,這裏肯定是在onDependentViewChanged做工作了。我們的任務就是獲取dependency距離底部的距離,並且設置給child,很簡單。

@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
    int offset = dependency.getTop() - child.getTop();
    ViewCompat.offsetTopAndBottom(child, offset);
    return true;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

首先我們先獲取兩個View的top值的差,然後讓child的位置位移一下就ok啦,如此簡單,那這個簡單的Behavior如何用呢?

<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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context="org.loader.mybehavior.MainActivity">

    <TextView
        android:id="@+id/depentent"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="#FFFF0000"
        android:gravity="center"
        android:textColor="@android:color/white"
        android:layout_gravity="top|left"
        android:text="depentent"/>

    <TextView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:background="#FF00FF00"
        android:gravity="center"
        android:textColor="@android:color/white"
        android:layout_gravity="top|right"
        app:layout_behavior="org.loader.mybehavior.DependentBehavior"
        android:text="auto"/>

</android.support.design.widget.CoordinatorLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

注意,第二個TextView我們設置了app:layout_behavior="org.loader.mybehavior.DependentBehavior"
值正好是我們定義的那個DependentBehavior

final TextView depentent = (TextView) findViewById(R.id.depentent);
depentent.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        ViewCompat.offsetTopAndBottom(v, 5);
    }
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在Activity中,我們每次點擊第一個TextView都會去改變一下它的位置,下面讓我們來看看另一個TextView的位置改變了沒有。

Scroll Behavior

在學會了如何自定義Behavior後,我們接着來實現上面說的第二種情況-滑動。爲了演示這種Behavior的定義,我們還是來做個無用功,讓一個ScrollView跟隨另一個ScrollView滑動。恩,先來看看效果吧,

從效果中我們可以看出,第二個ScrollView明顯是是在跟隨第一個進行滑動,現在就讓我們用自定義Behavior的形式實現它。
創建一個Behavior,起名叫ScrollBehavior,


public class ScrollBehavior extends CoordinatorLayout.Behavior<View> {

    public ScrollBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
        return super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
    }

    @Override
    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
        super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
    }

    @Override
    public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY) {
        return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

和你想的一樣,我們覆寫了onStartNestedScrollonNestedPreScroll方法,但是除了這兩個方法外,我們還覆寫了onNestedPreFling方法,這個方法是幹嘛的? 估計大家已經猜出來了,這裏是處理fling動作的,你想想,我們在滑動鬆開手的時候,ScrollView是不是還繼續滑動一會,那我們也需要讓跟隨的那個ScrollView也要繼續滑動一會,這種效果,onNestedPreFling就派上用場了。

好,接下來我們來實現代碼,首先來看看onStartNestedScroll,這裏的返回值表明這次滑動我們要不要關心,我們要關心什麼樣的滑動?當然是y軸方向上的。

@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
    return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

現在我們準備好了關心的滑動事件了,那如何讓它滑動起來呢?還是要看onNestedPreScroll的實現

@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
    super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
    int leftScrolled = target.getScrollY();
    child.setScrollY(leftScrolled);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

也很簡單,讓child的scrollY的值等於目標的scrollY的值就ok啦,那fling呢?更簡單,

@Override
public boolean onNestedFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY, boolean consumed) {
    ((NestedScrollView) child).fling((int)velocityY);
    return true;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

直接將現在的y軸上的速度傳遞傳遞給child,讓他fling起來就ok了。
定義好了Behavior,就得在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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">

    <android.support.v4.widget.NestedScrollView
        android:layout_gravity="left"
        android:layout_width="wrap_content"
        android:background="#FF00FF00"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="50dp"
                android:paddingBottom="50dp"
                android:textColor="@android:color/white"
                android:text="contentLeft"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="50dp"
                android:paddingBottom="50dp"
                android:textColor="@android:color/white"
                android:text="contentLeft"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="50dp"
                android:paddingBottom="50dp"
                android:textColor="@android:color/white"
                android:text="contentLeft"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="50dp"
                android:paddingBottom="50dp"
                android:textColor="@android:color/white"
                android:text="contentLeft"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="50dp"
                android:paddingBottom="50dp"
                android:textColor="@android:color/white"
                android:text="contentLeft"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="50dp"
                android:paddingBottom="50dp"
                android:textColor="@android:color/white"
                android:text="contentLeft"/>


        </LinearLayout>

    </android.support.v4.widget.NestedScrollView>

    <android.support.v4.widget.NestedScrollView
        android:layout_gravity="right"
        android:layout_width="wrap_content"
        android:background="#FFFF0000"
        android:layout_height="match_parent"
        app:layout_behavior="org.loader.mybehavior.ScrollBehavior">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="50dp"
                android:paddingBottom="50dp"
                android:textColor="@android:color/white"
                android:text="contentRight"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="50dp"
                android:paddingBottom="50dp"
                android:textColor="@android:color/white"
                android:text="contentRight"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="50dp"
                android:paddingBottom="50dp"
                android:textColor="@android:color/white"
                android:text="contentRight"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="50dp"
                android:paddingBottom="50dp"
                android:textColor="@android:color/white"
                android:text="contentRight"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="50dp"
                android:paddingBottom="50dp"
                android:textColor="@android:color/white"
                android:text="contentRight"/>
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingTop="50dp"
                android:paddingBottom="50dp"
                android:textColor="@android:color/white"
                android:text="contentRight"/>

        </LinearLayout>

    </android.support.v4.widget.NestedScrollView>

</android.support.design.widget.CoordinatorLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131

第二個ScrollView的layout_behavior我們指定爲org.loader.mybehavior.ScrollBehavior,現在就可以看到上面的效果了。

ok, 最後是文章中demo的代碼下載:http://download.csdn.net/detail/qibin0506/9352989

發佈了3 篇原創文章 · 獲贊 19 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章