CoordinatorLayout可以协调子View之间进行交互动作,那么CoordinatorLayout它是怎么协调子View的呢?其实核心就是Behavior。那么今天讲的就是-Behavior。Behavior的使用我们应该不陌生,在AppbarLayout 与NestedScrollView 联动的时候,我们为NestedScrollView设置了一个Behavior,当然这个Behavior是调用系统已有的,FloatingActionButton.Behavior、BottomSheetBehaviro、SwipeDismissBehavior、ScrollingViewBehavior都是系统为我们写好直接调用就好了,这里就不作为重点讲解了,想了解的同学可以看严振杰大神的博客http://blog.csdn.net/yanzhenjie1003/article/details/51938400。内容太枯燥来波效果图缓解一下尴尬:
官方的介绍:作用于CoordinatorLayout的子View的交互行为插件。一个Behavior 实现了用户的一个或者多个交互行为,它们可能包括拖拽、滑动、快滑或者其他一些手势。
Behavior 是一个顶层抽象类,其他的一些具体行为的Behavior 都是继承自这个类。它提供了几个重要的方法:
layoutDependsOn:
/**
* 表示是否给应用了Behavior 的View 指定一个依赖的布局,通常,当依赖的View 布局发生变化时
* 不管被被依赖View 的顺序怎样,被依赖的View也会重新布局
* @param parent
* @param child 绑定behavior 的View
* @param dependency 依赖的view
* @return 如果child 是依赖的指定的View 返回true,否则返回false
*/
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return super.layoutDependsOn(parent, child, dependency);
}
onDependentViewChanged:
/**
* 当被依赖的View 状态(如:位置、大小)发生变化时,这个方法被调用
* @param parent
* @param child
* @param dependency
* @return
*/
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
return super.onDependentViewChanged(parent, child, dependency);
}
onStartNestedScroll:
/**
* 当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);
}
onNestedPreScroll:
/**
* 嵌套滚动发生之前被调用
* 在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);
}
onNestedScroll:
/**
* 进行嵌套滚动时被调用
* @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);
}
onStopNestedScroll:
/**
* 嵌套滚动结束时被调用,这是一个清除滚动状态等的好时机。
* @param coordinatorLayout
* @param child
* @param target
*/
@Override
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target) {
super.onStopNestedScroll(coordinatorLayout, child, target);
}
onNestedScrollAccepted:
/**
* 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);
}
onNestedPreFling:
/**
* 用户松开手指并且会发生惯性动作之前调用,参数提供了速度信息,可以根据这些速度信息
* 决定最终状态,比如滚动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);
}
onLayoutChild:
//可以重写这个方法对子View 进行重新布局
@Override
public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
return super.onLayoutChild(parent, child, layoutDirection);
}
自定义Behavior 分为两种:
第一种是通过监听一个View的状态,如位置、大小的变化,来改变其他View的行为,这种只需要重写2个方法就可以了,分别是layoutDependsOn 和onDependentViewChanged, layoutDependsOn方法判断是指定依赖的View时,返回true,然后在onDependentViewChanged 里,被依赖的View做需要的行为动作。
第二种就是重写onStartNestedScroll、onNestedPreScroll、onNestedScroll等一系列方法,前面第一步分已经讲过。
上面两种方法相比,第一种很简单,第二种复杂一些,但是第二种实现的效果也要复杂。
/**
*
* 自定义Behavior :实现RecyclerView(或者其他可滑动View,如:NestedScrollView) 滑动覆盖header 的效果
*
*/
public class HeaderScrollBehavior extends CoordinatorLayout.Behavior {
private float Alpha ;
public CoverHeaderScrollBehavior(Context context, AttributeSet attributeSet){
super(context,attributeSet);
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return isDependent(dependency);
}
private boolean isDependent(View dependency){
return dependency!=null && dependency.getId() == R.id.iv_alpha;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
Alpha = child.getTranslationY() / getHeaderHeight();
dependency.setAlpha(Alpha);
return super.onDependentViewChanged(parent, child, dependency);
}
@Override
public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
if(params!=null && params.height == CoordinatorLayout.LayoutParams.MATCH_PARENT){
child.layout(0,0,parent.getWidth(),parent.getHeight());
child.setTranslationY(getHeaderHeight());
return true;
}
return super.onLayoutChild(parent, child, layoutDirection);
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {
return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
}
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
// 在这个方法里面只处理向上滑动
if(dy < 0){
return;
}
float transY = child.getTranslationY() - dy;
if(transY > 0){
child.setTranslationY(transY);
consumed[1]= dy;
}
}
@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);
// 在这个方法里只处理向下滑动
if(dyUnconsumed >0){
return;
}
float transY = child.getTranslationY() - dyUnconsumed;
if(transY > 0 && transY < getHeaderHeight()){
child.setTranslationY(transY);
}
}
/**
* 获取Header 高度
* @return
*/
public int getHeaderHeight(){
return MaterialDesignSimpleApplication.getAppContext().getResources().getDimensionPixelOffset(R.dimen.header_height);
}
}
.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
"@+id/iv_alpha"
android:layout_width="match_parent"
android:layout_height="@dimen/header_height"
android:scaleType="centerCrop"
android:src="@drawable/meinv"
/>
.support.v4.widget.NestedScrollView
android:id="@+id/nested_scroll_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
app:layout_behavior="@string/header_behavior"
>
"match_parent"
android:layout_height="match_parent"
android:text="@string/large_text"
/>
.support.v4.widget.NestedScrollView>
.support.design.widget.CoordinatorLayout>