Как доставляются события Android Touch? - PullRequest
28 голосов
/ 17 сентября 2011

Я не спрашиваю, как обрабатывать сенсорные события, но что происходит за кулисами?Если есть несколько вложенных виджетов, в каком порядке они видят события?Имеет ли разработчик контроль над этим?В идеале я хотел бы документ на эту тему.

Ответы [ 3 ]

55 голосов
/ 21 октября 2017

Давайте посмотрим на визуальный пример.

enter image description here

Когда происходит событие касания, сначала все уведомляются о событии, начиная сАктивность и продвижение до самого верха.Затем каждому дается возможность обработать событие, начиная с вида сверху (вид с самым высоким Z-порядком в области касания) и возвращаясь к действию.Таким образом, действие первым узнает об этом и последним получит шанс справиться с этим.

enter image description here

Если какая-то группа ViewGroup хочет обработатькоснитесь события сразу (и не дайте никому другому по очереди), тогда оно может просто вернуть true в onInterceptTouchEvent().Активность не имеет onInterceptTouchEvent(), но вы можете переопределить dispatchTouchEvent(), чтобы сделать то же самое.

Если View (или ViewGroup) имеет OnTouchListener, то событие касания обрабатывается OnTouchListener.onTouch().В противном случае он обрабатывается onTouchEvent().Если onTouchEvent() возвращает true для какого-либо события касания, тогда обработка на этом останавливается.Никто в дальнейшем не получит шанса на это.

Более подробное объяснение

Приведенная выше диаграмма делает вещи немного проще, чем они есть на самом деле.Например, между Activity и ViewGroup A (корневой макет) также есть Window и DecorView.Я оставил их выше, потому что мы обычно не должны взаимодействовать с ними.Тем не менее, я буду включать их ниже.Описание ниже следует за событием касания через исходный код.Вы можете щелкнуть ссылку, чтобы увидеть фактический исходный код.

(Обновление: исходный код был обновлен, поэтому номера строк отключены, но нажатие на ссылки все равно приведет вас к нужному файлуПросто выполните поиск по названию метода.)

  1. Действия из dispatchTouchEvent() уведомлены о событии касания.Событие касания передается как MotionEvent, которое содержит координаты x, y, время, тип события и другую информацию.
  2. Событие касания отправляется в superDispatchTouchEvent() окна..Window - абстрактный класс.Фактическая реализация: PhoneWindow.
  3. Следующим в очереди на получение уведомления является DecorView's superDispatchTouchEvent().DecorView - это то, что обрабатывает строку состояния, панель навигации, область содержимого и т. Д. На самом деле это просто FrameLayout подкласс , который сам по себе является подклассом ViewGroup.
  4. Следующим, кто получит уведомление (поправьте меня, если я ошибаюсь), является просмотр содержимого вашей деятельности.Это то, что вы устанавливаете в качестве корневого макета своей деятельности в xml при создании макета в редакторе макетов Android Studio.Поэтому, выбираете ли вы RelativeLayout, LinearLayout или ConstraintLayout, все они являются подклассами ViewGroup.А ViewGroup получает уведомление о событии касания в dispatchTouchEvent().Это ViewGroup A на моих диаграммах выше.
  5. ViewGroup уведомит всех детей о событии касания, включая любых ViewGroup детей.Это ViewGroup B в моих диаграммах выше.
  6. В любом месте по пути ViewGroup может замкнуть процесс уведомления, возвращая true для onInterceptTouchEvent().
  7. Если предположить, что no ViewGroup не прервет короткие уведомления, естественным концом строки для уведомлений будет то, когда View будет вызван dispatchTouchEvent() get.
  8. Теперь пришло время начать обработку событий. Если есть OnTouchListener, то он получает первый шанс обработать событие касания с помощью onTouch(). В противном случае , * View 110 * *onTouchEvent() получает справиться с этим.
  9. Теперь все ViewGroups рекурсивно вверх по линии получают возможность обрабатывать событие прикосновения так же, как это делал View.Хотя я не указал это на диаграмме выше, ViewGroup является подклассом View, поэтому все, что я описал в отношении OnTouchListener.onTouch() и onTouchEvent(), также относится к ViewGroups.
  10. Наконец, , если никто больше не хотел этого, 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 является мультитач, события также запускаются, когда другие пальцы («указатели») касаются экрана.

Дальнейшее изучение

23 голосов
/ 17 сентября 2011

С точки зрения активности:

События касания доставляются сначала в Activity.dispatchTouchEvent. Здесь вы можете поймать их первыми.

Здесь они отправляются в Window, где они пересекают иерархию View, в таком порядке, что виджеты, которые рисуются последними (поверх других виджетов), имеют шанс обработать касание в View.onTouchEvent первым. Если какой-то вид возвращает значение true в onTouchEvent, то обход прекращается, и другие виды не получают сенсорное событие.

Наконец, если ни один просмотр не использует сенсорный ввод, он доставляется в Activity.onTouchEvent.

Это все, что вы контролируете. И логично, что то, что вы видите нарисованным поверх чего-то другого, имеет шанс обработать событие касания, прежде чем что-то нарисовано под ним.

1 голос
/ 27 апреля 2018

после ответа Сурагча,

псевдокод:

   public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean consume = false;
        if (onInterceptTouchEvent(ev) {
            consume = onTouchEvent(ev);
        } else {
            consume = child.dispatchTouchEvent(ev);
        }

        return consume;
    }

исх: Android 开发 艺术 探索

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...