Как доставляются события Android Touch?
Я не спрашиваю, как обрабатывать сенсорные события, но что происходит за кулисами? Если есть несколько вложенных виджетов, в каком порядке они видят события? Имеет ли разработчик контроль над этим? В идеале я хотел бы документ на эту тему.
3 ответа
С точки зрения активности:
События касания доставляются сначала в Activity.dispatchTouchEvent. Это где вы можете поймать их в первую очередь.
Здесь они отправляются в Window, где они пересекают иерархию View, в таком порядке, что виджеты, которые рисуются последними (поверх других виджетов), имеют шанс обработать касание в View.onTouchEvent первым. Если какой-то вид возвращает значение true в onTouchEvent, то обход прекращается, а другие виды не получают событие касания.
Наконец, если никакой View не использует сенсорный ввод, он доставляется в Activity.onTouchEvent.
Это все, что вы контролируете. И логично, что то, что вы видите нарисованным поверх чего-то другого, имеет шанс обработать событие касания до того, как что-то нарисовано под ним.
Давайте посмотрим на визуальный пример.
Когда происходит событие касания, сначала все уведомляются о событии, начиная с действия и заканчивая представлением сверху. Затем каждому дается возможность обработать событие, начиная с представления сверху и заканчивая заданием. Таким образом, "Активность" первым узнает об этом и последним будет дан шанс справиться с этим.
Если какая-то ViewGroup хочет обработать сенсорное событие сразу (и не дать никому другому в очереди), она может просто вернуться true
в его onInterceptTouchEvent()
, Активность не имеет onInterceptTouchEvent()
но вы можете переопределить dispatchTouchEvent()
сделать то же самое.
Если View (или ViewGroup) имеет OnTouchListener
затем событие касания обрабатывается OnTouchListener.onTouch()
, В противном случае это обрабатывается onTouchEvent()
, Если onTouchEvent()
возвращается true
для любого сенсорного события обработка останавливается там. Никто из ниже по линии не получает шанс на это.
Более подробное объяснение
Приведенная выше диаграмма делает вещи немного проще, чем они есть на самом деле. Например, между Activity и ViewGroup A (корневой макет) также есть Window и DecorView. Я оставил их выше, потому что мы обычно не должны взаимодействовать с ними. Тем не менее, я буду включать их ниже. Описание ниже следует за событием касания через исходный код. Вы можете нажать на ссылку, чтобы увидеть фактический исходный код.
(Обновление: исходный код был обновлен, поэтому номера строк теперь отключены, но нажатие на ссылки все равно приведет вас к нужному файлу. Просто выполните поиск имени метода.)
- Деятельность
dispatchTouchEvent()
уведомлено о сенсорном событии. Событие касания передается какMotionEvent
, который содержит координаты x,y, время, тип события и другую информацию. - Событие касания отправляется в окно
superDispatchTouchEvent()
,Window
это абстрактный класс. Фактическая реализацияPhoneWindow
, - Следующим в очереди, чтобы получить уведомление, является DecorView's
superDispatchTouchEvent()
,DecorView
это то, что обрабатывает строку состояния, панель навигации, область содержимого и т. д. Это на самом деле простоFrameLayout
подкласс, который сам является подклассомViewGroup
, - Следующим, кто получит уведомление (поправьте меня, если я ошибаюсь), является просмотр содержимого вашей деятельности. Это то, что вы устанавливаете в качестве корневого макета своей деятельности в XML при создании макета в редакторе макетов Android Studio. Так ли вы выбираете
RelativeLayout
,LinearLayout
илиConstraintLayout
все они являются подклассамиViewGroup
, И ViewGroup получает уведомление о сенсорном событии вdispatchTouchEvent()
, Это ViewGroup A в моих диаграммах выше. -
ViewGroup
уведомит всех своих детей о событии касания, включаяViewGroup
дети. Это ViewGroup B в моих диаграммах выше. - Где-нибудь по пути,
ViewGroup
может замкнуть процесс уведомления, вернувtrue
заonInterceptTouchEvent()
, - При условии нет
ViewGroup
сократить уведомления, естественный конец строки для уведомлений, когда представлениеdispatchTouchEvent()
получить называется. - Теперь пришло время начать обрабатывать события. Если есть
OnTouchListener
затем он получает первый шанс при обработке события касанияonTouch()
, В противном случае, видonTouchEvent()
получает справиться с этим. - Теперь все ViewGroups рекурсивно вверх по линии получают возможность обрабатывать событие касания так же, как
View
сделал. Хотя я не указал это на диаграмме выше,ViewGroup
этоView
подкласс, так что все, что я описал оOnTouchListener.onTouch()
а такжеonTouchEvent()
также относится к ViewGroups. - Наконец, если никто не хотел этого, Activity также получает последний шанс обработать событие с помощью
onTouchEvent()
,
Часто задаваемые вопросы
Когда мне когда-нибудь понадобится переопределить dispatchTouchEvent()
?
Переопределите его в Упражнении, если вы хотите перехватить сенсорное событие, прежде чем какое-либо из представлений получит шанс на него. Для ViewGroup (включая корневой вид) просто переопределите onInterceptTouchEvent()
а также onTouchEvent()
,
Когда мне когда-нибудь понадобится переопределить onInterceptTouchEvent()
?
Если вы просто хотите шпионить за сенсорными уведомлениями, вы можете сделать это здесь и вернуться false
,
Однако основная цель переопределения этого метода - позволить ViewGroup обрабатывать событие касания определенного типа, в то время как дочерний процесс обрабатывает другой тип. Например, ScrollView
делает это для прокрутки, позволяя дочернему элементу обрабатывать что-то вроде нажатия кнопки. И наоборот, если дочерний вид не хочет, чтобы его родитель украл событие касания, он может вызвать requestDisallowTouchIntercept()
,
Какие типы сенсорных событий?
Основными из них являются
ACTION_DOWN
- Это начало сенсорного события. Вы должны всегда возвращатьсяtrue
дляACTION_DOWN
событие вonTouchEvent
если вы хотите обработать сенсорное событие. В противном случае вы не получите больше событий, доставленных вам.ACTION_MOVE
- Это событие постоянно срабатывает, когда вы перемещаете палец по экрану.ACTION_UP
- Это последнее событие сенсорного события.
Второе место ACTION_CANCEL
, Это вызывается, если ViewGroup вверх по дереву решает перехватить событие касания.
Вы можете посмотреть другие виды MotionEvents здесь. Поскольку Android является мультитач, события также запускаются, когда другие пальцы ("указатели") касаются экрана.
Дальнейшее обучение
- Android onTouchEvent Часть 1, Часть 2 и Часть 3 (видео YouTube - хорошее резюме некоторых ссылок ниже)
- Освоение системы Android Touch (подробное видео от разработчика Google)
- Внутренний интерфейс Android: конвейерная обработка сенсорных событий View
- Управление сенсорными событиями в ViewGroup (документы для Android)
- Входные события (документы для Android)
- Жесты и сенсорные события
Я подготовил диаграмму высокого уровня, которая должна иллюстрировать простой поток.
dispatchTouchEvent()
-Activity
,ViewGroup
,View
onInterceptTouchEvent()
-ViewGroup
onTouch()
-ViewGroup
,View
. С помощьюsetOnTouchListener()
onTouchEvent()
-Activity
,ViewGroup
,View
Следуя ответу Сурагча,
псевдокод:
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
if (onInterceptTouchEvent(ev) {
consume = onTouchEvent(ev);
} else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
исх:Android开发艺术探索