Как поступают события Android touch?

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

Ответ 1

С точки зрения деятельности:

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

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

Наконец, если View не затрагивает Touch, он доставлен в Activity.onTouchEvent.

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

Ответ 2

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

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

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

Ответ 3

следующий ответ Suragch,

псевдокод:

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

        return consume;
    }

исх: Android 开发 艺术 探索