如何解决在不拦截触摸事件的情况下检测 ViewGroup 中的长按
我希望在我的 ViewGroup
中有以下行为:
- 根本不拦截事件
- 检测到长按后立即拦截
- 仅在没有孩子检测到长按时才拦截
我认为,如果一个子节点在其 OnLongClickListener
上返回 true,这将导致 requestDisallowInterceptTouchEvent
向上传递视图层次结构,但这并没有发生...
有什么想法可以正确实施吗?
我目前的解决方案
class InterceptTouchFrameLayout @JvmOverloads constructor(
context: Context,attrs: AttributeSet? = null,defStyleAttr: Int = 0,defStyleRes: Int = 0
) : FrameLayout(context,attrs,defStyleAttr,defStyleRes) {
var onEventListener: ((event: Event,location: Point) -> Unit)? = null
var longPressDown: DownEvent? = null
var longPressEventHandled: Boolean = false
var longPressRunnable = Runnable {
longPressDown?.let { onEventListener?.invoke(Event.LongPress,it.getPoint()) }
longPressEventHandled = true
L.d { "Long Press Event" }
}
// -----------------
// Touch Events
// -----------------
/*
* This method JUST determines whether we want to intercept the motion.
* If we return true,onTouchEvent will be called and we do the actual
* event handling there.
*/
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
val intercept: Boolean // true if we want to intercept the touch event
if (longPressEventHandled) {
// the long press event has already been send out so we intercept - no child view needs to handle this event anymore!
intercept = true
} else {
when (ev.actionMasked) {
// Always handle the case of the touch gesture being complete.
MotionEvent.ACTION_CANCEL,MotionEvent.ACTION_UP -> {
cancelLongPressDelayed()
intercept = false
}
MotionEvent.ACTION_DOWN -> {
startLongPressDelayed(ev)
intercept = false
}
MotionEvent.ACTION_MOVE -> {
val inValidDistance = longPressDown?.inClickDistance(ev,touchSlop) ?: false
// if we are not in a valid long press distance we cancel the long press
if (!inValidDistance) {
cancelLongPressDelayed()
}
intercept = false
}
else -> {
// In general,we don't want to intercept touch events. They should be
// handled by the child view.
intercept = false
}
}
}
L.d { "Intercept: ${ev.actionMasked} | $intercept | $longPressEventHandled" }
return intercept
}
/* Here we actually handle the touch event
* This method will only be called if the touch event was intercepted in
* onInterceptTouchEvent
*/
override fun onTouchEvent(ev: MotionEvent): Boolean {
// we only intercept the touch event if we have detected a long press - otherwise we always forward the touch events to the children
// => this function should NEVER be called when longPressEventHandled == false!
L.d { "ev = ${ev.actionMasked} | longPressEventHandled = $longPressEventHandled" }
val handled: Boolean
when (ev.actionMasked) {
// Always handle the case of the touch gesture being complete.
MotionEvent.ACTION_CANCEL,MotionEvent.ACTION_UP -> {
cancelLongPressDelayed()
handled = true
}
else -> {
handled = false
}
}
return handled
}
override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
super.requestDisallowInterceptTouchEvent(disallowIntercept)
L.d { "disallowIntercept = $disallowIntercept" }
cancelLongPressDelayed()
}
// -----------------
// functions
// -----------------
private fun startLongPressDelayed(ev: MotionEvent) {
longPressEventHandled = false
longPressDown = DownEvent(ev.x.toInt(),ev.y.toInt())
handler.postDelayed(
longPressRunnable,(longPressTimeout.toFloat() * 1.1f).toLong() // we add a little bit to the timeout so that childrens long press handlers fire before us
)
}
private fun cancelLongPressDelayed() {
handler.removeCallbacks(longPressRunnable)
longPressEventHandled = false
longPressDown = null
}
// -----------------
// classes / constants
// -----------------
private val touchSlop: Int = ViewConfiguration.get(context).scaledTouchSlop
private val longPressTimeout = ViewConfiguration.getLongPressTimeout().toLong()
class DownEvent(val x: Int,val y: Int) {
fun inClickDistance(ev: MotionEvent,touchSlop: Int): Boolean {
val distX = abs(ev.x.toInt() - x)
val distY = abs(ev.y.toInt() - y)
return distX <= touchSlop && distY <= touchSlop
}
fun getPoint() = Point(x,y)
}
enum class Event {
LongPress
}
}
我的解决方案有问题
我的 LongClickListeners
中确实有一个带有 ViewGroup
的项目 - 这些项目确实在其侦听器中返回 true
但这无济于事。我不知道如何检测孩子是否也在处理长按 - 在这种情况下,我的视图组应该立即取消它的长按处理...
似乎我需要在 ALL LongClickListeners
中手动执行以下操作:
view.parent.requestDisallowInterceptTouchEvent()
但这意味着视图必须始终知道它在我的自定义视图组中,并且必须手动调用 requestDisallowInterceptTouchEvent
- 但这似乎不是很干净。这个问题有更好的解决方案吗?我不能在每个 OnLongClickListener
中手动执行某些操作?我可以在 ViewGroup
中执行什么操作,例如 doesAnyChildViewUnderMotionEventHandleThisLongPress()
之类的函数?有一个 View.hasOnLongClickListeners()
函数,但仅在 API >= 30 中,这对我的用例来说没问题,但似乎没有针对旧 API 的解决方案...
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。