Можете ли вы определить, какое дочернее представление было затронуто при обработке события в ViewGroup?

Рассмотрим эту гипотетическую иерархию...

LinearLayoutA <-- I want to handle the touches here...
  |
  +-SomeViewX
  +-SomeOtherViewY
  +-LinearLayoutB
      |
      +-CustomView1 <-- for these three CustomView objects
      +-CustomView2
      +-CustomView3

Что я хотел бы сделать, так это узнать, какой (большой) дочерний вид был затронут, и обработать его из LinearLayoutA. Кроме того, я не контролирую эти представления, поэтому я не могу просто заставить их обрабатывать прикосновения внутри и делегировать их родителям.

В настоящее время я вручную добавляю сенсорные прослушиватели в CustomViews 1-3, но это требует большой «шаблонной» работы, а также означает, что я не буду получать уведомления, если кто-то нажимает на SomeViewX или SomeOtherViewY, только те, которые я прикрепил к слушателю. к.

Теперь в других языках, таких как C # с WPF, если вы обрабатываете событие в эквиваленте LinearLayoutA, часть полезной нагрузки события является источником, то есть представлением, которое инициировало прикосновение, но я не знаю ничего подобного в Андроид.

Все примеры, которые я видел, требуют циклического прохождения дочерних элементов и их проверки на попадание, а затем устранения неоднозначности по z-порядку, если есть перекрытие, и когда вы идентифицировали один дочерний элемент, вам нужно пройти через его дочерние элементы и повторить.

Итак, существует ли встроенный или «Androidy» способ узнать, какой дочерний элемент в ViewGroup был затронут без ручного повторения и проверки на попадание или ручного присоединения слушателей ко всем его дочерним элементам?


person Mark A. Donohoe    schedule 20.07.2017    source источник
comment
OnTouchListener принимает View в качестве параметра, поэтому вы можете использовать один и тот же прослушиватель для всех трех объектов и узнать, какой из них был поражен, посмотрев на аргумент. Однако это по-прежнему требует ручного подключения одного прослушивателя.   -  person Elan Hamburger    schedule 20.07.2017
comment
Да, я пытался избежать ручного подключения слушателя, тем более что некоторые из этих представлений создаются вне моего контроля и/или динамически. Значит, тогда я прав... нет способа узнать исходное представление события касания, используя путь обработки событий по умолчанию?   -  person Mark A. Donohoe    schedule 20.07.2017
comment
Поскольку у вас нет контроля над представлениями, я думаю, вам придется где-то прикрепить прослушиватель, но повторное использование одного объекта OnTouchListener должно сделать это относительно безболезненным.   -  person Elan Hamburger    schedule 20.07.2017
comment
Спасибо. Если вы поместите это в ответ и если через день или около того никто не придумает ничего лучше, я отмечу ваш ответ как принятый.   -  person Mark A. Donohoe    schedule 20.07.2017
comment
Я собираюсь провести еще немного исследований, когда у меня будет больше времени сегодня вечером, прежде чем публиковать что-либо в качестве ответа. На самом деле должен быть способ сделать это, но все, что я видел до сих пор, упускает именно этот случай.   -  person Elan Hamburger    schedule 20.07.2017
comment
Действительно цените усилия! Спасибо! :)   -  person Mark A. Donohoe    schedule 20.07.2017
comment
Извините за задержку, на работе стало немного суматошно, и у меня не было много времени для написания кода для себя. Ответ опубликован.   -  person Elan Hamburger    schedule 28.07.2017


Ответы (1)


OnTouchListener для ViewGroups передается в ViewGroup в качестве его аргумента View, а не дочернего элемента в этом View, которого фактически коснулись. Поскольку у вас нет контроля над источником View в макете, вам придется добавить OnTouchListener вручную извне.

Как упоминалось в моих комментариях, вы можете повторно использовать один и тот же прослушиватель для всех View, присоединив его к каждому из View, которые вы хотите прослушивать. Если вы добавляете эти View динамически, должно быть тривиально также вызывать setOnTouchListener() для них при их создании.

Дополнительную информацию по этому вопросу см. в руководстве Android по управлению сенсорными событиями в ViewGroup который позволяет родителю перехватывать события касания дочернего элемента, но не наоборот.

person Elan Hamburger    schedule 27.07.2017
comment
Да, этого я и боялся. Это не сработает, потому что это добавляет слушателей к прямым детям, тогда как то, что я хочу, может быть (великим) внуком. Жаль, что Android не всплывает при касании вместе с событием, как в Windows. Опять же, у вас есть отправитель (который будет ViewGroup), но у вас также есть источник, который будет фактически затронутым представлением, независимо от того, где оно находится в иерархии (и при условии, что никто выше вас не обработал его). - person Mark A. Donohoe; 28.07.2017
comment
Вам не нужно использовать непосредственного родителя, чтобы применить прослушиватель. Везде, где вы получаете View, который вы добавляете, вызовите setOnTouchListener() и передайте ему объект OnTouchListener. Вы можете сохранить этот объект в любом месте текущей активности, поэтому не имеет значения, где в иерархии находится View. - person Elan Hamburger; 28.07.2017
comment
Я не слежу за тобой здесь. Скажем, я загружаю макет, который сам включает в себя макет, который сам включает в себя макет. Скажем, в самом глубоком макете используются элементы управления, называемые DrumPads. Я хочу знать, какой DrumPad был нажат. Если мне нужно добавить к нему обработчик, то мне придется пройтись по всем деревьям в поисках их для присоединения, не так ли? Кажется, но много. Я знаю... Я избалован. Я просто привык добавлять один слушатель, который слушает все под ним. - person Mark A. Donohoe; 28.07.2017
comment
Найдите свой addView(), где вы добавляете DrumPads в макет. Прямо перед этой строкой вызовите setOnTouchListener() на каждом DrumPad и передайте его слушателю. Нет необходимости ползать, потому что слушатель уже будет прикреплен. - person Elan Hamburger; 28.07.2017
comment
Я думаю, я не ясно. Я не контролирую этот макет или его иерархию, поэтому я не добавляю их в макет. Это может загрузить макет с глубиной иерархии 100 уровней. Мне пришлось бы просмотреть их все. Кроме того, это означает, что я должен знать об этих макетах. То, что я хотел, было похоже на WPF. Планировка не имеет значения. Вы всегда можете узнать, кто инициировал событие, независимо от того, насколько глубоко в иерархии. Вы просто просите первоисточник. Я предполагаю, что ответ: «Вы не можете, автоматически». - person Mark A. Donohoe; 28.07.2017
comment
Понимаю. Я думал, вы имеете в виду, что у вас нет контроля над DrumPads, но вы говорите, что у вас также нет контроля над макетом, который содержит DrumPads. Извините за путаницу. - person Elan Hamburger; 28.07.2017
comment
Да. И не совсем барабанные пэды в моем примере. Понятия не имею, почему я это сказал! ржу не могу. Итак, да, короче говоря, Android, похоже, не может сказать мне, что мне нужно, и это позор, потому что система четко знает, кто инициировал прикосновение, потому что она уже знает, как направить его к ним. Раз можно перехватить, жаль, что не сказали, кого перехватывают. В конце концов, это кому они отправляют TOUCHES_CANCELED, так что опять же, это известно. Только не потребителю API. - person Mark A. Donohoe; 28.07.2017