Omówienie zdarzeń wejściowych

Wypróbuj sposób tworzenia wiadomości
Jetpack Compose to zalecany zestaw narzędzi UI na Androida. Dowiedz się, jak korzystać z dotyku i wprowadzania w sekcji Utwórz

Istnieje kilka sposobów na przechwycenie zdarzeń związanych z interakcjami użytkownika z aplikacją na urządzeniach z Androidem. Jeśli chodzi o zdarzenia w interfejsie, należy przechwytywać zdarzenia z konkretnego obiektu View, z którym użytkownik wchodzi w interakcję. Służą do tego klasa View.

Wśród różnych klas widoku danych, które wykorzystasz do utworzenia układu, możesz zauważyć kilka publicznych metod wywołania zwrotnego, które mogą być przydatne w przypadku zdarzeń interfejsu. Metody te są wywoływane przez platformę Androida po wykonaniu odpowiedniego działania na danym obiekcie. Na przykład po dotknięciu widoku (np. przycisku) do obiektu jest wywoływana metoda onTouchEvent(). Aby jednak to przechwycić, musisz rozszerzyć klasę i zastąpić metodę. Jednak rozszerzanie każdego obiektu View w celu obsługi takiego zdarzenia nie byłoby jednak praktyczne. Dlatego klasa View zawiera również kolekcję zagnieżdżonych interfejsów z wywołaniami zwrotnymi, które można znacznie łatwiej zdefiniować. Te interfejsy, nazywane detektorami zdarzeń, umożliwiają rejestrowanie interakcji użytkowników z Twoim interfejsem.

Detektory zdarzeń najczęściej używasz do nasłuchiwania interakcji użytkownika, ale może się zdarzyć, że zechcesz rozszerzyć klasę View, aby utworzyć komponent niestandardowy. Możesz też rozszerzyć zajęcia Button, aby stworzyć coś bardziej wyszukanego. W tym przypadku możesz zdefiniować domyślne zachowanie zdarzeń klasy za pomocą modułów obsługi zdarzeń klasy.

Detektory zdarzeń

Detektor zdarzeń to interfejs klasy View, który zawiera jedną metodę wywołania zwrotnego. Metody te będą wywoływane przez platformę Androida, gdy widok, w którym został zarejestrowany detektor, zostanie aktywowany przez interakcję użytkownika z elementem w interfejsie.

Interfejsy detektora zdarzeń obejmują te metody wywołania zwrotnego:

onClick()
Od: View.OnClickListener. Dzieje się tak, gdy użytkownik dotknie elementu (w trybie dotykowym) albo skupi się na nim za pomocą klawiszy nawigacyjnych lub kulki, a następnie naciśnie odpowiedni klawisz „Enter” lub naciśnie kulkę.
onLongClick()
Od: View.OnLongClickListener. Dzieje się tak, gdy użytkownik dotknie i przytrzyma element (w trybie dotykowym) albo skupi się na nim za pomocą klawiszy nawigacyjnych lub kulki, a następnie naciśnie i przytrzyma odpowiedni klawisz „Enter” lub naciśnie i przytrzyma kulkę (przez 1 sekundę).
onFocusChange()
Od: View.OnFocusChangeListener. Jest ono wywoływane, gdy użytkownik przechodzi do elementu lub z niego odchodzi za pomocą klawiszy nawigacyjnych lub kulki.
onKey()
Od: View.OnKeyListener. Jest ono wywoływane, gdy użytkownik jest skupiony na elemencie i naciska lub puści klawisz sprzętowy na urządzeniu.
onTouch()
Od: View.OnTouchListener. Jest ono wywoływane, gdy użytkownik wykonuje działanie sklasyfikowane jako zdarzenie kliknięcia, w tym naciśnięcie, zwolnienie lub dowolny gest ruchu na ekranie (w ramach elementu).
onCreateContextMenu()
Od: View.OnCreateContextMenuListener. Tę nazwę wywołujemy podczas tworzenia menu kontekstowego (w wyniku długotrwałego „długiego kliknięcia”). Zapoznaj się z omówieniem menu kontekstowych w przewodniku dla programistów dotyczącym menu.

Metody te są jedynymi interfejsami, które można w nich stosować. Aby zdefiniować jedną z tych metod i obsługiwać zdarzenia, zaimplementuj zagnieżdżony interfejs w sekcji Aktywność lub zdefiniuj ją jako klasę anonimową. Następnie przekaż wystąpienie swojej implementacji do odpowiedniej metody View.set...Listener(). (np. wywołaj parametr setOnClickListener() i przekaż do niego swoją implementację OnClickListener).

Przykład poniżej pokazuje, jak zarejestrować odbiornik kliknięcia 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);
    ...
}

Być może łatwiej będzie również zaimplementować funkcję OnClickListener w Aktywności. Pozwoli to uniknąć dodatkowego obciążenia klas i przydzielania obiektów. Na 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 detektora muszą zwracać wartość logiczną. Przyczyna zależy od zdarzenia. Oto nieliczne powody, dla których warto je stosować:

  • onLongClick() – zwraca wartość logiczną wskazującą, czy zdarzenie zostało przetworzone i nie powinno być przenoszone dalej. To oznacza, że zwracaj wartość true, aby wskazać, że zdarzenie zostało wykonane i powinno się tu zatrzymać. Zwróć wartość false, jeśli zdarzenie nie zostało wykonane przez Ciebie, lub zdarzenie powinno być nadal przekazywane przez inne detektory po kliknięciu.
  • onKey() – zwraca wartość logiczną wskazującą, czy zdarzenie zostało przetworzone i nie powinno być przenoszone dalej. To oznacza, że zwracaj wartość true, aby wskazać, że zdarzenie zostało wykonane i powinno się tu zatrzymać. Zwróć wartość false, jeśli zdarzenie nie zostało przez Ciebie obsługiwane, lub zdarzenie powinno być nadal przekazywane do innych detektorów działających na kluczu.
  • onTouch() – zwraca wartość logiczną wskazującą, czy detektor obsługuje to zdarzenie. Ważne jest, że to zdarzenie może mieć wiele działań, które następują po sobie. Jeśli więc po otrzymaniu zdarzenia działania powodującego konwersję zwrócisz wartość false, będzie to oznaczać, że zdarzenie nie zostało przez Ciebie wykorzystane i nie interesują Cię kolejne działania związane z tym zdarzeniem. Oznacza to, że nie będziesz wykonywać żadnych innych działań w ramach tego zdarzenia, np. gestu palcem lub ostatecznego działania w górę.

Pamiętaj, że kluczowe zdarzenia sprzętowe są zawsze dostarczane do bieżącego widoku danych. Są one wysyłane od góry hierarchii widoków, a następnie w dół, aż dotrą do odpowiedniego miejsca docelowego. Jeśli widok danych (lub jego element podrzędny) jest obecnie aktywny, możesz obserwować podróż zdarzenia za pomocą metody dispatchKeyEvent(). Zamiast rejestrować kluczowe zdarzenia w widoku danych, możesz też otrzymywać wszystkie zdarzenia zawarte w aktywności za pomocą funkcji onKeyDown() i onKeyUp().

Rozważając też możliwość wprowadzania tekstu w aplikacji, pamiętaj, że wiele urządzeń ma wyłącznie programowe metody wprowadzania danych. Takie metody nie muszą opierać się na klawiszach. Niektóre z nich mogą korzystać z rozpoznawania mowy, pisma odręcznego itp. Nawet jeśli metoda wprowadzania ma interfejs podobny do klawiatury, zazwyczaj nie wywołuje rodziny zdarzeń onKeyDown(). Nigdy nie twórz interfejsu użytkownika, który wymaga do sterowania naciśnięciami określonych klawiszy, chyba że chcesz ograniczyć aplikację do urządzeń z klawiaturą sprzętową. W szczególności nie używaj tych metod do weryfikacji danych wejściowych, gdy użytkownik naciśnie klawisz Enter. Zamiast tego używaj działań takich jak IME_ACTION_DONE, aby zasygnalizować metodę wprowadzania, w jaki sposób ma zareagować Twoja aplikacja, co może spowodować istotne zmiany interfejsu użytkownika. Unikaj założenia programowej metody wprowadzania danych i ufaj, że będzie dostarczać do aplikacji już sformatowany tekst.

Uwaga: Android najpierw wywoła moduły obsługi zdarzeń, a następnie odpowiednie domyślne moduły obsługi na podstawie definicji klasy. Dlatego zwrócenie wartości true z tych detektorów zdarzeń zatrzyma propagację zdarzenia do innych detektorów zdarzeń, a także zablokuje wywołanie zwrotne do domyślnego modułu obsługi zdarzeń w widoku danych. Upewnij się, że chcesz zakończyć zdarzenie, gdy zwrócisz wartość true.

Moduły obsługi zdarzeń

Jeśli tworzysz komponent niestandardowy z poziomu widoku danych, możesz zdefiniować kilka metod wywołania zwrotnego używanych jako domyślne moduły obsługi zdarzeń. W dokumencie dotyczącym niestandardowych komponentów widoku danych poznasz typowe wywołania zwrotne służące do obsługi zdarzeń, takie jak:

Jest kilka innych metod, o których warto wiedzieć. Nie należą one do klasy View, ale mogą mieć bezpośredni wpływ na sposób obsługi zdarzeń. Gdy więc zarządzasz bardziej złożonymi zdarzeniami w układzie, rozważ inne metody:

Tryb dotykowy

Gdy użytkownik porusza się po interfejsie za pomocą klawiszy kierunkowych lub kulki, należy skupić się na elementach, których można użyć (np. przyciskach), aby wiedział, co akceptuje dane. Jeśli jednak urządzenie ma funkcje dotykowe, a użytkownik rozpoczyna interakcję z interfejsem, klikając go, nie trzeba już zaznaczać elementów ani koncentrować się na konkretnym widoku. Dlatego też dostępny jest tryb interakcji o nazwie „tryb dotykowy”.

Gdy użytkownik dotknie ekranu urządzenia dotykowego, przejdzie ono w tryb dotykowy. Od tej pory możliwe będzie zaznaczenie tylko tych widoków, w przypadku których wartość isFocusableInTouchMode() ma wartość Prawda, jak w przypadku widżetów do edycji tekstu. Inne, dotykowe, np. przyciski, nie są w nim uwzględniane, a po naciśnięciu uruchamiają się detektory generowane po kliknięciu.

Za każdym razem, gdy użytkownik naciśnie klawisz kierunkowy lub przewinie stronę przy użyciu kulki, urządzenie zamknie tryb dotykowy i znajdzie widok, na którym trzeba się skupić. Teraz użytkownik może wznowić interakcję z interfejsem bez dotykania ekranu.

Stan trybu dotykowego jest zachowywany w całym systemie (dotyczy wszystkich okien i działań). Aby przesłać zapytanie o bieżący stan, możesz zadzwonić pod numer isInTouchMode() i sprawdzić, czy urządzenie jest obecnie w trybie dotykowym.

Obsługa

Platforma będzie obsługiwać rutynowe przenoszenie fokusu w odpowiedzi na dane wejściowe użytkownika. Obejmuje to zmianę zaznaczenia w miarę usuwania lub ukrywania widoków bądź po udostępnieniu nowych widoków. Liczba wyświetleń wskazuje na ich gotowość do skupienie się na metodzie isFocusable(). Aby zmienić to, czy widok może być aktywny, wywołaj polecenie setFocusable(). W trybie dotykowym możesz za pomocą funkcji isFocusableInTouchMode() sprawdzić, czy widok pozwala na zaznaczenie. Możesz to zmienić w aplikacji setFocusableInTouchMode().

Na urządzeniach z Androidem 9 (poziom interfejsu API 28) lub nowszym działania nie przypisują początkowego fokusu. W razie potrzeby musisz jednak wyraźnie poprosić o zaznaczenie początkowe.

Ruch skupienia jest oparty na algorytmie, który znajduje najbliższego sąsiada w podanym kierunku. W rzadkich przypadkach domyślny algorytm może nie odpowiadać zamierzonemu działaniu dewelopera. W takich sytuacjach możesz podać w pliku układu jawne zastąpienia w tych atrybutach XML: nextFocusDown, nextFocusLeft, nextFocusRight i nextFocusUp. Dodaj jeden z tych atrybutów do widoku, w którym odchodzi się fokus. Określ wartość atrybutu jako identyfikator widoku do, który ma zostać zaznaczony. Na 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>

Zazwyczaj w tym pionowym układzie przejście od pierwszego przycisku w górę nie prowadzi do tego samego przycisku ani nie prowadzi do drugiego przycisku w dół. Gdy górny przycisk określi dolny jako nextFocusUp (i na odwrót), zaznaczenie będzie się cyklicznie zmieniać od góry do dołu i od dołu do góry.

Jeśli chcesz zadeklarować, że widok danych można zaznaczyć w interfejsie (gdy zwykle nie jest), dodaj do niego atrybut XML android:focusable w deklaracji układu. Ustaw wartość true. W trybie dotykowym możesz też zadeklarować widok jako możliwy do zaznaczenia za pomocą funkcji android:focusableInTouchMode.

Aby poprosić o konkretny widok, który zostanie zaznaczony, zadzwoń pod numer requestFocus().

Aby nasłuchiwać zdarzeń skupienia (będziesz otrzymywać powiadomienia, gdy widok zostanie zaznaczony lub przesunięty w tryb aktywności), użyj parametru onFocusChange(), jak omówiono w sekcji Detektory zdarzeń.