前面我们简单的介绍过CoordinatorLayout这个控件,这一篇我们详细地介绍这个ASD库中最重要的控件。
概述
在我们上一篇文章《AndroidASD完全解析06之CollapsingToolbarLayout》中,我们在NestedScrollView这个控件中有设置了一个属性:app:layout_behavior="@string/appbar_scrolling_view_behavior",我们看一下完整的代码:
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dp"
app:cardCornerRadius="5dp"
app:cardElevation="5dp">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:text="@string/custom_text"
android:textSize="16sp"/>
</android.support.v7.widget.CardView>
</android.support.v4.widget.NestedScrollView>
在这里,appbar_scrolling_view_behavior并不是一个字符串,而是一个类,而且这个类我们可以自定义。
这里,我们需要引入一个新的东西这个就叫Behavior。
在这里我们先看一下CoordinatorLayout中Behavior的官方介绍:
A Behavior implements one or more interactions that a user can take on a child view. These interactions may include drags, swipes, flings, or any other gestures.
简单地说:一个Behavior可以提供给一个或者多个用户在同一个子View中实现拖动、滑动、飞速滑动或者其它手势操作。
这个Behavior是CoordinatorLayout的内部类,直接子类有:AppBarLayout.Behavior, AppBarLayout.ScrollingViewBehavior, FloatingActionButton.Behavior, SwipeDismissBehavior<V extends View>我们自定义的时候需要继承于Behavior。
Behavior只有是CoordinatorLayout的直接子View才有意义,我们可以为任何View添加一个Behavior。
Behavior是一系列回调。让开发者有机会以非侵入的方式为View添加动态的依赖布局,和处理父布局(CoordinatorLayout)滑动手势的机会。
下面我们学习一下怎么实现一个自定义Behavior
使用
跟ASD库的其它控件一样,使用SnackBar和CoordinatorLayout需要在bundle.gradle中添加如下代码:
compile 'com.android.support:design:24.0.0'
在自定义Behavior中,如果是某个View监听另外一个View的大小,位置,显示状态等状态变化的时候,我们需要关心的是下面的三个方法:
layoutDependsOn(CoordinatorLayout parent, View child, View dependency)
onDependentViewChanged(CoordinatorLayout parent, View child, View dependency)
onDependentViewRemoved(CoordinatorLayout parent, View child, View dependency)
具体的代码解析如下:
/**
* 必须要实现这个构造函数,因为CoordinatorLayout源码中的parseBehavior()方法直接反射调用这个构造函数。
*
* @param context 上下文对象
* @param attrs 接收XMl中定义的属性信息
*/
public DependentBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* 设置我们需要关心的View
*
* @param parent 当前的CoordinatorLayout
* @param child 我们设置当前Behavior的View
* @param dependency 我们需要关心的View
* @return
*/
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return dependency instanceof Toolbar;
}
/**
* 当我们关心的View发生变化的时候,我们需要怎么处理
*
* @param parent 当前的CoordinatorLayout
* @param child 我们设置当前Behavior的View
* @param dependency 我们需要关心的View
* @return
*/
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
}
/**
* 在layoutDependsOn返回true的基础上之后,报告dependency被移除了
*
* @param parent 当前的CoordinatorLayout
* @param child 设置了Behavior的View
* @param dependency 设置Behavior依赖的View
*/
@Override
public void onDependentViewRemoved(CoordinatorLayout parent, View child, View dependency) {
super.onDependentViewRemoved(parent, child, dependency);
}
}
如果是监听CoordinatorLayout里面滑动状态的,我们需要关心的是另外一些方法:
onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes)
onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed)
onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes)
onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target)
onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed)
onNestedFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY, boolean consumed)
onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY)
具体的方法解析代码就是:
public class CustomBehavior extends CoordinatorLayout.Behavior<View> {
public CustomBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* 有嵌套滑动到来了,问下该Behavior是否接受嵌套滑动
*
* @param coordinatorLayout 当前的CoordinatorLayout
* @param child 该Behavior对应的View
* @param directTargetChild 嵌套滑动对应的父类的子类
* @param target 具体嵌套滑动的那个子类
* @param nestedScrollAxes 支持嵌套滚动轴。水平方向,垂直方向,或者不指定
* @return 是否接受该嵌套滑动
*/
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
return super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
}
/**
* 在嵌套滑动的子View未滑动之前告诉过来的准备滑动的情况
*
* @param coordinatorLayout
* @param child 该Behavior对应的View
* @param target 具体嵌套滑动的那个子类
* @param dx 水平方向嵌套滑动的子View想要变化的距离
* @param dy 垂直方向嵌套滑动的子View想要变化的距离
* @param consumed 这个参数要我们在实现这个函数的时候指定,回头告诉子View当前父View消耗的距离 consumed[0] 水平消耗的距离,consumed[1] 垂直消耗的距离 好让子view做出相应的调整
*/
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
}
/**
* Behavior接受了嵌套滑动的请求该函数调用。onStartNestedScroll返回true该函数会被调用。 参数和onStartNestedScroll一样
*
* @param coordinatorLayout 当前的CoordinatorLayout
* @param child 该Behavior对应的View
* @param directTargetChild 嵌套滑动对应的父类的子类
* @param target 具体嵌套滑动的那个子类
* @param nestedScrollAxes 支持嵌套滚动轴。水平方向,垂直方向,或者不指定
*/
@Override
public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
super.onNestedScrollAccepted(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
}
/**
* 停止嵌套滑动
*
* @param coordinatorLayout
* @param child 设置Behavior的View
* @param target 具体嵌套滑动的那个子类
*/
@Override
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target) {
super.onStopNestedScroll(coordinatorLayout, child, target);
}
/**
* 嵌套滑动的子View在滑动之后报告过来的滑动情况
*
* @param coordinatorLayout
* @param child 设置Behavior的View
* @param target 具体嵌套滑动的那个子类
* @param dxConsumed 水平方向嵌套滑动的子View滑动的距离
* @param dyConsumed 垂直方向嵌套滑动的子View滑动的距离
* @param dxUnconsumed 水平方向嵌套滑动的子View未滑动的距离
* @param dyUnconsumed 垂直方向嵌套滑动的子View未滑动的距离
*/
@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
}
/**
* 嵌套滑动的子View在fling之后报告过来的fling情况
*
* @param coordinatorLayout
* @param child 设置Behavior的View
* @param target 具体嵌套滑动的那个子类
* @param velocityX 水平方向速度
* @param velocityY 垂直方向速度
* @param consumed 子view是否fling了
* @return true Behavior是否消耗了fling;false Behavior没有消耗fling
*/
@Override
public boolean onNestedFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY, boolean consumed) {
return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
}
/**
* 在嵌套滑动的子View未fling之前告诉过来的准备fling的情况
*
* @param coordinatorLayout
* @param child 设置Behavior的View
* @param target 具体嵌套滑动的那个子类
* @param velocityX 水平方向速度
* @param velocityY 垂直方向速度
* @return true Behavior是否消耗了fling;false Behavior没有消耗fling
*/
@Override
public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY) {
return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
}
}
关于Behavior的自定义解析就到这里,附上国内镜像API,猛戳这里
最后推荐两个强大的自定义Behavior开源项目,都在Github上,实现的效果也非常酷炫,不过逻辑比较复杂:
附上效果图: