Behavior是CoordinatorLayout的核心,系统提供了一些实现好的效果,如ScrollingViewBehavior、BottomSheetBehavior、SwipeDismissBehavior等,但想要根据业务要求实现一些复杂的效果,还是需要掌握自定义Behavior。
自定义Behavior有两种方式,当然都需要继承CoordinatorLayout.Behavior<子View类型>
,都必须实现有参构造:
layoutDependsOn()
,在该方法中判断传入的Viewdependency
是否是我们想要的被依赖View,代码如下:
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, ImageView child, View dependency) {
//也可以通过过id判断 是则返回true 不是false
return dependency instanceof NestedScrollView;
}
onDependentViewChanged()
, 如果上一个方法中返回true确定了依赖关系,那么被依赖View状态发生变化时就会走到该方法中,我们在这里可以根据被依赖View的状态对子View做出相应改变,如果改变了子View的状态,则需要返回true,否则返回false,代码如下: @Override
public boolean onDependentViewChanged(CoordinatorLayout parent, ImageView child, View dependency) {
//获取被依赖View的Y轴偏移量
float translationY = dependency.getTranslationY();
//根据偏移量计算子View需要调整的透明度
float alpha = translationY / getHeaderHeight();
//改变子View透明度
child.setAlpha(alpha);
//改变了子View状态 返回true 反之false
return true;
}
先了解下几个重要的方法:
/**
* 可以重写这个方法对子View 进行重新布局
*/
@Override
public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
return super.onLayoutChild(parent, child, layoutDirection);
}
/**
* 当coordinatorLayout 的子View试图开始嵌套滑动的时候被调用。当返回值为true的时候表明
* coordinatorLayout 充当nested scroll parent 处理这次滑动,需要注意的是只有当返回值为true
* 的时候,Behavior 才能收到后面的一些nested scroll 事件回调(如:onNestedPreScroll、onNestedScroll等)
* 这个方法有个重要的参数nestedScrollAxes,表明处理的滑动的方向。
*
* @param coordinatorLayout 和Behavior 绑定的View的父CoordinatorLayout
* @param child 和Behavior 绑定的View
* @param directTargetChild
* @param target
* @param nestedScrollAxes 嵌套滑动 应用的滑动方向,看 {@link ViewCompat#SCROLL_AXIS_HORIZONTAL},
* {@link ViewCompat#SCROLL_AXIS_VERTICAL}
* @return
*/
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
return super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
}
/**
* 嵌套滚动发生之前被调用
* 在nested scroll child 消费掉自己的滚动距离之前,嵌套滚动每次被nested scroll child
* 更新都会调用onNestedPreScroll。注意有个重要的参数consumed,可以修改这个数组表示你消费
* 了多少距离。假设用户滑动了100px,child 做了90px 的位移,你需要把 consumed[1]的值改成90,
* 这样coordinatorLayout就能知道只处理剩下的10px的滚动。
* @param coordinatorLayout
* @param child
* @param target
* @param dx 用户水平方向的滚动距离
* @param dy 用户竖直方向的滚动距离
* @param consumed
*/
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
}
/**
* 进行嵌套滚动时被调用
* @param coordinatorLayout
* @param child
* @param target
* @param dxConsumed target 已经消费的x方向的距离
* @param dyConsumed target 已经消费的y方向的距离
* @param dxUnconsumed x 方向剩下的滚动距离
* @param dyUnconsumed y 方向剩下的滚动距离
*/
@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);
}
/**
* 嵌套滚动结束时被调用,这是一个清除滚动状态等的好时机。
* @param coordinatorLayout
* @param child
* @param target
*/
@Override
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target) {
super.onStopNestedScroll(coordinatorLayout, child, target);
}
/**
* onStartNestedScroll返回true才会触发这个方法,接受滚动处理后回调,可以在这个
* 方法里做一些准备工作,如一些状态的重置等。
* @param coordinatorLayout
* @param child
* @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);
}
/**
* 用户松开手指并且会发生惯性动作之前调用,参数提供了速度信息,可以根据这些速度信息
* 决定最终状态,比如滚动Header,是让Header处于展开状态还是折叠状态。返回true 表
* 示消费了fling.
*
* @param coordinatorLayout
* @param child
* @param target
* @param velocityX x 方向的速度
* @param velocityY y 方向的速度
* @return
*/
@Override
public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, View child, View target, float velocityX, float velocityY) {
return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
}
简单来说就是在onStartNestedScroll()
中判断是否需要处理该事件,需要在子View处理之前处理的业务放在onNestedPreScroll()
中,这里切记要把消费掉的事件距离放在consumed[]中,consumed[0]对应dx,consumed[1]对应dy,onNestedScroll()
是在嵌套滑动时被调用的,参数中有子View已消耗和未消耗的事件值。
dy > 0 向上滑动 dy < 0 向下滑动
具体还是要根据不同情况进行分析拆解,确定依赖关系,控制滑动等。
参考:
自定义Behavior的艺术探索-仿UC浏览器主页
Material Design 之 Behavior的使用和自定义Behavior