美文网首页基要稳
A super-powered FrameLayout—协调布局

A super-powered FrameLayout—协调布局

作者: Guxxxd | 来源:发表于2021-12-02 15:19 被阅读0次

    上篇文章介绍了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 <V> 此行为操作的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


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

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

    一起成长,共同进步

    相关文章

      网友评论

        本文标题:A super-powered FrameLayout—协调布局

        本文链接:https://www.haomeiwen.com/subject/gxcjoltx.html