Давайте посмотрим на визуальный пример.
Когда происходит событие касания, сначала все уведомляются о событии, начиная сАктивность и продвижение до самого верха.Затем каждому дается возможность обработать событие, начиная с вида сверху (вид с самым высоким 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's
superDispatchTouchEvent()
.DecorView
- это то, что обрабатывает строку состояния, панель навигации, область содержимого и т. Д. На самом деле это просто FrameLayout
подкласс , который сам по себе является подклассом ViewGroup
. - Следующим, кто получит уведомление (поправьте меня, если я ошибаюсь), является просмотр содержимого вашей деятельности.Это то, что вы устанавливаете в качестве корневого макета своей деятельности в xml при создании макета в редакторе макетов Android Studio.Поэтому, выбираете ли вы
RelativeLayout
, LinearLayout
или ConstraintLayout
, все они являются подклассами ViewGroup
.А ViewGroup получает уведомление о событии касания в dispatchTouchEvent()
.Это ViewGroup A на моих диаграммах выше. ViewGroup
уведомит всех детей о событии касания, включая любых ViewGroup
детей.Это ViewGroup B в моих диаграммах выше. - В любом месте по пути
ViewGroup
может замкнуть процесс уведомления, возвращая true
для onInterceptTouchEvent()
. - Если предположить, что no
ViewGroup
не прервет короткие уведомления, естественным концом строки для уведомлений будет то, когда View будет вызван dispatchTouchEvent()
get. - Теперь пришло время начать обработку событий. Если есть
OnTouchListener
, то он получает первый шанс обработать событие касания с помощью onTouch()
. В противном случае , * View 110 * *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 является мультитач, события также запускаются, когда другие пальцы («указатели») касаются экрана.
Дальнейшее изучение