Android自定义Behavior

Behavior是CoordinatorLayout的核心,系统提供了一些实现好的效果,如ScrollingViewBehavior、BottomSheetBehavior、SwipeDismissBehavior等,但想要根据业务要求实现一些复杂的效果,还是需要掌握自定义Behavior。

如何自定义Behavior

自定义Behavior有两种方式,当然都需要继承CoordinatorLayout.Behavior<子View类型>,都必须实现有参构造:

  1. 指定View依赖于某个View,监听被依赖View的状态变化,如大小、位置、显示状态等,根据被依赖View的变化来做出相应变化,较为简单;
  2. 通过NestedScrolling 机制,重写相关回调方法来处理滑动事件,比较复杂。但相对于自定义View,这样做更解耦,灵活。

依赖View方式

  1. 重写layoutDependsOn(),在该方法中判断传入的Viewdependency是否是我们想要的被依赖View,代码如下:

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, ImageView child, View dependency) {

        //也可以通过过id判断 是则返回true 不是false
        return dependency instanceof NestedScrollView;
    }
  1. 重写 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

你可能感兴趣的:(Android学习之路,Behavior)