Najlepszym miejscem do odkrycia tego jest kod źródłowy. Dokumenty są bardzo nieodpowiednie w wyjaśnianiu tego.
dispatchTouchEvent jest faktycznie zdefiniowany w Activity, View i ViewGroup. Pomyśl o tym jak o kontrolerze, który decyduje, jak kierować zdarzenia dotykowe.
Na przykład najprostszym przypadkiem jest View.dispatchTouchEvent, który przekieruje zdarzenie touch do OnTouchListener.onTouch, jeśli jest zdefiniowany, lub do metody rozszerzenia onTouchEvent .
W przypadku ViewGroup.dispatchTouchEvent rzeczy są znacznie bardziej skomplikowane. Musi dowiedzieć się, który z jego widoków potomnych powinien otrzymać zdarzenie (wywołując child.dispatchTouchEvent). Jest to w zasadzie algorytm testowania trafień, w którym określa się, który prostokąt obwiedni widoku potomnego zawiera współrzędne punktu styku.
Ale zanim zdoła wywołać zdarzenie do odpowiedniego widoku potomnego, rodzic może szpiegować i / lub przechwytywać zdarzenia razem. Właśnie po to jest onInterceptTouchEvent . Wywołuje więc tę metodę najpierw przed wykonaniem testu trafień, a jeśli zdarzenie zostało przejęte (przez zwrócenie wartości true z onInterceptTouchEvent), wysyła ACTION_CANCEL do widoków potomnych, aby mogły zrezygnować z przetwarzania zdarzenia dotyku (z poprzednich zdarzeń dotyku), a następnie wszystkie zdarzenia dotykowe na poziomie nadrzędnym są wysyłane do onTouchListener.onTouch (jeśli zdefiniowano) lub onTouchEvent (). Również w takim przypadku onInterceptTouchEvent nigdy nie jest wywoływany ponownie.
Czy chciałbyś nawet zastąpić [Aktywność | Grupa widoków | Widok] .dispatchTouchEvent? O ile nie wykonujesz niestandardowego routingu, prawdopodobnie nie powinieneś.
Głównymi metodami rozszerzenia są ViewGroup.onInterceptTouchEvent, jeśli chcesz szpiegować i / lub przechwytywać zdarzenia dotykowe na poziomie nadrzędnym oraz View.onTouchListener / View.onTouchEvent do obsługi zdarzeń głównych.
Podsumowując, jego zbyt skomplikowana konstrukcja imo, ale Android api bardziej skłania się ku elastyczności niż prostocie.
Ponieważ jest to pierwszy wynik w Google. Chcę podzielić się z Tobą wspaniałą rozmową Dave'a Smitha na Youtube: Opanowanie systemu dotykowego Android, a slajdy są dostępne tutaj . Dzięki temu dobrze zrozumiałem system dotykowy Android:
Jak obsługuje działanie :
Jak dotykają widoków :
Jak obsługuje się ViewGroup :
Podaje również przykładowy kod niestandardowego dotyku na github.com/devunwired/ .
Odpowiedź: Zasadniczo
dispatchTouchEvent()
jest wywoływany na każdejView
warstwie, aby ustalić, czyView
zainteresowany jest w trwającym geście. W sposób ma zdolność do kradzieży zdarzenia dotykowe w swoim -method, zanim byłoby zadzwonić na dzieci. Zatrzyma tylko Dysponowanie jeśli -method zwraca true. Różnicą jest to, że jest dysponowanie i powie czy powinien przechwycić (nie wysyłającej do dzieci) , czy nie (wysyłając do dzieci) .ViewGroup
ViewGroup
dispatchTouchEvent()
dispatchTouchEvent()
ViewGroup
ViewGroup
onInterceptTouchEvent()
dispatchTouchEvent()
MotionEvents
onInterceptTouchEvent
MotionEvent
Można sobie wyobrazić kod grupy ViewGroup wykonującej mniej więcej to (bardzo uproszczone):
źródło
Dodatkowa odpowiedź
Oto kilka wizualnych uzupełnień innych odpowiedzi. Moja pełna odpowiedź jest tutaj .
dispatchTouchEvent()
Metoda ciąguViewGroup
zastosowańonInterceptTouchEvent()
zdecydować, czy powinien on natychmiast obsłużyć zdarzenia dotykowy (zonTouchEvent()
) lub kontynuować powiadamiającdispatchTouchEvent()
metod jej dzieci.źródło
Istnieje wiele nieporozumień na temat tych metod, ale tak naprawdę nie jest to tak skomplikowane. Większość zamieszania wynika z tego, że:
View/ViewGroup
lub któregokolwiek z jej dzieci nie zwracają prawdziwe wonTouchEvent
,dispatchTouchEvent
ionInterceptTouchEvent
będzie tylko wezwałMotionEvent.ACTION_DOWN
. Bez wartości trueonTouchEvent
widok nadrzędny zakłada, że widok nie potrzebuje MotionEvent.MotionEvent.ACTION_DOWN
, nawet jeśli grupa ViewGroup zwróci wartość trueonTouchEvent
.Kolejność przetwarzania wygląda następująco:
dispatchTouchEvent
jest nazywany.onInterceptTouchEvent
jest wywoływanyMotionEvent.ACTION_DOWN
lub gdy którekolwiek z dzieci grupy ViewGroup zwróciło wartość trueonTouchEvent
.onTouchEvent
jest wywoływany po raz pierwszy przez dzieci grupy ViewGroup, a gdy żadne z dzieci nie zwraca wartości true, jest wywoływane wView/ViewGroup
.Jeśli chcesz wyświetlić podgląd
TouchEvents/MotionEvents
bez wyłączania wydarzeń na swoich dzieciach, musisz zrobić dwie rzeczy:dispatchTouchEvent
aby wyświetlić podgląd zdarzenia i powrócićsuper.dispatchTouchEvent(ev)
;onTouchEvent
i zwróć wartość true, w przeciwnym razie nie otrzymasz żadnychMotionEvent
opróczMotionEvent.ACTION_DOWN
.Jeśli chcesz wykryć jakiś gest, na przykład przesunięcie, bez wyłączania innych zdarzeń na swoich dzieciach, o ile nie wykryłeś tego gestu, możesz to zrobić w następujący sposób:
onInterceptTouchEvent
gdy flaga jest ustawiona na anulowanie przetwarzania MotionEvent przez dzieci. Jest to również wygodne miejsce do zresetowania flagi, ponieważ onInterceptTouchEvent nie będzie wywoływany ponownie aż do następnegoMotionEvent.ACTION_DOWN
.Przykład przesłonięcia w
FrameLayout
(mój przykład w to C #, ponieważ programuję w Xamarin Android, ale logika jest taka sama w Javie):źródło
Natknąłem się na bardzo intuicyjne wyjaśnienia na tej stronie http://doandroids.com/blogs/tag/codeexample/ . Zaczerpnięty z:
źródło
Uchwyty dispatchTouchEvent przed onInterceptTouchEvent.
Korzystając z tego prostego przykładu:
Widać, że dziennik będzie wyglądał następująco:
Więc jeśli pracujesz z tymi dwoma modułami obsługi, użyj dispatchTouchEvent do obsługi zdarzenia w pierwszej instancji, które przejdzie do onInterceptTouchEvent.
Inna różnica polega na tym, że jeśli dispatchTouchEvent zwróci „fałsz”, zdarzenie nie zostanie propagowane do dziecka, w tym przypadku EditText, natomiast jeśli zwrócisz false w onInterceptTouchEvent, zdarzenie nadal zostanie wysłane do EditText
źródło
Krótka odpowiedź:
dispatchTouchEvent()
zostanie wywołana przede wszystkim.Krótka rada: nie powinna zastępować,
dispatchTouchEvent()
ponieważ jest trudna do kontrolowania, czasami może spowolnić działanie. IMHO, sugeruję zastąpienieonInterceptTouchEvent()
.Ponieważ większość odpowiedzi wspomina dość wyraźnie o zdarzeniu przepływu dotykowego w działaniu / grupie widoków / widoku, dodaję tylko więcej szczegółów na temat kodu tych metod w
ViewGroup
(ignorującdispatchTouchEvent()
):onInterceptTouchEvent()
zostanie wywołany jako pierwszy, zdarzenie ACTION zostanie wywołane odpowiednio w dół -> ruch -> w górę. Istnieją 2 przypadki:Jeśli zwrócisz fałsz w 3 przypadkach (ACTION_DOWN, ACTION_MOVE, ACTION_UP), uzna, że rodzic nie będzie potrzebował tego zdarzenia dotyku , więc
onTouch()
rodzice nigdy nieonTouch()
dzwonią , ale dzieci dzwonią ; jednak proszę zauważyć:onInterceptTouchEvent()
Nadal otrzymywać zdarzenia dotykowego, tak długo, jak jej dzieci nie zadzwonićrequestDisallowInterceptTouchEvent(true)
.onTouch()
rodziców.Odwrotnie, jeśli zwrócisz wartość true , rodzic natychmiast wykradnie to zdarzenie dotykowe i
onInterceptTouchEvent()
natychmiast się zatrzyma, zamiastonTouch()
wezwać rodziców, a wszystkieonTouch()
dzieci otrzymają ostatnie zdarzenie akcji - ACTION_CANCEL (oznacza to, że rodzice ukradł zdarzenie dotykowe, a dzieci nie mogą sobie z tym poradzić). PrzepływonInterceptTouchEvent()
zwracanej wartości false jest normalny, ale istnieje niewielkie zamieszanie w przypadku zwracanej wartości true, więc wymienię to tutaj:onTouch()
z rodziców otrzyma ACTION_DOWN ponownie i następujące działania (ACTION_MOVE, ACTION_UP).onTouch()
a rodzice otrzymają kolejne ACTION_MOVE (nie to samo ACTION_MOVE wonInterceptTouchEvent()
) i kolejne działania (ACTION_MOVE, ACTION_UP).onTouch()
ponieważ rodzice NIE zadzwonią wcale, ponieważ jest za późno, aby rodzice wykradli zdarzenie dotykowe.Jeszcze jedna ważna rzecz to ACTION_DOWN zdarzenia,
onTouch()
które określi, czy widok chce otrzymać więcej akcji z tego wydarzenia, czy nie. Jeśli widok zwróci wartość true przy ACTION_DOWN wonTouch()
, oznacza to, że widok jest skłonny otrzymać więcej akcji z tego zdarzenia. W przeciwnym razie wartość false przy ACTION_DOWN wonTouch()
oznacza, że widok nie otrzyma żadnej akcji z tego zdarzenia.źródło
Odpowiedź znajdziesz w tym filmie https://www.youtube.com/watch?v=SYoN-OvdZ3M&list=PLonJJ3BVjZW6CtAMbJz1XD8ELUs1KXaTD&index=19 i 3 kolejnych filmach. Wszystkie zdarzenia dotykowe zostały wyjaśnione bardzo dobrze, jest to bardzo jasne i pełne przykładów.
źródło
Poniższy kod w podklasie ViewGroup zapobiegnie odbiorze zdarzeń dotykowych przez kontenery nadrzędne:
Użyłem tego z niestandardową nakładką, aby zapobiec reagowaniu widoków tła na zdarzenia dotykowe.
źródło
Podstawowa różnica :
źródło
ViewGroup's
onInterceptTouchEvent()
jest zawsze punktem wejścia dlaACTION_DOWN
zdarzenia, które jest pierwszym zdarzeniem, które ma miejsce.Jeśli chcesz, aby ViewGroup przetworzyło ten gest, zwróć true z
onInterceptTouchEvent()
. Po powrocie prawda, ViewGroup naonTouchEvent()
otrzyma wszystkie kolejne wydarzenia aż do następnegoACTION_UP
lubACTION_CANCEL
, w większości przypadków, zdarzeń dotykowych pomiędzyACTION_DOWN
iACTION_UP
czyACTION_CANCEL
toACTION_MOVE
, co zazwyczaj uznawane za przewijanie / rzucać gesty.Jeśli zwrócisz false z
onInterceptTouchEvent()
, widok docelowyonTouchEvent()
zostanie wywołany. Będzie powtarzane dla kolejnych wiadomości, dopóki nie zwrócisz wartości trueonInterceptTouchEvent()
.Źródło: http://neevek.net/posts/2013/10/13/implementing-onInterceptTouchEvent-and-onTouchEvent-for-ViewGroup.html
źródło
Zarówno działanie, jak i widok mają metody dispatchTouchEvent () i onTouchEvent. ViewGroup ma również te metody, ale ma inną metodę o nazwie onInterceptTouchEvent. Typy zwracane przez te metody są logiczne, można kontrolować trasę wysyłki za pomocą wartości zwracanej.
Wysłanie zdarzenia w Androidzie zaczyna się od Activity-> ViewGroup-> View.
źródło
źródło
Mała odpowiedź:
OnInterceptTouchEvent występuje przed setOnTouchListener.
źródło