Na Androidzie istnieje więcej niż 1 sposób na przechwytywanie zdarzeń związanych z interakcją użytkownika z aplikacją. W przypadku zdarzeń w interfejsie użytkownika należy przechwytywać zdarzenia z konkretnego obiektu View, z którym użytkownik wchodzi w interakcję. Klasa View umożliwia to.
W różnych klasach View, których będziesz używać do tworzenia układu, możesz zauważyć kilka publicznych metod wywołania zwrotnego, które wydają się przydatne w przypadku zdarzeń interfejsu. Metody te są wywoływane przez platformę Androida, gdy na danym obiekcie wystąpi odpowiednie działanie. Na przykład, gdy użytkownik dotknie obiektu View (np. przycisku), w tym obiekcie zostanie wywołana metoda onTouchEvent(). Aby jednak to przechwycić, musisz rozszerzyć klasę i zastąpić metodę. Rozszerzanie każdego obiektu View w celu obsługi takiego zdarzenia nie byłoby jednak praktyczne. Dlatego klasa View zawiera też kolekcję zagnieżdżonych interfejsów z wywołaniami zwrotnymi, które można znacznie łatwiej zdefiniować. Te interfejsy,
nazywane detektorami zdarzeń, umożliwiają przechwytywanie interakcji użytkownika z interfejsem.
Chociaż detektory zdarzeń są najczęściej używane do nasłuchiwania interakcji użytkownika, może się zdarzyć, że będziesz chcieć rozszerzyć klasę View, aby utworzyć komponent niestandardowy.
Możesz na przykład rozszerzyć klasę Button, aby utworzyć coś bardziej zaawansowanego. W takim przypadku możesz zdefiniować domyślne zachowania zdarzeń dla swojej
klasy za pomocą modułów obsługi zdarzeń klasy.
Detektory zdarzeń
Detektor zdarzeń to interfejs w klasie View, który zawiera jedną metodę wywołania zwrotnego. Metody te będą wywoływane przez platformę Androida, gdy użytkownik wejdzie w interakcję z elementem w interfejsie, co spowoduje wywołanie obiektu View, w którym zarejestrowano detektor.
Interfejsy detektorów zdarzeń zawierają te metody wywołania zwrotnego:
onClick()- Z
View.OnClickListener. Jest wywoływana, gdy użytkownik dotknie elementu (w trybie dotykowym) lub wybierze go za pomocą klawiszy nawigacyjnych lub trackballa i naciśnie odpowiedni klawisz „Enter” albo naciśnie trackball. onLongClick()- Z
View.OnLongClickListener. Jest wywoływana, gdy użytkownik dotknie i przytrzyma element (w trybie dotykowym) lub wybierze go za pomocą klawiszy nawigacyjnych lub trackballa i naciśnie oraz przytrzyma odpowiedni klawisz „Enter” albo naciśnie i przytrzyma trackball (przez sekundę). onFocusChange()- Z
View.OnFocusChangeListener. Jest wywoływana, gdy użytkownik przechodzi do elementu lub z niego za pomocą klawiszy nawigacyjnych lub trackballa. onKey()- Z
View.OnKeyListener. Jest wywoływana, gdy użytkownik wybierze element i naciśnie lub zwolni klawisz sprzętowy na urządzeniu. onTouch()- Z
View.OnTouchListener. Jest wywoływana, gdy użytkownik wykona działanie kwalifikujące się jako zdarzenie dotknięcia, w tym naciśnięcie, zwolnienie, lub dowolny gest ruchu na ekranie (w granicach elementu). onCreateContextMenu()- Z
View.OnCreateContextMenuListener. Jest wywoływana, gdy tworzone jest menu kontekstowe (w wyniku długiego kliknięcia). Więcej informacji o menu kontekstowym znajdziesz w przewodniku dla programistów dotyczącym menu.
Metody te są jedynymi elementami odpowiednich interfejsów. Aby zdefiniować jedną z tych metod i obsługiwać zdarzenia, zaimplementuj zagnieżdżony interfejs w aktywności lub zdefiniuj go jako klasę anonimową.
Następnie przekaż instancję implementacji do odpowiedniej metody View.set...Listener(). (Na przykład wywołaj
i przekaż do niej implementację setOnClickListener()OnClickListener.)
Poniższy przykład pokazuje, jak zarejestrować detektor kliknięć przycisku.
Kotlin
protected void onCreate(savedValues: Bundle) { ... val button: Button = findViewById(R.id.corky) // Register the onClick listener with the implementation above button.setOnClickListener { view -> // do something when the button is clicked } ... }
Java
// Create an anonymous implementation of OnClickListener private OnClickListener corkyListener = new OnClickListener() { public void onClick(View v) { // do something when the button is clicked } }; protected void onCreate(Bundle savedValues) { ... // Capture our button from layout Button button = (Button)findViewById(R.id.corky); // Register the onClick listener with the implementation above button.setOnClickListener(corkyListener); ... }
Może się też okazać, że wygodniej będzie zaimplementować OnClickListener jako część aktywności. Pozwoli to uniknąć dodatkowego wczytywania klasy i alokacji obiektu. Przykład:
Kotlin
class ExampleActivity : Activity(), OnClickListener { protected fun onCreate(savedValues: Bundle) { val button: Button = findViewById(R.id.corky) button.setOnClickListener(this) } // Implement the OnClickListener callback fun onClick(v: View) { // do something when the button is clicked } }
Java
public class ExampleActivity extends Activity implements OnClickListener { protected void onCreate(Bundle savedValues) { ... Button button = (Button)findViewById(R.id.corky); button.setOnClickListener(this); } // Implement the OnClickListener callback public void onClick(View v) { // do something when the button is clicked } ... }
Zwróć uwagę, że wywołanie zwrotne onClick() w powyższym przykładzie nie ma wartości zwracanej, ale niektóre inne metody detektorów zdarzeń muszą zwracać wartość logiczną. Powód zależy od zdarzenia. W przypadku tych kilku metod, które zwracają wartość logiczną, jest to spowodowane tym, że:
– zwraca wartość logiczną wskazującą, czy zdarzenie zostało przetworzone i nie powinno być przekazywane dalej. Oznacza to, że jeśli zwrócisz true, zdarzenie zostanie obsłużone i zatrzyma się w tym miejscu. Jeśli zwrócisz false, zdarzenie nie zostanie obsłużone i/lub powinno być przekazywane do innych detektorów kliknięć.onLongClick()– zwraca wartość logiczną wskazującą, czy zdarzenie zostało przetworzone i nie powinno być przekazywane dalej. Oznacza to, że jeśli zwrócisz true, zdarzenie zostanie obsłużone i zatrzyma się w tym miejscu. Jeśli zwrócisz false, zdarzenie nie zostanie obsłużone i/lub powinno być przekazywane do innych detektorów klawiszy.onKey()– zwraca wartość logiczną wskazującą, czy detektor przetwarza to zdarzenie. Ważne jest, że to zdarzenie może mieć kilka działań, które następują po sobie. Jeśli więc po otrzymaniu zdarzenia naciśnięcia zwrócisz false, oznacza to, że nie przetworzyłeś zdarzenia i nie interesują Cię też kolejne działania związane z tym zdarzeniem. W związku z tym nie zostaniesz wywołany w przypadku żadnych innych działań w ramach zdarzenia, takich jak gest palcem czy ostateczne zdarzenie zwolnienia.onTouch()
Pamiętaj, że zdarzenia klawiszy sprzętowych są zawsze dostarczane do obiektu View, który jest obecnie aktywny. Są one wysyłane od góry hierarchii widoków w dół, aż dotrą do odpowiedniego miejsca docelowego. Jeśli Twój obiekt View (lub jego element podrzędny)
jest obecnie aktywny, możesz zobaczyć, jak zdarzenie przechodzi przez metodę . Alternatywnie do przechwytywania zdarzeń klawiszy za pomocą obiektu View możesz też otrzymywać
wszystkie zdarzenia w aktywności za pomocą dispatchKeyEvent()
i onKeyDown().onKeyUp()
Pamiętaj też, że w przypadku wprowadzania tekstu w aplikacji wiele urządzeń ma tylko programowe metody wprowadzania danych. Metody te nie muszą być oparte na klawiszach. Niektóre mogą używać głosowego wprowadzania tekstu, pisma odręcznego itp. Nawet jeśli
metoda wprowadzania danych ma interfejs podobny do klawiatury, zwykle nie wywoła rodziny zdarzeń
. Nigdy nie twórz interfejsu, który wymaga naciśnięcia określonych klawiszy, chyba że chcesz ograniczyć aplikację do urządzeń z klawiaturą sprzętową. W szczególności nie polegaj na tych metodach w celu sprawdzania poprawności danych wejściowych, gdy użytkownik naciśnie klawisz Enter. Zamiast tego używaj działań takich jak onKeyDown()IME_ACTION_DONE, aby poinformować metodę wprowadzania danych, jak aplikacja ma reagować, aby mogła w odpowiedni sposób zmienić swój interfejs. Nie zakładaj, jak powinna działać programowa metoda wprowadzania danych, i po prostu ufaj, że dostarczy ona do aplikacji już sformatowany tekst.
Uwaga: Android najpierw wywoła moduły obsługi zdarzeń, a potem odpowiednie domyślne moduły obsługi z definicji klasy. W związku z tym zwrócenie wartości true z tych detektorów zdarzeń spowoduje zatrzymanie propagacji zdarzenia do innych detektorów zdarzeń, a także zablokuje wywołanie zwrotne do domyślnego modułu obsługi zdarzeń w obiekcie View. Dlatego, gdy zwracasz wartość true, musisz mieć pewność, że chcesz zakończyć zdarzenie.
Moduły obsługi zdarzeń
Jeśli tworzysz komponent niestandardowy na podstawie obiektu View, możesz zdefiniować kilka metod wywołania zwrotnego używanych jako domyślne moduły obsługi zdarzeń. W dokumencie o niestandardowych komponentach widoku dowiesz się o niektórych typowych wywołaniach zwrotnych używanych do obsługi zdarzeń, w tym:
– wywoływana, gdy wystąpi nowe zdarzenie klawisza.onKeyDown(int, KeyEvent)– wywoływana, gdy wystąpi zdarzenie zwolnienia klawisza.onKeyUp(int, KeyEvent)– wywoływana, gdy wystąpi zdarzenie ruchu trackballa.onTrackballEvent(MotionEvent)– wywoływana, gdy wystąpi zdarzenie ruchu na ekranie dotykowym.onTouchEvent(MotionEvent)– wywoływana, gdy widok zyskuje lub traci fokus.onFocusChanged(boolean, int, Rect)
Istnieje kilka innych metod, o których warto pamiętać. Nie są one częścią klasy View, ale mogą bezpośrednio wpływać na sposób obsługi zdarzeń. Dlatego podczas zarządzania bardziej złożonymi zdarzeniami w układzie warto wziąć pod uwagę te inne metody:
– umożliwiaActivity.dispatchTouchEvent(MotionEvent)Activityprzechwytywanie wszystkich zdarzeń dotknięcia, zanim zostaną one wysłane do okna.– umożliwia grupieViewGroup.onInterceptTouchEvent(MotionEvent)ViewGroupobserwowanie zdarzeń wysyłanych do podrzędnych obiektów View.– wywołaj tę metodę w obiekcie View nadrzędnym, aby wskazać, że nie powinien on przechwytywać zdarzeń dotknięcia za pomocą metodyViewParent.requestDisallowInterceptTouchEvent(boolean).onInterceptTouchEvent(MotionEvent)
Tryb dotykowy
Gdy użytkownik porusza się po interfejsie za pomocą klawiszy kierunkowych lub trackballa, konieczne jest ustawienie fokusu na elementach, które można aktywować (np. przyciskach), aby użytkownik widział, co będzie akceptować dane wejściowe. Jeśli jednak urządzenie ma funkcję dotyku, a użytkownik zacznie wchodzić w interakcję z interfejsem, dotykając go, nie trzeba już wyróżniać elementów ani ustawiać fokusu na konkretnym obiekcie View. Dlatego istnieje tryb interakcji o nazwie „tryb dotykowy”.
Gdy użytkownik dotknie ekranu urządzenia z funkcją dotyku, urządzenie przejdzie w tryb dotykowy. Od tego momentu fokus będzie można ustawić tylko na obiektach View, dla których metoda isFocusableInTouchMode() zwraca wartość true, np. na widżetach do edycji tekstu.
Inne obiekty View, które można dotykać, np. przyciski, nie będą aktywowane po dotknięciu. Po naciśnięciu po prostu uruchomią swoje detektory kliknięć.
Gdy użytkownik naciśnie klawisz kierunkowy lub przewinie trackball, urządzenie wyjdzie z trybu dotykowego i znajdzie widok, który ma zostać aktywowany. Teraz użytkownik może kontynuować interakcję z interfejsem bez dotykania ekranu.
Stan trybu dotykowego jest utrzymywany w całym systemie (we wszystkich oknach i aktywnościach).
Aby sprawdzić bieżący stan, możesz wywołać metodę isInTouchMode(), aby sprawdzić, czy urządzenie jest obecnie w trybie dotykowym.
Obsługa fokusu
Platforma będzie obsługiwać rutynowe przenoszenie fokusu w odpowiedzi na dane wejściowe użytkownika.
Obejmuje to zmianę fokusu, gdy obiekty View są usuwane lub ukrywane albo gdy pojawiają się nowe obiekty View. Obiekty View wskazują, że chcą przejąć fokus
za pomocą metody . Aby zmienić, czy obiekt View może przejąć
fokus, wywołaj metodę isFocusable(). W trybie dotykowym
możesz sprawdzić, czy obiekt View zezwala na fokus, za pomocą metody setFocusable().
Możesz to zmienić za pomocą isFocusableInTouchMode().
setFocusableInTouchMode()
Na urządzeniach z Androidem 9 (API na poziomie 28) lub nowszym aktywności nie przypisują początkowego fokusu. Jeśli chcesz, musisz wyraźnie poprosić o początkowy fokus.
Przenoszenie fokusu jest oparte na algorytmie, który znajduje najbliższego sąsiada w danym kierunku. W rzadkich przypadkach domyślny algorytm może nie odpowiadać zamierzonemu zachowaniu programisty. W takich sytuacjach możesz podać wyraźne zastąpienia za pomocą tych atrybutów XML w pliku układu: nextFocusDown, nextFocusLeft, nextFocusRight, i nextFocusUp. Dodaj jeden z tych atrybutów do obiektu View, z którego ma zostać przeniesiony fokus. Zdefiniuj wartość atrybutu jako identyfikator obiektu View, do którego ma zostać przeniesiony fokus. Przykład:
<LinearLayout android:orientation="vertical" ... > <Button android:id="@+id/top" android:nextFocusUp="@+id/bottom" ... /> <Button android:id="@+id/bottom" android:nextFocusDown="@+id/top" ... /> </LinearLayout>
Zwykle w tym układzie pionowym przejście w górę z pierwszego przycisku nie spowoduje przejścia do żadnego elementu, podobnie jak przejście w dół z drugiego przycisku. Teraz, gdy górny przycisk zdefiniował dolny jako nextFocusUp (i odwrotnie), zaznaczenie będzie się przełączać z góry na dół i z dołu na górę.
Jeśli chcesz zadeklarować obiekt View jako aktywny w interfejsie (gdy tradycyjnie nie jest), dodaj do niego atrybut XML android:focusable w deklaracji układu.
Ustaw wartość true. Możesz też zadeklarować obiekt View jako aktywny w trybie dotykowym za pomocą atrybutu android:focusableInTouchMode.
Aby poprosić o aktywowanie konkretnego obiektu View, wywołaj metodę .requestFocus()
Aby nasłuchiwać zdarzeń fokusu (otrzymywać powiadomienia, gdy obiekt View zyskuje lub traci fokus), użyj
,
jak opisano w sekcji Detektory zdarzeń.onFocusChange()