Отсутствие сенсорных событий, когда работа выполняется в потоке пользовательского интерфейса - PullRequest
0 голосов
/ 21 мая 2019

Я разрабатываю клавиатуру 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 все еще происходило.

Ответы [ 2 ]

0 голосов
/ 11 июня 2019

Я наконец узнал, что некоторые события игнорируются, когда я настраиваю его следующим образом. При добавлении 5 секунд сна система выводит следующее (за которым следует список того, что использует ЦП):

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:

Это то, что, как я полагаю, приводит к тому, что некоторые события, к сожалению, игнорируются. И поэтому спящий поток не является допустимым тестом в этом случае.

В конце концов меня вдохновил ответ Гейба Сечана, так как логично, что когда пользователь печатает быстро, он может коснуться другим пальцем, прежде чем поднять последний. Так что теперь мы также поддерживаем ACTION_POINTER_DOWN и ACTION_POINTER_UP, и это, кажется, работает намного лучше.

0 голосов
/ 21 мая 2019

События касания могут быть объединены, особенно события перемещения. Вы проверяете это?

Существует 0 шансов сенсорных событий, просто пропущенных, фреймворк этого не делает. Объединение происходит, если его одного типа. Кроме того, если вы получите ОТМЕНА, это означает, что сенсорные события переходят в другой вид, и вы больше не будете получать, пока все пальцы не будут подняты. Поэтому, если вы видите отмену, вам следует выяснить, кто крадет ваши прикосновения. onInterceptTouchEvent не исправит это, так как это влияет только на ваши дети, а не на вас. Во всяком случае, это ухудшит положение.

Также проверьте ACTION_POINTER_DOWN и ACTION_POINTER_UP. Это происходит вместо подъема и опускания, если несколько пальцев касаются одновременно. Это может быть вызвано вами случайно (или экран может неправильно регистрироваться, особенно если вы почти касаетесь, иногда может быть паразитная емкость).

...