Jeśli Twoja aplikacja wymaga komponentu widoku niestandardowego, musisz zwiększyć dostępność tego widoku. Aby ułatwić dostęp do widoku niestandardowego, wykonaj te czynności, jak opisano na tej stronie:
- Obsługuj kliknięcia kontrolera kierunkowego.
- Wdróż metody interfejsu Accessibility API.
- Wyślij obiekty (
AccessibilityEvent
) specyficzne do widoku niestandardowego. - Wypełnij pola
AccessibilityEvent
iAccessibilityNodeInfo
na potrzeby widoku.
Obsługuj kliknięcia kontrolera kierunkowego
Na większości urządzeń kliknięcie widoku za pomocą kontrolera kierunkowego wysyła do aktywnego widoku KeyEvent
z wartością KEYCODE_DPAD_CENTER
. Wszystkie standardowe widoki Androida prawidłowo obsługują właściwość KEYCODE_DPAD_CENTER
. Tworząc własny element sterujący View
, upewnij się, że to zdarzenie będzie działać tak samo jak kliknięcie widoku na ekranie dotykowym.
Element sterujący musi traktować zdarzenie KEYCODE_ENTER
tak samo jak element KEYCODE_DPAD_CENTER
. Ułatwia to użytkownikom
obsługę pełnej klawiatury.
Wdrażanie metod interfejsu Accessibility API
Zdarzenia ułatwień dostępu to komunikaty o interakcjach użytkowników z komponentami interfejsu wizualnego aplikacji. Komunikaty te są obsługiwane przez usługi ułatwień dostępu, które wykorzystują informacje zawarte w tych wydarzeniach do generowania dodatkowych opinii i promptów. Metody ułatwień dostępu wchodzą w skład klas View
i View.AccessibilityDelegate
. Można to zrobić w następujący sposób:
dispatchPopulateAccessibilityEvent()
onPopulateAccessibilityEvent()
, a następnie metodę dispatchPopulateAccessibilityEvent()
w przypadku każdego widoku podrzędnego. 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ą użytkownikowi interakcję lub opinię o zdarzeniu. Jeśli zastąpisz tę metodę, wywołaj jej superimplementację i zmodyfikuj tylko te właściwości, które nie są ustawione przez klasę super.onInitializeAccessibilityNodeInfo()
View
ma standardowy zestaw właściwości widoku, ale jeśli Twój widok niestandardowy zapewnia interaktywną kontrolę wykraczającą poza prosty TextView
lub Button
, zastąp tę metodę i umieść dodatkowe informacje o widoku danych w obiekcie AccessibilityNodeInfo
obsługiwanym przez tę metodę.onPopulateAccessibilityEvent()
AccessibilityEvent
. Nazwa jest też wywoływana, jeśli widok jest podrzędny względem widoku, który generuje zdarzenie ułatwień dostępu.
onRequestSendAccessibilityEvent()
AccessibilityEvent
. Dzięki temu widokowi nadrzędnemu można uzupełnić zdarzenie ułatwień dostępu o dodatkowe informacje. Tę metodę należy stosować tylko wtedy, gdy widok niestandardowy może zawierać widoki podrzędne, a widok nadrzędny może dostarczać informacje kontekstowe do zdarzenia ułatwień dostępu przydatne dla usług ułatwień dostępu.sendAccessibilityEvent()
- System wywołuje tę metodę, gdy użytkownik wykonuje działanie związane z widokiem danych. Zdarzenie jest klasyfikowane według typu działania użytkownika, np.
TYPE_VIEW_CLICKED
. Ogólnie rzecz biorąc, musisz wysyłaćAccessibilityEvent
przy każdej zmianie zawartości widoku niestandardowego. sendAccessibilityEventUnchecked()
- Ta metoda jest używana, gdy kod wywołujący musi bezpośrednio kontrolować sprawdzanie pod kątem włączenia ułatwień dostępu na urządzeniu (
AccessibilityManager.isEnabled()
). Jeśli wdrożysz tę metodę, wykonaj wywołanie tak, jakby były włączone ułatwienia dostępu, niezależnie od ustawienia systemu. Zwykle nie musisz implementować tej metody w przypadku widoku niestandardowego. dispatchPopulateAccessibilityEvent()
onInitializeAccessibilityEvent()
onInitializeAccessibilityNodeInfo()
onPopulateAccessibilityEvent()
TYPE_VIEW_CLICKED
TYPE_VIEW_FOCUSED
TYPE_VIEW_HOVER_ENTER
TYPE_VIEW_HOVER_EXIT
TYPE_VIEW_LONG_CLICKED
TYPE_VIEW_SCROLLED
- Wygeneruj odpowiedni
AccessibilityEvent
dla zinterpretowanego działania kliknięcia. - Włącz usługi ułatwień dostępu, aby umożliwić wykonywanie niestandardowych kliknięć użytkownikom, którzy nie mogą korzystać z ekranu dotykowego.
Aby zapewnić obsługę ułatwień dostępu, zastąp i zaimplementuj poprzednie metody ułatwień dostępu bezpośrednio w klasie widoku niestandardowego.
W przypadku klasy widoku niestandardowego wdróż przynajmniej te metody ułatwień dostępu:
Więcej informacji o implementowaniu tych metod znajdziesz w sekcji o wypełnianiu zdarzeń ułatwień dostępu.
Wysyłanie zdarzeń związanych z ułatwieniami dostępu
W zależności od specyfiki widoku niestandardowego może być konieczne wysyłanie obiektów AccessibilityEvent
w różnych momentach lub w przypadku zdarzeń, które nie są obsługiwane przez domyślną implementację. Klasa View
udostępnia domyślną implementację tych typów zdarzeń:
Ogólnie rzecz biorąc, musisz wysyłać AccessibilityEvent
za każdym razem, gdy zmieni się zawartość widoku niestandardowego. Jeśli np. implementujesz niestandardowy pasek z suwakiem, który umożliwia użytkownikowi wybranie wartości liczbowej przez naciśnięcie klawisza strzałki w lewo lub w prawo, zawsze przy każdej zmianie wartości suwaka Twój widok niestandardowy musi wywoływać zdarzenie TYPE_VIEW_TEXT_CHANGED
. Ten przykładowy kod pokazuje, jak używać metody sendAccessibilityEvent()
do zgłaszania 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ń ułatwień dostępu
Każdy element AccessibilityEvent
ma zestaw wymaganych właściwości opisujących bieżący stan widoku. Obejmują one nazwę klasy widoku, opis treści i stan sprawdzenia. Konkretne właściwości wymagane w przypadku każdego typu zdarzenia są opisane w dokumentacji referencyjnej AccessibilityEvent
.
Implementacja View
podaje domyślne wartości tych wymaganych właściwości. Wiele z tych wartości, w tym nazwa klasy i sygnatura czasowa zdarzenia, jest ustawianych automatycznie. Jeśli tworzysz niestandardowy komponent widoku, musisz podać informacje o jego zawartości i cechach. Mogą to być proste jak etykieta przycisku
i mogą obejmować dodatkowe informacje o stanie, które chcesz dodać do zdarzenia.
Do wypełniania lub modyfikowania informacji w elemencie AccessibilityEvent
używaj metod onPopulateAccessibilityEvent()
i onInitializeAccessibilityEvent()
. Używaj metody onPopulateAccessibilityEvent()
do dodawania lub modyfikowania tekstu zdarzenia. Dzięki temu usługi ułatwień dostępu takie jak TalkBack przechodzą na prompty dźwiękowe. Aby podać dodatkowe informacje o zdarzeniu, np. stan wyboru widoku, użyj metody onInitializeAccessibilityEvent()
.
Dodatkowo zaimplementuj metodę onInitializeAccessibilityNodeInfo()
. Usługi ułatwień dostępu korzystają z obiektów AccessibilityNodeInfo
wypełnianych przez tę metodę, aby badać hierarchię widoków, która po otrzymaniu zdarzenia ułatwień dostępu generuje zdarzenie związane z ułatwieniami dostępu, i przekazywać użytkownikom odpowiednie informacje zwrotne.
Poniższy przykładowy kod pokazuje, jak zastąpić te trzy 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); } }
Te metody możesz zaimplementować bezpośrednio w klasie widoku niestandardowego.
Dostosowywanie 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 bardziej szczegółowe informacje kontekstowe.
Czasami usługi ułatwień dostępu nie mogą uzyskać odpowiednich informacji z hierarchii widoków. Przykładem jest niestandardowy element sterujący interfejsu, który zawiera co najmniej 2 osobne klikalne obszary, takie jak element sterujący kalendarza. W takim przypadku usługi nie mogą uzyskać odpowiednich informacji, ponieważ klikalne podsekcje nie są częścią hierarchii widoków.
![](https://developer.android.google.cn/static/guide/topics/ui/accessibility/calendar.png?authuser=7&hl=pl)
Rysunek 1. Niestandardowy widok kalendarza z elementami dnia do wyboru.
W przykładzie na Rysunku 1 cały kalendarz jest zaimplementowany jako pojedynczy widok, więc usługi ułatwień dostępu nie otrzymują wystarczającej ilości informacji o zawartości widoku i wyborze użytkownika w tym widoku, chyba że programista dostarczy dodatkowe informacje. Jeśli na przykład użytkownik kliknie dzień oznaczony etykietą 17, platforma ułatwień dostępu otrzyma tylko opis dla całego elementu sterującego kalendarza. W takim przypadku usługa ułatwień dostępu TalkBack odczyta komunikat „Kalendarz” lub „Kalendarz kwietniowy”, a użytkownik nie wie, który dzień został wybrany.
Aby zapewnić odpowiedni kontekst dla usług ułatwień dostępu w takich sytuacjach, platforma umożliwia określenie hierarchii widoków wirtualnych. Hierarchia widoków wirtualnych to sposób, w jaki deweloperzy aplikacji mogą zapewnić uzupełniającą hierarchię widoków danych w ramach usług ułatwień dostępu, które są bardziej zbliżone do informacji na ekranie. Dzięki temu usługi ułatwień dostępu mogą dostarczać użytkownikom bardziej przydatne informacje kontekstowe.
Inną sytuacją, w której hierarchia widoku wirtualnego może być potrzebna, jest interfejs zawierający zestaw elementów sterujących View
, które mają ściśle powiązane funkcje. W takiej sytuacji działanie jednego lub więcej elementów wpływa na zawartość co najmniej jednego elementu, np. selektora liczb z oddzielnymi przyciskami w górę i w dół. W takim przypadku usługi ułatwień dostępu nie mogą uzyskać wystarczających informacji, ponieważ działanie jednego z elementów sterujących powoduje zmianę treści w innej, a relacja tych ustawień z usługą może być niewidoczna.
Aby rozwiązać ten problem, zgrupuj powiązane elementy sterujące w widok zawierający i podaj hierarchię widoku wirtualnego z tego kontenera, aby jasno przedstawiać informacje i zachowanie zapewniane przez te opcje.
Aby udostępnić wirtualny widok hierarchii widoków, zastąp metodę getAccessibilityNodeProvider()
w widoku niestandardowym lub grupie widoków i zwróć implementację AccessibilityNodeProvider
.
Hierarchię widoków wirtualnych możesz wdrożyć, korzystając z biblioteki pomocy, używając metody ViewCompat.getAccessibilityNodeProvider()
i udostępniając implementację za pomocą AccessibilityNodeProviderCompat
.
Aby uprościć przekazywanie informacji do usług ułatwień dostępu i zarządzanie nimi, możesz zamiast tego wdrożyć ExploreByTouchHelper
.
Zawiera element AccessibilityNodeProviderCompat
, który można dołączyć jako element AccessibilityDelegateCompat
widoku, wywołując stronę setAccessibilityDelegate
.
Przykład: ExploreByTouchHelperActivity
.
Z kolei ExploreByTouchHelper
korzystają też widżety platformy, takie jak CalendarView
, w widoku podrzędnym SimpleMonthView
.
Obsługa niestandardowych zdarzeń dotknięcia
Ustawienia widoku niestandardowego mogą wymagać niestandardowego działania zdarzeń dotknięcia, jak pokazano w tych przykładach.
Zdefiniuj działania oparte na kliknięciach
Jeśli widżet korzysta z interfejsu OnClickListener
lub OnLongClickListener
, system automatycznie wykonuje działania ACTION_CLICK
i ACTION_LONG_CLICK
. Jeśli Twoja aplikacja używa bardziej niestandardowego widżetu, który opiera się na interfejsie OnTouchListener
, zdefiniuj niestandardowe moduły obsługi działań ułatwień dostępu opartych na kliknięciu. Aby to zrobić, dla każdego działania wywołaj metodę replaceAccessibilityAction()
, tak jak 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
Element sterujący może korzystać z metody odbiornika onTouchEvent(MotionEvent)
, aby wykrywać zdarzenia ACTION_DOWN
i ACTION_UP
oraz wywoływać specjalne zdarzenie kliknięcia. Aby zachować zgodność z usługami ułatwień dostępu, kod obsługujący to niestandardowe zdarzenie kliknięcia musi spełniać te warunki:
Aby sprawnie sprostać tym wymaganiom, Twój kod musi zastąpić metodę performClick()
, która musi wywołać superimplementację tej metody, a następnie wykonać wszelkie działania wymagane przez zdarzenie kliknięcia. Po wykryciu niestandardowego działania kliknięcia kod ten musi wywołać Twoją metodę performClick()
. Poniższy przykładowy kod ilustruje 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; } }
Poprzedni wzorzec pomaga zapewnić zgodność niestandardowego zdarzenia kliknięcia z usługami ułatwień dostępu dzięki użyciu metody performClick()
do generowania zdarzenia ułatwień dostępu i zapewnienia punktu wejścia usług ułatwień dostępu, które działają w imieniu użytkownika wykonującego zdarzenie kliknięcia niestandardowego.