A super-powered FrameLayout—协调布局CoordinatorLayout(二):Behavior

上篇文章介绍了CoordinatorLayout的基本使用 — A super-powered FrameLayout—协调布局CoordinatorLayout(一):深度基本了解

本篇文章说一说Behavior

  • 是什么
  • 怎么写
  • 用在哪

一、Behavior官方解释

  /**
     * Interaction behavior plugin for child views of {@link CoordinatorLayout}.
     *
     * <p>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.</p>
     *
     * @param <V> The View type that this Behavior operates on
     */

CoordinatorLayout直接子view之间相互作用行为的插件.

行为实现了一个或多个用户可以在子view上进行的交互.
这些交互可能包括拖动、滑动、甩动或任何其他手势.

@param 此行为操作的view类型.

二、自定义Behavior

2.1 初识 CoordinatorLayout.Behavior

方法是真的多,就这还删除了被标记@Deprecated的方法,后面会挑捡些重要的方法着重说明

public static abstract class Behavior<V extends View> {

    public Behavior() {}

    public Behavior(Context context, AttributeSet attrs) {}

    public void onAttachedToLayoutParams(@NonNull CoordinatorLayout.LayoutParams params) {}

    public void onDetachedFromLayoutParams() {}

    public boolean onInterceptTouchEvent(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull MotionEvent ev) { return false; }

    public boolean onTouchEvent(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull MotionEvent ev) { return false; }

    @ColorInt
    public int getScrimColor(@NonNull CoordinatorLayout parent, @NonNull V child) { return Color.BLACK; }

    @FloatRange(from = 0, to = 1)
    public float getScrimOpacity(@NonNull CoordinatorLayout parent, @NonNull V child) { return 0.f; }

    public boolean blocksInteractionBelow(@NonNull CoordinatorLayout parent, @NonNull V child) { return getScrimOpacity(parent, child) > 0.f; }

    public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull View dependency) { return false; }

    public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull View dependency) { return false; }

    public void onDependentViewRemoved(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull View dependency) { }

    public boolean onMeasureChild(@NonNull CoordinatorLayout parent, @NonNull V child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { return false; }

    public boolean onLayoutChild(@NonNull CoordinatorLayout parent, @NonNull V child, int layoutDirection) { return false; }

    public static void setTag(@NonNull View child, @Nullable Object tag) { }

    public static Object getTag(@NonNull View child) { }
    
    public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View directTargetChild, @NonNull View target, @ScrollAxis int axes, @NestedScrollType int type) { }

    public void onNestedScrollAccepted(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View directTargetChild, @NonNull View target,@ScrollAxis int axes, @NestedScrollType int type) { }

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

    public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, @NestedScrollType int type, @NonNull int[] consumed) { }
    
    public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, @NestedScrollType int type) { }

    public boolean onNestedFling(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, float velocityX, float velocityY, boolean consumed) { return false; }

    public boolean onNestedPreFling(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, float velocityX, float velocityY) { return false; }
    
    public WindowInsetsCompat onApplyWindowInsets(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull WindowInsetsCompat insets) { return insets; }

    public boolean onRequestChildRectangleOnScreen(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull Rect rectangle, boolean immediate) { return false; }

    public void onRestoreInstanceState(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull Parcelable state) {// no-op}

    public Parcelable onSaveInstanceState(@NonNull CoordinatorLayout parent, @NonNull V child) { return BaseSavedState.EMPTY_STATE; }

    public boolean getInsetDodgeRect(@NonNull CoordinatorLayout parent, @NonNull V child, @NonNull Rect rect) { return false; }
}

2.2 CoordinatorLayout.Behavior<V extends View>的泛型、方法说明

2.2.1 泛型说明

官方释义:此Behavior操作的view类型.
这个Behavior是用在哪种类型的View上,TextViewImageViewButton等等都可以,如果确定了某一种类型,就需要用在对应的那一种类型上;或者直接使用View作为泛型类型,通吃。

2.2.2 方法说明
  • Behavior()无参构造方法用于手动实例化
  • Behavior(Context context, AttributeSet attrs)2个参构造方法如需再xml中设置,需要重写
  • void onAttachedToLayoutParams(CoordinatorLayout.LayoutParams params)当 Behavior 已附加到 LayoutParams 实例时调用
    -void onDetachedFromLayoutParams()当 Behavior 与其持有的 LayoutParams 实例分离时调用
  • boolean onInterceptTouchEvent(CoordinatorLayout parent,V child,MotionEvent ev)拦截CoordinatorLayout 触摸事件,在CoordinatorLayout 触摸事件分发到子view之前响应,如果此行为想要拦截并接管事件流,则为 true。默认返回 false
  • boolean onTouchEvent(CoordinatorLayout parent, V child,MotionEvent ev)一旦 Behavior 拦截到触摸事件,则事件流将被发送到此方法;如果Behavior处理此触摸事件并希望继续接收此流中的事件,则为 true。默认返回 false
  • boolean layoutDependsOn(CoordinatorLayout parent, V child,View dependency)设置此Behavior的view需要依赖的view
  • boolean onDependentViewChanged(CoordinatorLayout parent, V child,View dependency)当依赖view的大小或位置在发生变化时,调用此方法。
  • void onDependentViewRemoved(CoordinatorLayout parent, V child, View dependency)当依赖的view从父视图中删除后调用此方法
  • onMeasureChild(CoordinatorLayout parent, V child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed)当父 CoordinatorLayout 即将测量给定的子视图时调用,此方法可用于对子视图执行自定义或修改的测量,以代替默认的子测量行为
  • boolean onLayoutChild(CoordinatorLayout parent, V child,int layoutDirection)此方法可用于执行子视图的自定义或修改布局,以代替默认的子布局行为
  • boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout,V child, View directTargetChild, View target, @ScrollAxis int axes, @NestedScrollType int type) 嵌套滑动开始,如果behavior消费此次嵌套滑动,返回 true
  • void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout,V child, View directTargetChild, View target,@ScrollAxis int axes, @NestedScrollType int type)当 CoordinatorLayout 接受嵌套滚动时调用
  • void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target, @NestedScrollType int type)当嵌套滚动结束时调用
  • void onNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, @NestedScrollType int type, @NonNull int[] consumed)嵌套滑动进行中,要监听的子 View的滑动事件已经被消费
  • void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, int dy, int[] consumed, @NestedScrollType int type)嵌套滑动进行中,要监听的子 View将要滑动,滑动事件即将被消费
  • boolean onNestedFling(CoordinatorLayout coordinatorLayout,V child, View target, float velocityX, float velocityY,boolean consumed)当嵌套滚动子项开始投掷或将是“投掷”的动作时调用
  • boolean onNestedPreFling( CoordinatorLayout coordinatorLayout,V child, View target, float velocityX, float velocityY)当嵌套滚动子项即将开始“投掷”时调用

2.3 自定义CoordinatorLayout.Behavior<V extends View>

2.3.1 明确需求,自定义Behavior一般情况下分为
  1. 此view随着依赖view的状态(位置等)变化而变化
class CustomBehavior : CoordinatorLayout.Behavior<View> {

    companion object {
        private const val TAG = "mr_gu"
    }

    // 列表顶部和title底部重合时,列表的滑动距离。
    private var deltaY = 0f

    constructor()
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)

    /**
     * 实现Behavior必须要要复写的方法
     */
    override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
        Log.i(TAG, "layoutDependsOn: ")
        return dependency is NestedScrollView
    }

    /**
     * 当依赖的view状态发生变化时,设置此behavior的view应当作出的变化
     *
     * 这里实现的是,当NestedScrollView的位置变化时,设置此behavior的view的随着变化
     */
    override fun onDependentViewChanged(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
        if (deltaY == 0f) {
            deltaY = dependency.y - child.height
        }
        var dy = dependency.y - child.height
        dy = if (dy < 0) 0f else dy
        val y = -(dy / deltaY) * child.height
        child.translationY = y

        Log.i(TAG, "onDependentViewChanged: ")

        return true
    }
} 
  1. 此view随着依赖view的滑动状态变化而变化
// 滑动状态透明度为0.5f,停止滑动透明度1f
class CustomBehavior : CoordinatorLayout.Behavior<View> {

    companion object {
        private const val TAG = "mr_gu"
    }

    // 列表顶部和title底部重合时,列表的滑动距离。
    private var deltaY = 0f

    constructor()
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)

    /**
     * 实现Behavior必须要要复写的方法
     */
    override fun layoutDependsOn(parent: CoordinatorLayout, child: View, dependency: View): Boolean {
        Log.i(TAG, "layoutDependsOn: ")
        return dependency is NestedScrollView
    }
    
    override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout, child: View,
                                     directTargetChild: View, target: View, axes: Int, type: Int): Boolean {
        Log.i(TAG, "onStartNestedScroll: ")
        // 只分发垂直滑动
        return axes == ViewCompat.SCROLL_AXIS_VERTICAL
    }

    override fun onStopNestedScroll(coordinatorLayout: CoordinatorLayout, child: View, target: View, type: Int) {
        super.onStopNestedScroll(coordinatorLayout, child, target, type)
        child.visibility = View.VISIBLE
        child.alpha = 1f
        Log.i(TAG, "onStopNestedScroll: ")
    }

    override fun onNestedScroll(coordinatorLayout: CoordinatorLayout, child: View, target: View, dxConsumed: Int,
        dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int, type: Int, consumed: IntArray) {
        super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed,
            dxUnconsumed, dyUnconsumed, type, consumed)
        Log.i(TAG, "onNestedScroll: ")
         // 也可以拿到当次滑动的距离
        child.alpha = 0.5f
    }
}
  1. 1 + 2

三、总结与思考

使用Behavior可以较为方便的实现页面中view间相互作用的效果,文章中的例子都是最简单的效果,面对设计小姐姐天马行空的设计和想法,Behavior可能会帮助你很好的应对,不至于扎耳挠腮,骂骂咧咧,还容易掉头发

Behavior是底层逻辑是什么样的,为什么在xml中简单的配置个Behavior属性就可以实现,答案都在源码中,read the fucking resource code ,can help you grow quickly

【本文参考】
SheHuan —— Android CoordinatorLayout之自定义Behavior


如果文章对你有帮助,点个赞再走呗

如果文章中存在错误,还望评论区指出

一起成长,共同进步

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值