Я разрабатываю клавиатуру Android, которая в основном представляет собой пользовательский LinearLayout
(с именем KeyboardView
) с LinearLayout
s (представляющий строки), который имеет набор TextView
s (представляющий клавиши). Если бы он был представлен в XML, это было бы что-то вроде:
<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
и D
и режимом сна 2 секунды. При их нажатии друг за другом принимаются только события касания (ACTION_DOWN
, ACTION_MOVE
, ACTION_UP
) для A
, B
и D
. Однако в некоторых случаях ACTION_CANCEL
получено.
Чтобы преувеличить проблему, я попытался установить режим сна на 5 секунд и щелкнуть вторую клавишу во время сна касанием первой клавиши. В этом случае никакие события для второго щелчка никогда не принимаются, но все сенсорные события для первой клавиши принимаются.
Для приведенного выше сценария три полученных события выглядят следующим образом (при выводе в журнал). Обратите внимание, что второй щелчок (который никогда не получен) находится между журналом № 1 и № 2:
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 }
Понятия не имею, является ли это частью платформы Android или я что-то упускаю. Я мог бы "просто" поместить проделанную работу onTouchEvent
в поток, но я боялся бы, что она просто охватывает реальную проблему, и я вообще предпочел бы избегать потоков, если это возможно. Я также пытался переопределить onInterceptTouchEvent
и всегда позволять ему возвращаться true
(чтобы избежать ACTION_CANCEL
), но в некоторых случаях ACTION_CANCEL
все еще происходило.