Я не спрашиваю, как обрабатывать сенсорные события, но что происходит за кулисами? Если есть несколько вложенных виджетов, в каком порядке они видят события? Имеет ли разработчик контроль над этим? В идеале я хотел бы получить документ по этому вопросу.
Как поступают события Android touch?
Ответ 1
С точки зрения деятельности:
События касания доставляются сначала в Activity.dispatchTouchEvent. Это где вы можете поймать их в первую очередь.
Здесь они отправляются в Window, где они пересекают иерархию View, в том порядке, в котором виджеты, которые нарисованы последними (поверх других виджетов), имеют возможность обработать touch в View.onTouchEvent. Если какой-либо вид возвращает true в onTouchEvent, то обход останавливается, а другие представления не получают события касания.
Наконец, если View не затрагивает Touch, он доставлен в Activity.onTouchEvent.
Это все ваше управление. И логично, что то, что вы видите нарисованным поверх чего-то другого, имеет возможность обработать событие касания перед чем-то, что находится под ним.
Ответ 2
Давайте посмотрим на визуальный пример.
Когда происходит событие касания, сначала все получают уведомление о событии, начиная с действия и заканчивая представлением сверху. Затем каждому дается возможность обработать событие, начиная с вида сверху (вид с самым высоким Z-порядком в области касания) и возвращаясь к действию. Таким образом, действие первым узнает об этом и последним получит шанс справиться с этим.
Если какая-то 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
superDispatchTouchEvent()
.DecorView
- это то, что обрабатывает строку состояния, панель навигации, область содержимого и т.д. На самом деле это просто подклассFrameLayout
, который сам является подклассомViewGroup
. - Следующим, кто получит уведомление (поправьте меня, если я ошибаюсь), является просмотр содержимого вашей деятельности. Это то, что вы задаете в качестве корневого макета своей деятельности в xml при создании макета в редакторе макетов Android Studio. Поэтому, независимо от того, выберете ли вы
RelativeLayout
,LinearLayout
илиConstraintLayout
, все они являются подклассамиViewGroup
. А ViewGroup получает уведомление о событии касания вdispatchTouchEvent()
. Это ViewGroup A на моих диаграммах выше. ViewGroup
уведомит всех своих детей о событии касания, включая всехViewGroup
детей. Это ViewGroup B на моих диаграммах выше.- В любом месте
ViewGroup
может замкнуть процесс уведомления, вернувtrue
дляonInterceptTouchEvent()
. - Предполагая, что
ViewGroup
не будет сокращать уведомления, естественный конец строки для уведомлений - когда вызывается ViewdispatchTouchEvent()
. - Теперь пришло время начать обрабатывать события. Если есть
OnTouchListener
, то он получает первый шанс обработать событие касания с помощьюonTouch()
. В противном случае видonTouchEvent()
справляется с этим. - Теперь все ViewGroups рекурсивно вверх по линии получают возможность обрабатывать событие касания так же, как это делал
View
. Хотя я не указал это на диаграмме выше,ViewGroup
является подклассомView
, поэтому все, что я описал вOnTouchListener.onTouch()
иonTouchEvent()
, также применимо к ViewGroups. - Наконец,, если никто не хотел этого, Activity также получает последний шанс обработать событие с помощью
onTouchEvent()
.
FAQ
Когда мне нужно будет переопределить 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 (подробное видео от разработчика Google)
- Внутренний интерфейс Android: обработка событий сенсорного конвейера
- Управление сенсорными событиями в ViewGroup (документы для Android)
- Входные события (документы для Android)
- Жесты и сенсорные события
Ответ 3
следующий ответ Suragch,
псевдокод:
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
if (onInterceptTouchEvent(ev) {
consume = onTouchEvent(ev);
} else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
исх: Android 开发 艺术 探索
Ответ 4
Я подготовил диаграмму высокого уровня, которая должна иллюстрировать простой процесс. Пожалуйста, не стесняйтесь комментировать и редактировать его.