Behavior使用

一、CoordinatorLayout

CoordinatorLayout的主要功能是协调内部各个子控件直接的状态关系,也就是说,可以协调多个View进行互动,比如:移动,动画等。它是通过Behavior。代码连接已经放在了最下面,有需要可以下载

二、Behavior

是作用于CoordinatorLayout的子View的交互行为插件。Google给我们提供了一些Behavior,我们也可以自己定义Behavior,代码在最下面

1. BottomSheetBehavior

它是一个从底部弹出一个布局,例如我们经常用的分享功能

1.1 用法

image

注:我们设置了一个Behavior,bottom_sheet_behavior,是系统提供好的一个behavior,如果一开始需要隐藏的话,可以设置app:behavior_peekHeight="0dp"

然后在代码中这样写

image

BottomSheetBehavior有5种状态

(1)STATE_EXPANDED展开状态,显示完整布局。

(2)STATE_COLLAPSED折叠状态,显示peekHeigth 的高度,如果peekHeight为0,则全部隐藏,与STATE_HIDDEN效果一样。

(3)STATE_DRAGGING拖拽时的状态

(4)STATE_HIDDEN隐藏时的状态

(5)STATE_SETTLING释放时的状态

2. BottomSheetDialog

它是一个Dialog,从底部弹出一个Dialog,比如淘宝商品详情页的立即购买,它是对BottomSheetBehavior的一个封装,是获取一个Behavior,设置一个监听状态的回调,设置了下滑可以隐藏

示例如下:

image

注意:系统的BottomSheetDialog是基于BottomSheetBehavior封装的,这里判断了当滑动隐藏了BottomSheetBehavior中的View后,内部设置了BottomSheetBehavior的状态为STATE_HIDDEN,所以我们再次调用dialog.show()的时候Dialog没法再打开状态为STATE_HIDE的Dialog,所以我们需要自己来实现,监听用户滑动关闭后,把BottomSheetBehavior的状态再设置为STATE_COLLAPSED

image

3.SwipeDissmissBehavior

滑动关闭或者滑动消失,Snackbar就是使用的这个,当滑动Snackbar的时候,Snackbar消失

代码也特别简单,在代码中直接new一个SwipeDissmissBehavior,设置属性,添加到CoordinatorLayout.LayoutParams,直接代码截图

image

4. 自定义Behavior

Google为我们提供了一些场景使用的Behavior,但是有时候,要实现多个View之间的交互,我们可以使用自定义Behavior

4.1 第一种是通过监听一个View的状态,如果位置,大小的变化,来改变其它View的行为,这种只需要重写两个方法就可以了,分别是layoutDependsOn和onDependentViewChanged,layoutDependsOn方法判断是指定依赖的View时,返回true,然后在onDependentViewChange里,被依赖的View做需要的行为动作

4.2 第二种就是重写onStartNestedScroll、onNestedPreScroll等

具体方法:


/**** 

* 表示是否给应用了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, FloatingActionButton child, View dependency) {

return super.layoutDependsOn(parent, child, dependency);

}

/**

* 当被依赖的View 状态(如:位置、大小)发生变化时,这个方法被调用

* @param parent

* @param child

* @param dependency

* @return

*/

@Override

public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, View dependency) {

return super.onDependentViewChanged(parent, child, dependency);

}

/**

* 当coordinatorLayout 的子View试图开始嵌套滑动的时候被调用。当返回值为true的时候表明

* coordinatorLayout 充当nested scroll parent 处理这次滑动,需要注意的是只有当返回值为true

的时候,Behavior 才能收到后面的一些nested scroll 事件回调(如:onNestedPreScroll、onNestedScroll等)

这个方法有个重要的参数axes,表明处理的滑动的方向。

* @param coordinatorLayout  和Behavior 绑定的View的父CoordinatorLayout

* @param child 和Behavior 绑定的View

* @param directTargetChild

* @param target

* @param axes 嵌套滑动 应用的滑动方向(ViewCompat.SCROLL_AXIS_HORIZONTAL,@ViewCompat.SCROLL_AXIS_VERTICAL)

* @param type

* @return

*/

@Override

public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,@NonNull FloatingActionButton child,@NonNull View directTargetChild,@NonNull View target,int axes,int type) {

return super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes);

}

/**

* 嵌套滚动发生之前被调用

* 在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

* @param type

*/

@Override

public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout,@NonNull FloatingActionButton child,@NonNull View target,int dx,int dy,@NonNull int[] consumed,int type) {

super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);

}

/**

* 进行嵌套滚动时被调用

* @param coordinatorLayout

* @param child

* @param target

* @param dxConsumed 已经消费的x方向的距离

* @param dyConsumed 已经消费的y方向的距离

* @param dxUnconsumed x 方向剩下的滚动距离

* @param dyUnconsumed y 方向剩下的滚动距离

* @param type

*/

@Override

public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,@NonNull FloatingActionButton child,@NonNull View target,int dxConsumed,int dyConsumed,int dxUnconsumed,int dyUnconsumed,int type) {

super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);

}

/**

* 嵌套滚动结束时被调用,这是一个清除滚动状态等的好时机。

* @param coordinatorLayout

* @param child

* @param target

* @param type

*/

@Override

public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,@NonNull FloatingActionButton child,@NonNull View target,int type) {

super.onStopNestedScroll(coordinatorLayout, child, target, type);

}

/**

* onStartNestedScroll返回true才会触发这个方法,接受滚动处理后回调,可以在这个

方法里做一些准备工作,如一些状态的重置等。

* @param coordinatorLayout

* @param child

* @param directTargetChild

* @param target

* @param axes

* @param type

*/

@Override

public void onNestedScrollAccepted(@NonNull CoordinatorLayout coordinatorLayout,@NonNull FloatingActionButton child,@NonNull View directTargetChild,@NonNull View target,int axes,int type) {

super.onNestedScrollAccepted(coordinatorLayout, child, directTargetChild, target, axes, type);

}

/**

* 用户松开手指并且会发生惯性动作之前调用,参数提供了速度信息,可以根据这些速度信息决定最终状态,

比如滚动Header,是让Header处于展开状态还是折叠状态。返回true 表示消费了fling.

* @param coordinatorLayout

* @param child

* @param target

* @param velocityX x 方向的速度

* @param velocityY y 方向的速度

* @return

*/

@Override

public boolean onNestedPreFling(@NonNull CoordinatorLayout coordinatorLayout,@NonNull FloatingActionButton child,@NonNull View target,float velocityX,float velocityY) {

return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);

}

/**

* /可以重写这个方法对子View 进行重新布局

* @param parent

* @param child

* @param layoutDirection

* @return

*/

@Override

public boolean onLayoutChild(CoordinatorLayout parent, FloatingActionButton child,int layoutDirection) {

return super.onLayoutChild(parent, child, layoutDirection);

}

案例:仿知乎首页滑动隐藏/显示

它是CoordinatorLayout的子View之间的交互,实现子View随着RecyclerView的滚动显示或者隐藏,只需要滑动一段距离,来显示隐藏,实现onNestedScroll(),在里面判断View需要移动的位置,然后在进行动画移动


//向上的时候是出来,向下是隐藏

if(dyConsumed>0){//往上滑动,是隐藏,需要加一个标志位

    if(!isOut){//不是往下走,需要往下走

        CoordinatorLayout.LayoutParams params=(CoordinatorLayout.LayoutParams)child.getLayoutParams();

child.animate().translationY(params.bottomMargin+child.getMeasuredHeight()).setDuration(300).start();

//处理底部位移动画

        mBottomTabView.animate().translationY(mBottomTabView.getMeasuredHeight()).setDuration(300).start();

isOut=true;

}

}else {//往下滑动

    if(isOut){

child.animate().translationY(0).setDuration(300).start();

//处理底部位移动画

        mBottomTabView.animate().translationY(0).setDuration(300);

isOut=false;

}

}

注:1.自定义Behavior构造方法一定要重载!!!不然会报错

   2.底部移动和隐藏需要先在onLayoutChild()里面获取底部控件id,然后再在onNestedScroll()写移动动画

Override

public boolean onLayoutChild(CoordinatorLayout parent, FloatingActionButton child,int layoutDirection) {

mBottomTabView = parent.findViewById(R.id.bottom_tab_layout);

return super.onLayoutChild(parent, child, layoutDirection);

}

具体代码连接:https://github.com/sisirukou/Behavior.git

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容