一、需求
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 条评论