一、需求

A:红色矩形框为悬浮窗,要求能在桌面做生意拖动但不超出桌面。

B:酱色矩形框,要求点击可折叠/展开橘黄色+紫色框的区域;如果是拖动,需要优先于点击事件,拖动整个悬浮窗。

C:列表,需要上下滑动。但是左右滑动时则拖动整个悬浮窗。

以上就是典型的拖动+点击+滑动事件冲突的场景,A为CustomOverlayLinearLayout,B为CustomTouchHandleImageView,C为RecyclerView。

二、事件传递的顺序

1)最开始的时候只有A,通过复写onTouch方法或者调用setOnTouchListener设置,可以实现悬浮窗A的任意拖动。
因为CustomOverlayLinearLayout默认onInterceptTouchEvent方法返回false,即不拦截事件,事件往下层子View传递,但是子View没有消费事件,所以最终事件得以被A的onTouch方法响应。

2)接着B加到布局,B设置了一个点击事件。
由于B是一个View,并非ViewGroup,所以没有onInterceptTouchEvent方法,直接点击事件消费了事件,触发自己的点击事件,事件便不会向上分发至A,那么拖动B则A不会拖动。但是产品要求B既能点击又能拖动,怎么解决?

B既能点击又能拖动换一种说法就是B要消费点击事件,但是不消费MOVE事件,由A消费MOVE事件即可拖动。

上面已经说到了CustomOverlayLinearLayout默认onInterceptTouchEvent方法返回false,即不拦截事件,事件往下层子View传递,现在B添加了点击事件,会消费事件,那么自然A没有消费事件,会出现A拖动失效的问题,根本原因是A没有消费事件。

A如何消费事件

首先,A是一个ViewGroup,要想消费,先得拦截。所以得复写onInterceptTouchEvent方法:
这里有一个事件坐标位移公式,根据位移的大小判断,如果是MOVE事件则拦截,对于点击事件要给B放行,否则B的点击事件会失效。

 private var startX = 0f
    private var startY = 0f
    private val touchSlop = ViewConfiguration.get(context!!).scaledTouchSlop

    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
        when (ev.action) {
            MotionEvent.ACTION_DOWN -> {
                Log.d(TAG, "onInterceptTouchEvent ACTION_DOWN")
                startX = ev.x
                startY = ev.y
            }
            MotionEvent.ACTION_MOVE -> {
                Log.d(TAG, "onInterceptTouchEvent ACTION_MOVE")
                val dx = ev.x - startX
                val dy = ev.y - startY

                if (dx * dx + dy * dy > touchSlop * touchSlop) {
                    //2.处理与logo点击事件冲突
                    return true // 拦截事件,交给父View处理
                }
            }
            MotionEvent.ACTION_UP -> {
                Log.d(TAG, "onInterceptTouchEvent ACTION_UP")
            }
        }
        val onInterceptTouchEvent = super.onInterceptTouchEvent(ev)
        Log.d(TAG, "onInterceptTouchEvent: $onInterceptTouchEvent")
        return onInterceptTouchEvent
    }

3)接着C加到布局,C有列表滑动事件。
同理也是复写A的onInterceptTouchEvent方法,在 MotionEvent.ACTION_MOVE里加上判断就行:只有展开且竖向滑动才不拦截事件

 MotionEvent.ACTION_MOVE -> {
                Log.d(TAG, "onInterceptTouchEvent ACTION_MOVE")
                val dx = ev.x - startX
                val dy = ev.y - startY

                if (dx * dx + dy * dy > touchSlop * touchSlop) {
                    //1.处理与RecyclerView滑动冲突
                    if(expand){
                        // 如果是竖向滑动,则不拦截事件,交给 RecyclerView 处理
                        if (Math.abs(dy) > Math.abs(dx)) {
                            Log.d(TAG, "onInterceptTouchEvent: 交给 RecyclerView 处理")
                            return false
                        }
                    }else{
                        Log.d(TAG, "onInterceptTouchEvent: not expand")
                    }

                    //2.处理与logo点击事件冲突
                    return true // 拦截事件,交给父View处理
                }
            }

0 条评论

发表回复

您的电子邮箱地址不会被公开。