Как доставляются события 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. Я оставил их выше, потому что мы обычно не должны взаимодействовать с ними. Тем не менее, я буду включать их ниже. Описание ниже следует за событием касания через исходный код. Вы можете нажать на ссылку, чтобы увидеть фактический исходный код.

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

  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. При условии нет ViewGroup сократить уведомления, естественный конец строки для уведомлений, когда представление dispatchTouchEvent() получить называется.
  8. Теперь пришло время начать обрабатывать события. Если есть OnTouchListener затем он получает первый шанс при обработке события касания onTouch(), В противном случае, вид onTouchEvent() получает справиться с этим.
  9. Теперь все ViewGroups рекурсивно вверх по линии получают возможность обрабатывать событие касания так же, как View сделал. Хотя я не указал это на диаграмме выше, ViewGroup это View подкласс, так что все, что я описал о OnTouchListener.onTouch() а также onTouchEvent() также относится к ViewGroups.
  10. Наконец, если никто не хотел этого, 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 является мультитач, события также запускаются, когда другие пальцы ("указатели") касаются экрана.

Дальнейшее обучение

Я подготовил диаграмму высокого уровня, которая должна иллюстрировать простой поток.

  • dispatchTouchEvent() - Activity, ViewGroup, View
  • onInterceptTouchEvent() - ViewGroup
  • onTouch() - ViewGroup, View. С помощьюsetOnTouchListener()
  • onTouchEvent() - Activity, ViewGroup, View

[iOS onTouch]

Следуя ответу Сурагча,

псевдокод:

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

        return consume;
    }

исх:Android开发艺术探索

Другие вопросы по тегам