我正在使用手势检测器从滚动视图捕获滚动事件:
val gestureDetector = GestureDetector(this.fragment.activity, ScrollGestureListener(scrollView))
scrollView.setOnTouchListener(OnTouchListener { view, event ->
gestureDetector.onTouchEvent(event)
return@OnTouchListener false
})
internal inner class ScrollGestureListener(view: View) : GestureDetector.SimpleOnGestureListener() {
override fun onScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
[email protected]()
return true
}
}
当使用compileSdkVersion 30时,我的应用程序会在
onScroll
函数上崩溃,因为它总是收到第一个参数的空值。我通过将第一个参数设为可选来解决这个问题:
override fun onScroll(e1: MotionEvent?, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
现在我尝试将compileSdkVersion更新为33,上面的行将不再编译。但如果我恢复到标准函数签名,那么当我滚动滚动视图时应用程序会再次崩溃。
对this SO post的评论说:“如果手势检测器以外的其他东西消耗了 ACTION_DOWN 事件,你就会遇到这样的崩溃。”但我不知道还能做什么。我在此片段中的其他一些视图上有手势检测器,但如果我注释掉所有这些代码,崩溃仍然存在。
我临时将
onDown
的实现添加到我的 ScrollGestureListener
中,但它从未被调用,因此这似乎与 ACTION_DOWN 注释有关。
查看
GestureDetector.java
的源代码,我发现它发送到 onScroll
的第一个参数是 mCurrentDownEvent
,因此这似乎也与 ACTION_DOWN 注释相关。但是,如果我在 Android Studio 中向 GestureDetector.java
添加断点,mCurrentDownEvent
永远不会显示为 null。此外,它始终与作为第二个参数传递的事件相同 (ev
) - GestureDetector
只是将 mCurrentDownEvent
设置为 ev
的副本。
看起来崩溃发生在
GestureDetector
调用 onScroll
时,因为我的 onScroll
方法中的断点未到达,并且如果我删除 onScroll
覆盖,也会发生同样的崩溃 - 显然只是调用超类实施 onScroll
会导致崩溃。以下是控制台中显示的内容:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.arlomedia.bandhelper, PID: 18797
java.lang.NullPointerException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkNotNullParameter, parameter e1
at com.arlomedia.bandhelper.helpers.DocumentViewer$ScrollGestureListener.onScroll(Unknown Source:2)
at android.view.GestureDetector.onTouchEvent(GestureDetector.java:788)
at com.arlomedia.bandhelper.helpers.DocumentViewer.viewDocument$lambda-9(DocumentViewer.kt:1398)
at com.arlomedia.bandhelper.helpers.DocumentViewer.$r8$lambda$kyj-4h2jNAIkIYOWSE7_HVetJAg(Unknown Source:0)
at com.arlomedia.bandhelper.helpers.DocumentViewer$$ExternalSyntheticLambda7.onTouch(Unknown Source:6)
at android.view.View.dispatchTouchEvent(View.java:15147)
在致电
OnTouchListener
之前,我尝试在我的 onTouchEvent
中添加某种检查 - 像这样:
if (event != null) {
gestureDetector.onTouchEvent(event)
}
但是
event
这里永远不会为空,并且在查看它的属性时,我没有看到任何可以检查以确定它是否会导致崩溃的内容。
另一个潜在的线索是,只有在调用
onScroll
时我的手指仍在屏幕上时才会调用 onTouchEvent
。通常情况就是这样,但我尝试像这样延迟它的调用:
val runnable = Runnable {
gestureDetector.onTouchEvent(event)
}
App.instance.timerHandler.postDelayed(runnable, 1000)
然后,如果我执行滚动并在一秒钟内将手指从屏幕上抬起,则不会调用
onScroll
并且不会发生崩溃。如果我执行滚动并将手指放在屏幕上超过一秒,则会调用 onScroll
并发生崩溃。
我可以想象两种方法来解决此问题:在调用
OnTouchListener
之前验证我的 onTouchEvent
中的事件,或者找出导致 GestureDetector
向 onScroll
发送无效事件的原因。但我对这两件事都没有想法了。还有人有想法吗?
我在新片段中一步一步地重建此功能以隔离问题。滚动视图包裹在
TextView
周围,以便使文本视图可滑动(惯性滚动),但我在该文本视图上也有一个 OnTouchListener
,这样我就可以检测捏合手势,以调整文本大小。显然,这正在吸收 onDown
事件,因此周围的滚动视图没有收到它。
我假设有一种方法可以在两个视图之间共享
onDown
事件,但在我的情况下,将捏合手势检测器从内部文本视图移动到外部滚动视图更容易,因此滚动手势检测器和捏合手势探测器处于同一视图上——类似这样:
val scrollGestureDetector = GestureDetector(this.fragment.activity, ScrollGestureListener(scrollView))
val pinchGestureDetector = GestureDetector(this.fragment.activity, PinchGestureListener(scrollView))
scrollView.setOnTouchListener(OnTouchListener { view, event ->
scrollGestureDetector.onTouchEvent(event)
pinchGestureDetector.onTouchEvent(event)
return@OnTouchListener false
})
internal inner class ScrollGestureListener(view: View) : GestureDetector.SimpleOnGestureListener() {
override fun onScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
[email protected]()
return true
}
}
internal inner class PinchGestureListener(view: View) : ScaleGestureDetector.SimpleOnScaleGestureListener() {
override fun onScale(detector: ScaleGestureDetector): Boolean {
[email protected]()
return true
}
}
我也遇到了这个问题,对我有用的解决方案之一是将侦听器包装在
java
类中,并将 MotionEvent
设为 Nullable
class CustomSimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener {
private final CustomScrollGestureListener listener;
public CustomSimpleOnGestureListener(CustomScrollGestureListener listener) {
this.listener = listener;
}
@Override
public boolean onScroll(@Nullable MotionEvent e1, @Nullable MotionEvent e2, float distanceX, float distanceY) {
return listener.onScroll(distanceX, distanceY);
}
}
然后将监听器设置为对象
private val mListener = CustomSimpleOnGestureListener(object : CustomScrollGestureListener {
override fun onScroll(distanceX: Float, distanceY: Float): Boolean {
// Your code
return true
}
})