CoordinatorLayout中的Behavior主要用来实现控件之间的交互以及滑动交互。常见的Behavior有”@string/appbar_scrolling_view_hehavior” 以及 “@string/bottom_sheet_behavior” ,这两个是Design库自带的Behavior。我们自己也可以根据需要自定义Behavior,来实现漂亮的交互。

 自定义Behavior可以分为两类,一类是定义控件监听CoordinatorLayout的滑动状态;一类是定义控件监听另一个控件的状态变化。

要自定义Behavior首先继承CoordinatorLayout.Behavior类,并实现构造方法。如下:

public class FootBehavior extends CoordinatorLayout.Behavior<View> {

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

然后根据需要重写相应的方法。

1. 监听CoordinatorLayout的滑动状态

 要监听CoordinatorLayout的滑动状态,主要注意两个方法:onStartNestedScroll() 和 onNestedPreScroll() 。

/**
     * 当CoordinatorLayout开始滑动时会调用这个方法,任意与CoordinatorLayout通过behavior关联的控件都要响应此方法并返回true
     * 如果返回false,控件将不会响应CoordinatorLayout的滑动事件。
     * @param coordinatorLayout
     * @param child     与CoordinatorLayout通过behavior关联的控件
     * @param directTargetChild
     * @param target
     * @param nestedScrollAxes  滑动方向,ViewCompat.SCROLL_AXIS_VERTICAL表示纵向滑动
     *                          ViewCompat.SCROLL_AXIS_HORIZONTAL表示横向滑动
     * @return
     */
    @Override
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
        //响应CoordinatorLayout的纵向滑动
        return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;
    }

    /**
     *  CoordinatorLayout滑动时调用此方法
     * @param coordinatorLayout
     * @param child     与CoordinatorLayout通过behavior关联的子控件
     * @param target
     * @param dx        手指水平方向滑动的距离,左滑dx>0 右滑dx<0
     * @param dy        手指竖直方法滑动的距离,上滑dy>0 下滑dy<0
     * @param consumed  实际已滑动的距离,consumed[0]表示水平距离,consumed[1]表示竖直距离;
     *                 实际已滑动的距离总是小于或等于手指滑动的距离
     */
    @Override
    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
        if(dy>MIN_SCROLL_DISTANCE && isVisible && !isAnimating){ //如果向上滑动,则隐藏底部控件
            hideBottomView(child);
        }else if(dy<-MIN_SCROLL_DISTANCE && !isVisible && !isAnimating){ //如果向下滑动,则显示底部控件
            showBottomView(child);
        }
    }

 这里我在CoordinatorLayout底部放了一个布局,希望CoordinatorLayout向上滑动的时候,底部布局隐藏起来;当CoordinatorLayout向下滑动的时候,底部布局显示出来。其中MIN_SCROLL_DISTANCE是自定义的一个常量,避免响应距离过小的滑动。isVisible用来标识当前底部布局的显示状态,还有isAnimating就不说了,文章最后将给出完整的代码。

2. 一个控件监听另一个控件的状态变化

 一个控件监听另一个控件的状态变化,实际上就是要将这两个控件关联起来,当一个控件变动时,另一个控件也发生状态变化。这里主要也涉及到两个方法:layoutDependsOn() 以及 onDependentViewChanged() 。

/**
     * 使一个控件与目标控件关联,如果要响应目标控件的变化,就返回true,否则返回false
     * @param parent
     * @param child         子控件
     * @param dependency    被关联的目标控件,可以通过类型或者id来确定目标控件
     * @return
     */
    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
        //return dependency instanceof Button;
        return dependency.getId() == R.id.button;
    }

    /**
     * 当目标控件状态发生状态变化时(比如位置和大小),会调用此方法
     * @param parent
     * @param child
     * @param dependency
     * @return
     */
    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
        //子控件的位置随着目标控件位置变化
        child.setX(dependency.getX());
        child.setY(dependency.getY() + dependency.getHeight() + 30);
        return true;
    }

自定义Behavior完成之后,就可以愉快地使用了:

<FrameLayout
        app:layout_behavior=".FootBehavior"
        android:layout_gravity="bottom"
        android:background="@color/colorPrimary"
        android:padding="15dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="自定义Behavior"
            android:textColor="@android:color/white"
            android:textSize="17sp"
            android:layout_gravity="center"/>

    </FrameLayout>

要查看完整代码点这里: TestBehavior @github