Jeśli Twoja aplikacja wymaga niestandardowego komponentu widoku, musisz zwiększyć dostępność widoku. Wykonaj te czynności, aby zwiększyć dostępność widoku niestandardowego, zgodnie z opisem na tej stronie:
- Obsługa kliknięć kontrolera kierunkowego.
- Zaimplementuj metody interfejsu API ułatwień dostępu.
- wysyłać
AccessibilityEventobiekty specyficzne dla widoku niestandardowego. - Wypełnij pola
AccessibilityEventiAccessibilityNodeInfow widoku danych.
Obsługa kliknięć kontrolera kierunkowego
Na większości urządzeń kliknięcie widoku za pomocą kontrolera kierunkowego wysyła KeyEvent z KEYCODE_DPAD_CENTER do widoku, który jest obecnie aktywny. Wszystkie standardowe widoki Androida obsługują KEYCODE_DPAD_CENTERprawidłowo. Podczas tworzenia niestandardowego elementu sterującegoViewupewnij się, że to zdarzenie ma taki sam efekt jak dotknięcie widoku na ekranie dotykowym.
Element sterujący niestandardowy musi traktować zdarzenie KEYCODE_ENTER
KEYCODE_DPAD_CENTERtak samo jak KEYCODE_DPAD_CENTER. Ułatwia to użytkownikom interakcje z pełną klawiaturą.
Implementowanie metod interfejsu API ułatwień dostępu
Zdarzenia związane z ułatwieniami dostępu to wiadomości o interakcjach użytkowników z elementami wizualnego interfejsu aplikacji. Te wiadomości są obsługiwane przez usługi ułatwień dostępu, które wykorzystują informacje z tych zdarzeń do generowania dodatkowych opinii i promptów. Metody ułatwień dostępu są częścią klas View i View.AccessibilityDelegate. Są to następujące metody:
dispatchPopulateAccessibilityEvent()onPopulateAccessibilityEvent() dla tego widoku, a następnie metodę dispatchPopulateAccessibilityEvent() dla każdego elementu podrzędnego tego widoku. onInitializeAccessibilityEvent()TextView lub Button, zastąp tę metodę i ustaw dodatkowe informacje o widoku, takie jak typ pola hasła, typ pola wyboru lub stany, które zapewniają interakcję użytkownika lub informacje zwrotne w zdarzeniu, za pomocą tej metody. Jeśli zastąpisz tę metodę, wywołaj jej implementację superklasy i modyfikuj tylko właściwości, które nie są ustawione przez superklasę.onInitializeAccessibilityNodeInfo()View ma standardowy zestaw właściwości widoku, ale jeśli Twój widok niestandardowy zapewnia interaktywną kontrolę wykraczającą poza proste TextView lub Button, zastąp tę metodę i ustaw dodatkowe informacje o widoku w obiekcie AccessibilityNodeInfo obsługiwanym przez tę metodę.onPopulateAccessibilityEvent()AccessibilityEvent dla Twojego widoku. Jest ona też wywoływana, jeśli widok jest elementem podrzędnym widoku, który generuje zdarzenie związane z ułatwieniami dostępu.
onRequestSendAccessibilityEvent()AccessibilityEvent. Na tym etapie rodzic może dodać do zdarzenia związanego z ułatwieniami dostępu dodatkowe informacje. Zaimplementuj tę metodę tylko wtedy, gdy widok niestandardowy może zawierać widoki podrzędne i gdy widok nadrzędny może przekazywać informacje kontekstowe do zdarzenia ułatwień dostępu, które są przydatne dla usług ułatwień dostępu.sendAccessibilityEvent()- System wywołuje tę metodę, gdy użytkownik wykona działanie w widoku. Zdarzenie jest klasyfikowane według typu działania użytkownika, np.
TYPE_VIEW_CLICKED. Ogólnie rzecz biorąc, musisz wysłaćAccessibilityEventza każdym razem, gdy zmieni się zawartość widoku niestandardowego. sendAccessibilityEventUnchecked()- Ta metoda jest używana, gdy kod wywołujący musi bezpośrednio kontrolować sprawdzanie, czy ułatwienia dostępu są włączone na urządzeniu (
AccessibilityManager.isEnabled()). Jeśli zaimplementujesz tę metodę, wykonaj wywołanie tak, jakby ułatwienia dostępu były włączone, niezależnie od ustawienia systemowego. Zwykle nie musisz implementować tej metody w przypadku widoku niestandardowego. dispatchPopulateAccessibilityEvent()onInitializeAccessibilityEvent()onInitializeAccessibilityNodeInfo()onPopulateAccessibilityEvent()TYPE_VIEW_CLICKEDTYPE_VIEW_FOCUSEDTYPE_VIEW_HOVER_ENTERTYPE_VIEW_HOVER_EXITTYPE_VIEW_LONG_CLICKEDTYPE_VIEW_SCROLLED- Wygeneruj odpowiedni
AccessibilityEventdla zinterpretowanego działania związanego z kliknięciem. - Włącz usługi ułatwień dostępu, aby wykonywać niestandardowe kliknięcia dla użytkowników, którzy nie mogą korzystać z ekranu dotykowego.
Aby zapewnić ułatwienia dostępu, zastąp i wdroż podane wyżej metody ułatwień dostępu bezpośrednio w klasie widoku niestandardowego.
W przypadku niestandardowej klasy widoku zaimplementuj co najmniej te metody ułatwień dostępu:
Więcej informacji o implementowaniu tych metod znajdziesz w sekcji wypełniania zdarzeń związanych z dostępnością.
Wysyłanie zdarzeń związanych z ułatwieniami dostępu
W zależności od szczegółów niestandardowego widoku może być konieczne wysyłanie obiektówAccessibilityEvent w różnych momentach lub w przypadku zdarzeń nieobsługiwanych przez domyślną implementację. Klasa View udostępnia domyślną implementację tych typów zdarzeń:
Zasadniczo musisz wysyłać AccessibilityEvent za każdym razem, gdy zmienia się zawartość niestandardowego widoku. Jeśli na przykład wdrażasz niestandardowy suwak, który umożliwia użytkownikowi wybór wartości liczbowej przez naciśnięcie klawisza strzałki w lewo lub w prawo, widok niestandardowy musi emitować zdarzenie TYPE_VIEW_TEXT_CHANGED
za każdym razem, gdy zmieni się wartość suwaka. Poniższy przykładowy kod pokazuje, jak użyć metody sendAccessibilityEvent() do zgłoszenia tego zdarzenia.
Kotlin
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean { return when(keyCode) { KeyEvent.KEYCODE_DPAD_LEFT -> { currentValue-- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED) true } ... } }
Java
@Override public boolean onKeyUp (int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) { currentValue--; sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED); return true; } ... }
Wypełnianie zdarzeń związanych z ułatwieniami dostępu
Każdy AccessibilityEvent ma zestaw wymaganych właściwości, które opisują bieżący stan widoku. Obejmują one takie elementy jak nazwa klasy widoku, opis treści i stan zaznaczenia. Wymagane właściwości poszczególnych typów zdarzeń zostały opisane w AccessibilityEventdokumentacji referencyjnej.
Implementacja View zapewnia domyślne wartości tych wymaganych właściwości. Wiele z tych wartości, w tym nazwa klasy i sygnatura czasowa zdarzenia, jest podawanych automatycznie. Jeśli tworzysz niestandardowy komponent widoku, musisz podać informacje o jego zawartości i charakterystyce. Mogą to być proste informacje, np. etykieta przycisku, ale też dodatkowe informacje o stanie, które chcesz dodać do zdarzenia.
Użyj metod
onPopulateAccessibilityEvent()
i
onInitializeAccessibilityEvent()
do wypełniania lub modyfikowania informacji w AccessibilityEvent. Używaj metody
onPopulateAccessibilityEvent() do dodawania lub modyfikowania tekstu
wydarzenia, który jest przekształcany w komunikaty głosowe przez usługi ułatwień dostępu, takie jak TalkBack. Metoda onInitializeAccessibilityEvent() służy do wypełniania dodatkowych informacji o zdarzeniu, np. stanu wyboru widoku.
Dodatkowo zaimplementuj metodęonInitializeAccessibilityNodeInfo(). Usługi ułatwień dostępu używają obiektów AccessibilityNodeInfo wypełnianych przez tę metodę do badania hierarchii widoków, która generuje zdarzenie ułatwień dostępu po jego otrzymaniu, i przekazywania użytkownikom odpowiednich informacji zwrotnych.
Poniższy przykład kodu pokazuje, jak zastąpić te 3 metody w widoku:
Kotlin
override fun onPopulateAccessibilityEvent(event: AccessibilityEvent?) { super.onPopulateAccessibilityEvent(event) // Call the super implementation to populate its text for the // event. Then, add text not present in a super class. // You typically only need to add the text for the custom view. if (text?.isNotEmpty() == true) { event?.text?.add(text) } } override fun onInitializeAccessibilityEvent(event: AccessibilityEvent?) { super.onInitializeAccessibilityEvent(event) // Call the super implementation to let super classes // set appropriate event properties. Then, add the new checked // property that is not supported by a super class. event?.isChecked = isChecked() } override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo?) { super.onInitializeAccessibilityNodeInfo(info) // Call the super implementation to let super classes set // appropriate info properties. Then, add the checkable and checked // properties that are not supported by a super class. info?.isCheckable = true info?.isChecked = isChecked() // You typically only need to add the text for the custom view. if (text?.isNotEmpty() == true) { info?.text = text } }
Java
@Override public void onPopulateAccessibilityEvent(AccessibilityEvent event) { super.onPopulateAccessibilityEvent(event); // Call the super implementation to populate its text for the // event. Then, add the text not present in a super class. // You typically only need to add the text for the custom view. CharSequence text = getText(); if (!TextUtils.isEmpty(text)) { event.getText().add(text); } } @Override public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); // Call the super implementation to let super classes // set appropriate event properties. Then, add the new checked // property that is not supported by a super class. event.setChecked(isChecked()); } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); // Call the super implementation to let super classes set // appropriate info properties. Then, add the checkable and checked // properties that are not supported by a super class. info.setCheckable(true); info.setChecked(isChecked()); // You typically only need to add the text for the custom view. CharSequence text = getText(); if (!TextUtils.isEmpty(text)) { info.setText(text); } }
Możesz zaimplementować te metody bezpośrednio w klasie widoku niestandardowego.
Podawanie dostosowanego kontekstu ułatwień dostępu
Usługi ułatwień dostępu mogą sprawdzać hierarchię widoków elementu interfejsu, który generuje zdarzenie ułatwień dostępu. Dzięki temu usługi ułatwień dostępu mogą dostarczać użytkownikom bogatsze informacje kontekstowe.
Zdarzają się sytuacje, w których usługi ułatwień dostępu nie są w stanie uzyskać wystarczających informacji z hierarchii widoków. Przykładem może być niestandardowy element sterujący interfejsu, który ma co najmniej 2 obszary, w które można kliknąć osobno, np. element sterujący kalendarzem. W takim przypadku usługi nie mogą uzyskać odpowiednich informacji, ponieważ klikalne podsekcje nie są częścią hierarchii widoków.
Rysunek 1. Widok kalendarza niestandardowego z elementami dnia do wyboru.
W przykładzie na ilustracji 1 cały kalendarz jest zaimplementowany jako jeden widok, więc usługi ułatwień dostępu nie otrzymują wystarczających informacji o zawartości widoku i wyborze użytkownika w tym widoku, chyba że deweloper poda dodatkowe informacje. Jeśli na przykład użytkownik kliknie dzień oznaczony numerem 17, platforma ułatwień dostępu otrzyma tylko informacje o całym elemencie sterującym kalendarza. W takim przypadku usługa ułatwień dostępu TalkBack odczytuje „Kalendarz” lub „Kalendarz kwietnia”, a użytkownik nie wie, który dzień jest zaznaczony.
Aby w takich sytuacjach zapewnić usługom ułatwień dostępu odpowiednie informacje kontekstowe, platforma udostępnia sposób na określenie wirtualnej hierarchii widoków. Wirtualna hierarchia widoków to sposób, w jaki deweloperzy aplikacji mogą udostępniać usługom ułatwień dostępu uzupełniającą hierarchię widoków, która dokładniej odzwierciedla informacje wyświetlane na ekranie. Dzięki temu usługi ułatwień dostępu mogą przekazywać użytkownikom bardziej przydatne informacje kontekstowe.
Inną sytuacją, w której może być potrzebna wirtualna hierarchia widoków, jest interfejs użytkownika zawierający zestaw View elementów sterujących o ściśle powiązanych funkcjach, w którym działanie jednego elementu sterującego wpływa na zawartość co najmniej jednego elementu – np. selektor liczb z osobnymi przyciskami w górę i w dół. W takim przypadku usługi ułatwień dostępu nie mogą uzyskać odpowiednich informacji, ponieważ działanie jednego elementu sterującego zmienia zawartość innego, a związek między tymi elementami może nie być dla usługi oczywisty.
Aby sobie z tym poradzić, pogrupuj powiązane elementy sterujące w widoku kontenera i utwórz wirtualną hierarchię widoków z tego kontenera, aby wyraźnie przedstawić informacje i zachowanie zapewniane przez elementy sterujące.
Aby udostępnić hierarchię widoku wirtualnego, zastąp metodę
getAccessibilityNodeProvider()
w widoku niestandardowym lub grupie widoków i zwróć implementację
AccessibilityNodeProvider.
Hierarchię widoku wirtualnego możesz wdrożyć za pomocą biblioteki pomocy, korzystając z metody
ViewCompat.getAccessibilityNodeProvider()
i zapewniając implementację za pomocą
AccessibilityNodeProviderCompat.
Aby uprościć zadanie dostarczania informacji do usług ułatwień dostępu i zarządzania fokusem ułatwień dostępu, możesz zamiast tego wdrożyć ExploreByTouchHelper.
Zapewnia AccessibilityNodeProviderCompat i może być dołączony jako AccessibilityDelegateCompat widoku przez wywołanie setAccessibilityDelegate.
Przykład znajdziesz w sekcji ExploreByTouchHelperActivity.
ExploreByTouchHelper jest też używany przez widżety frameworka, takie jak
CalendarView, za pomocą widoku podrzędnego
SimpleMonthView.
Obsługa niestandardowych zdarzeń dotknięcia
Elementy sterujące widoku niestandardowego mogą wymagać niestandardowego zachowania zdarzeń dotknięcia, jak pokazano w poniższych przykładach.
Definiowanie działań opartych na kliknięciach
Jeśli widżet korzysta z interfejsu OnClickListener lub OnLongClickListener, system obsługuje działania ACTION_CLICK i ACTION_LONG_CLICK. Jeśli Twoja aplikacja korzysta z bardziej spersonalizowanego widżetu, który opiera się na interfejsie OnTouchListener, zdefiniuj niestandardowe moduły obsługi działań związanych z kliknięciem, które są dostępne w ramach ułatwień dostępu. Aby to zrobić, wywołaj metodę replaceAccessibilityAction() dla każdej czynności, jak pokazano w tym fragmencie kodu:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { ... // Assumes that the widget is designed to select text when tapped, and selects // all text when tapped and held. In its strings.xml file, this app sets // "select" to "Select" and "select_all" to "Select all". ViewCompat.replaceAccessibilityAction( binding.textSelectWidget, ACTION_CLICK, getString(R.string.select) ) { view, commandArguments -> selectText() } ViewCompat.replaceAccessibilityAction( binding.textSelectWidget, ACTION_LONG_CLICK, getString(R.string.select_all) ) { view, commandArguments -> selectAllText() } }
Java
@Override protected void onCreate(Bundle savedInstanceState) { ... // Assumes that the widget is designed to select text when tapped, and select // all text when tapped and held. In its strings.xml file, this app sets // "select" to "Select" and "select_all" to "Select all". ViewCompat.replaceAccessibilityAction( binding.textSelectWidget, ACTION_CLICK, getString(R.string.select), (view, commandArguments) -> selectText()); ViewCompat.replaceAccessibilityAction( binding.textSelectWidget, ACTION_LONG_CLICK, getString(R.string.select_all), (view, commandArguments) -> selectAllText()); }
Tworzenie niestandardowych zdarzeń kliknięcia
Kontrolka niestandardowa może używać onTouchEvent(MotionEvent)metody odbiornika do wykrywania zdarzeńACTION_DOWN i ACTION_UP oraz wywoływania specjalnego zdarzenia kliknięcia. Aby zachować zgodność z usługami ułatwień dostępu, kod obsługujący to niestandardowe zdarzenie kliknięcia musi:
Aby skutecznie spełnić te wymagania, kod musi zastąpić metodę
performClick(), która musi wywoływać implementację superklasy tej metody, a następnie wykonywać działania wymagane przez zdarzenie kliknięcia. Gdy zostanie wykryte niestandardowe działanie kliknięcia, kod musi wywołać metodę performClick(). Poniższy przykład kodu pokazuje ten wzorzec.
Kotlin
class CustomTouchView(context: Context) : View(context) { var downTouch = false override fun onTouchEvent(event: MotionEvent): Boolean { super.onTouchEvent(event) // Listening for the down and up touch events. return when (event.action) { MotionEvent.ACTION_DOWN -> { downTouch = true true } MotionEvent.ACTION_UP -> if (downTouch) { downTouch = false performClick() // Call this method to handle the response and // enable accessibility services to // perform this action for a user who can't // tap the touchscreen. true } else { false } else -> false // Return false for other touch events. } } override fun performClick(): Boolean { // Calls the super implementation, which generates an AccessibilityEvent // and calls the onClick() listener on the view, if any. super.performClick() // Handle the action for the custom click here. return true } }
Java
class CustomTouchView extends View { public CustomTouchView(Context context) { super(context); } boolean downTouch = false; @Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); // Listening for the down and up touch events switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downTouch = true; return true; case MotionEvent.ACTION_UP: if (downTouch) { downTouch = false; performClick(); // Call this method to handle the response and // enable accessibility services to // perform this action for a user who can't // tap the touchscreen. return true; } } return false; // Return false for other touch events. } @Override public boolean performClick() { // Calls the super implementation, which generates an AccessibilityEvent // and calls the onClick() listener on the view, if any. super.performClick(); // Handle the action for the custom click here. return true; } }
Powyższy wzorzec pomaga zapewnić zgodność niestandardowego zdarzenia kliknięcia z usługami ułatwień dostępu, ponieważ używa metody performClick() do generowania zdarzenia ułatwień dostępu i zapewnia punkt wejścia dla usług ułatwień dostępu, aby działać w imieniu użytkownika wykonującego niestandardowe zdarzenie kliknięcia.