我正在开发一个Android键盘,它基本上是具有LinearLayout
的{{1}}(代表行)的自定义KeyboardView
(命名为LinearLayout
) s(代表键)。如果应以XML表示,则类似于:
TextView
在<KeyboardView> <!-- extends LinearLayout -->
<LinearLayout> <!-- row #1 -->
<TextView /> <!-- key #1 -->
... <!-- more TextViews -->
</LinearLayout>
... <!-- more rows/LinearLayouts -->
</KeyboardView>
中,我会覆盖KeyboardView
,如果用户触摸onTouchEvent
之一的边界内,则会调用一个控制器(该控制器会弄清楚应该如何处理该键)。这意味着TextViews
的子代永远不会获得焦点事件。出于各种原因以这种方式完成此操作,例如因此可以处理键盘的滑动操作。
在大多数情况下,此方法工作正常,但是我们得到了一些有关键盘并非总是对所有触摸事件做出反应的报告。重现这种行为可能很困难,但是我们已经确认,这不是由用户触摸键之间的间隙引起的(因为没有间隙),因此我们认为在某些情况下,我们需要花费太多时间弄清楚该键应该做什么-KeyboardView
的执行时间可能太长。
为了检验该理论,我最终在onTouchEvent
上添加了Thread.sleep
(以模拟许多工作)。这似乎在某种程度上重现了该问题,因为很明显,并非所有触摸事件都被接收到。似乎系统具有某种队列,以便接收某些事件,但忽略一些事件。我制作了一个简单的键盘,仅包含键onTouchEvent
,A
,B
和C
,并且睡眠了2秒。当一个接一个地按下它们时,仅接收到D
,ACTION_DOWN
和ACTION_MOVE
的触摸事件(ACTION_UP
,A
,B
)。但是在某些情况下会收到D
。
为了夸大问题,我尝试将睡眠设置为5秒,然后在第一次按键触摸的睡眠期间单击第二个按键。在这种情况下,将永远不会收到第二次单击的事件-但是会收到第一把按键的所有触摸事件。
对于上述情况,三个接收到的事件如下(当输出到日志时)。请注意,第二次按键单击(从未收到)在日志#1和#2之间:
ACTION_CANCEL
我不知道它是否只是Android框架的一部分,或者我缺少什么。我可以“只是”将完成的工作MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=181.0, y[0]=536.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=116942926, downTime=116942926, deviceId=8, source=0x1002 }
MotionEvent { action=ACTION_MOVE, actionButton=0, id[0]=0, x[0]=181.0, y[0]=536.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=2, eventTime=116942992, downTime=116942926, deviceId=8, source=0x1002 }
MotionEvent { action=ACTION_UP, actionButton=0, id[0]=0, x[0]=181.0, y[0]=536.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=116942999, downTime=116942926, deviceId=8, source=0x1002 }
放入线程中,但是我担心它仅涵盖了实际问题,并且我一般会尽可能避免使用线程。我还尝试覆盖onTouchEvent
并始终让它返回onInterceptTouchEvent
(以试图避免true
),但是在某些情况下ACTION_CANCEL
仍然会发生。
答案 0 :(得分:0)
可以组合触摸事件,尤其是移动事件。您要检查吗?
触摸事件被完全跳过的可能性为0,该框架不会这样做。如果类型相同,则确实会发生组合。另外,如果您取消,这意味着触摸事件将进入另一种视图,并且直到所有手指都抬起时您再也不会收到任何信息-因此,如果您看到取消,则应该弄清楚谁在窃取您的触摸。 onInterceptTouchEvent不会解决此问题,因为这只会影响您的孩子而不是您的孩子。如果有的话,会使情况变得更糟。
还要检查ACTION_POINTER_DOWN和ACTION_POINTER_UP。如果同时触摸多个手指,则会发生这些事件,而不是上下移动。您可能是意外造成的(或者屏幕可能未正确注册,尤其是在您几乎要触摸时,有时可能会有杂散电容)。
答案 1 :(得分:0)
我终于发现,当我这样设置事件时,一些事件被忽略了。当添加5秒钟的睡眠时,系统将输出此消息(随后是使用CPU的列表):
2019-06-11 17:06:45.200 905-990/? E/ActivityManager: ANR in [my_app_identifier]
PID: 10365
Reason: Input dispatching timed out (Waiting to send non-key event because the touched window has not finished processing certain input events that were delivered to it over 500.0ms ago. Wait queue length: 12. Wait queue head age: 5490.6ms.)
Load: 0.0 / 0.0 / 0.0
CPU usage from 284899ms to 0ms ago (2019-06-11 17:02:00.002 to 2019-06-11 17:06:44.902) with 99% awake:
我认为这会导致不幸地忽略某些事件。因此,在这种情况下,休眠线程不是有效的测试。
最后,我受到了Gabe Sechan的回答的启发,因为按照逻辑,当用户快速键入内容时,在抬起最后一根手指之前,他们可能会用另一根手指触摸。因此,现在我们也支持ACTION_POINTER_DOWN
和ACTION_POINTER_UP
,这似乎工作得更好。