Aby ułatwić osobom niepełnosprawnym korzystanie z Twojej aplikacji, zaprojektuj ją tak, aby spełniała najważniejsze wymagania.
Weź pod uwagę minimalne rozmiary docelowych elementów dotykowych
Każdy element na ekranie, który można kliknąć, dotknąć lub w inny sposób aktywować, powinien być na tyle duży, aby umożliwiać niezawodną interakcję. Podczas określania rozmiaru tych elementów pamiętaj, by ustawić minimalny rozmiar na 48 dp. Jest to zgodne z wytycznymi dotyczącymi ułatwień dostępu w stylu Material Design.
Komponenty materiałowe – takie jak Checkbox
, RadioButton
, Switch
, Slider
i Surface
– ustawiają ten minimalny rozmiar wewnętrznie, ale tylko wtedy, gdy komponent może wykonywać działania użytkownika. Jeśli np. parametr Checkbox
ma wartość inną niż null, pole wyboru zawiera dopełnienie, którego szerokość i wysokość muszą wynosić co najmniej 48 dp.onCheckedChange
@Composable private fun CheckableCheckbox() { Checkbox(checked = true, onCheckedChange = {}) }
Jeśli parametr onCheckedChange
ma wartość null, dopełnienie nie jest uwzględniane, ponieważ nie można bezpośrednio korzystać z komponentu.
@Composable private fun NonClickableCheckbox() { Checkbox(checked = true, onCheckedChange = null) }
Gdy implementujesz elementy sterujące wyborem, takie jak Switch
, RadioButton
lub Checkbox
, zazwyczaj klikasz w kontenerze nadrzędnym, ustawiasz wywołanie zwrotne kliknięcia w komponencie na null
i dodajesz modyfikator toggleable
lub selectable
do nadrzędnego elementu kompozycyjnego.
@Composable private fun CheckableRow() { MaterialTheme { var checked by remember { mutableStateOf(false) } Row( Modifier .toggleable( value = checked, role = Role.Checkbox, onValueChange = { checked = !checked } ) .padding(16.dp) .fillMaxWidth() ) { Text("Option", Modifier.weight(1f)) Checkbox(checked = checked, onCheckedChange = null) } } }
Gdy rozmiar klikalnego elementu kompozycyjnego jest mniejszy niż minimalny rozmiar docelowego elementu dotykowego, funkcja tworzenia nadal zwiększa rozmiar docelowych elementów dotykowych. Dzieje się tak, ponieważ rozmiar docelowego elementu dotykowego wykracza poza granice funkcji kompozycyjnej.
Ten przykład zawiera bardzo mały klikalny element Box
. Obszar docelowego elementu dotykowego jest automatycznie rozwijany poza granice obszaru Box
, więc kliknięcie obok Box
nadal wywołuje zdarzenie kliknięcia.
@Composable private fun SmallBox() { var clicked by remember { mutableStateOf(false) } Box( Modifier .size(100.dp) .background(if (clicked) Color.DarkGray else Color.LightGray) ) { Box( Modifier .align(Alignment.Center) .clickable { clicked = !clicked } .background(Color.Black) .size(1.dp) ) } }
Aby zapobiec potencjalnemu nakładaniu się obszarów dotykowych elementów kompozycyjnych, zawsze używaj odpowiednio dużego rozmiaru minimalnego dla funkcji kompozycyjnej. W tym przykładzie oznacza to użycie modyfikatora sizeIn
do ustawienia minimalnego rozmiaru pola wewnętrznego:
@Composable private fun LargeBox() { var clicked by remember { mutableStateOf(false) } Box( Modifier .size(100.dp) .background(if (clicked) Color.DarkGray else Color.LightGray) ) { Box( Modifier .align(Alignment.Center) .clickable { clicked = !clicked } .background(Color.Black) .sizeIn(minWidth = 48.dp, minHeight = 48.dp) ) } }
Dodawanie etykiet kliknięć
Za pomocą etykiety kliknięcia możesz nadać elementowi kompozycyjnemu znaczenie związane z kliknięciami. Etykiety kliknięć opisują, co się dzieje, gdy użytkownik wchodzi w interakcję z komponentem. Usługi ułatwień dostępu używają etykiet kliknięć, aby opisać aplikację użytkownikom o określonych potrzebach.
Aby ustawić etykietę kliknięcia, przekaż parametr w modyfikatorze clickable
:
@Composable private fun ArticleListItem(openArticle: () -> Unit) { Row( Modifier.clickable( // R.string.action_read_article = "read article" onClickLabel = stringResource(R.string.action_read_article), onClick = openArticle ) ) { // .. } }
Jeśli nie masz dostępu do modyfikatora kliknięcia, możesz też ustawić etykietę kliknięcia w modyfikatorze semantyki:
@Composable private fun LowLevelClickLabel(openArticle: () -> Boolean) { // R.string.action_read_article = "read article" val readArticleLabel = stringResource(R.string.action_read_article) Canvas( Modifier.semantics { onClick(label = readArticleLabel, action = openArticle) } ) { // .. } }
Opisz elementy wizualne
Gdy zdefiniujesz element kompozycyjny Image
lub Icon
, platforma Androida nie może automatycznie sprawdzić, co wyświetla aplikacja. Musisz przekazać opis tekstowy elementu wizualnego.
Wyobraź sobie ekran, na którym użytkownik może udostępnić znajomym bieżącą stronę. Zawiera on klikalną ikonę udostępniania:
Platforma Androida nie jest w stanie opisać jej w zależności od samej ikony osobom z wadą wzroku. Platforma Androida wymaga dodatkowego opisu tekstowego ikony.
Parametr contentDescription
opisuje element wizualny. Użyj zlokalizowanego ciągu znaków, tak aby był widoczny dla użytkownika.
@Composable private fun ShareButton(onClick: () -> Unit) { IconButton(onClick = onClick) { Icon( imageVector = Icons.Filled.Share, contentDescription = stringResource(R.string.label_share) ) } }
Niektóre elementy wizualne mają charakter czysto dekoracyjny – użytkownik nie powinien ich przekazywać. Gdy ustawisz parametr contentDescription
na null
, informujesz platformę Androida, że ten element nie ma powiązanych działań ani stanu.
@Composable private fun PostImage(post: Post, modifier: Modifier = Modifier) { val image = post.imageThumb ?: painterResource(R.drawable.placeholder_1_1) Image( painter = image, // Specify that this image has no semantic meaning contentDescription = null, modifier = modifier .size(40.dp, 40.dp) .clip(MaterialTheme.shapes.small) ) }
To Ty decydujesz, czy dany element wizualny wymaga contentDescription
. Zastanów się, czy dany element przekazuje informacje,
które są potrzebne do wykonania zadania. Jeśli nie, to lepiej
pominąć opis.
Scal elementy
Usługi ułatwień dostępu, takie jak Talkback i Switch Access, pozwalają użytkownikom przenosić zaznaczenie między elementami na ekranie. Ważne jest, aby elementy były skoncentrowane na odpowiedniej szczegółowości. Gdy każdy element kompozycyjny niskiego poziomu na ekranie jest ustawiony niezależnie, użytkownicy muszą wchodzić w interakcję z innymi, aby poruszać się po ekranie. Jeśli elementy zbyt agresywnie łączą się ze sobą, użytkownicy mogą nie zrozumieć, które elementy razem
Gdy zastosujesz modyfikator clickable
do funkcji kompozycyjnej, funkcja Utwórz automatycznie połączy wszystkie elementy, które zawiera ten element. Odnosi się to również do ListItem
: elementy elementu listy scalają się, a usługi ułatwień dostępu postrzegają je jako jeden element.
Istnieje możliwość, że grupa elementów kompozycyjnych tworzy grupę logiczną, ale ta grupa nie jest klikalna ani nie wchodzi w skład elementu listy. Nadal chcesz, aby usługi ułatwień dostępu wyświetlały je jako jeden element. Na przykład wyobraźmy sobie kompozycję, która zawiera awatar użytkownika, jego nazwę i pewne dodatkowe informacje:
Możesz włączyć tworzenie, aby scalić te elementy, używając parametru mergeDescendants
w modyfikatorze semantics
. Dzięki temu usługi ułatwień dostępu wybierają tylko scalony element, a wszystkie właściwości semantyczne elementów podrzędnych są scalane.
@Composable private fun PostMetadata(metadata: Metadata) { // Merge elements below for accessibility purposes Row(modifier = Modifier.semantics(mergeDescendants = true) {}) { Image( imageVector = Icons.Filled.AccountCircle, contentDescription = null // decorative ) Column { Text(metadata.author.name) Text("${metadata.date} • ${metadata.readTimeMinutes} min read") } } }
Usługi ułatwień dostępu skupiają się teraz na całym kontenerze, scalając zawartość:
Dodaj działania niestandardowe
Przyjrzyj się tej pozycji na liście:
Gdy używasz czytnika ekranu, takiego jak TalkBack, do słyszenia, co wyświetla się na ekranie, najpierw wybiera cały element, a następnie ikonę zakładki.
Przy długiej liście może to być bardzo częste. Lepszym rozwiązaniem jest zdefiniowanie działania niestandardowego, które umożliwia użytkownikowi dodanie elementu do zakładek. Pamiętaj, że musisz też wyraźnie usunąć działanie ikony zakładki, aby nie została ona wybrana przez usługę ułatwień dostępu. Do tego służy modyfikator clearAndSetSemantics
:
@Composable private fun PostCardSimple( /* ... */ isFavorite: Boolean, onToggleFavorite: () -> Boolean ) { val actionLabel = stringResource( if (isFavorite) R.string.unfavorite else R.string.favorite ) Row( modifier = Modifier .clickable(onClick = { /* ... */ }) .semantics { // Set any explicit semantic properties customActions = listOf( CustomAccessibilityAction(actionLabel, onToggleFavorite) ) } ) { /* ... */ BookmarkButton( isBookmarked = isFavorite, onClick = onToggleFavorite, // Clear any semantics properties set on this node modifier = Modifier.clearAndSetSemantics { } ) } }
Opisz stan elementu
Funkcja kompozycyjna może definiować element stateDescription
na potrzeby semantyki, za pomocą którego platforma Androida odczytuje stan elementu kompozycyjnego. Na przykład funkcja kompozycyjna z możliwością przełączania może mieć stan „zaznaczone” lub „odznaczone”. W niektórych przypadkach może być konieczne zastąpienie domyślnych etykiet opisu stanu używanych przez funkcję Utwórz. Możesz to zrobić, jawnie określając etykiety opisu stanu przed zdefiniowaniem funkcji kompozycyjnej jako możliwej do przełączania:
@Composable private fun TopicItem(itemTitle: String, selected: Boolean, onToggle: () -> Unit) { val stateSubscribed = stringResource(R.string.subscribed) val stateNotSubscribed = stringResource(R.string.not_subscribed) Row( modifier = Modifier .semantics { // Set any explicit semantic properties stateDescription = if (selected) stateSubscribed else stateNotSubscribed } .toggleable( value = selected, onValueChange = { onToggle() } ) ) { /* ... */ } }
Zdefiniuj nagłówki
Aplikacje wyświetlają czasem dużą ilość treści na jednym ekranie w kontenerze, który można przewijać. Na przykład ekran może zawierać pełną treść artykułu czytanego przez użytkownika:
Użytkownicy z ułatwieniami dostępu mają trudności z poruszaniem się po ekranie. Aby ułatwić nawigację, wskaż elementy, które są nagłówkami. W poprzednim przykładzie każdy tytuł podsekcji można zdefiniować jako nagłówek ułatwień dostępu. Niektóre usługi ułatwień dostępu, takie jak TalkBack, pozwalają użytkownikom przechodzić bezpośrednio z poziomu nagłówka do nagłówka.
Aby wskazać, że element kompozycyjny jest nagłówkiem w komponencie w komponencie, określ jego właściwość semantics
:
@Composable private fun Subsection(text: String) { Text( text = text, style = MaterialTheme.typography.headlineSmall, modifier = Modifier.semantics { heading() } ) }
Obsługa niestandardowych funkcji kompozycyjnych
Za każdym razem, gdy zastępujesz w aplikacji określone komponenty Material Design własnymi wersjami, musisz pamiętać o kwestiach związanych z ułatwieniami dostępu.
Załóżmy, że zastępujesz materiał Checkbox
własną implementacją.
Możesz zapomnieć o dodaniu modyfikatora triStateToggleable
, który obsługuje właściwości ułatwień dostępu tego komponentu.
Zapoznaj się z implementacją komponentu w bibliotece Material Design i naśladuj podobne działania ułatwień dostępu. Dodatkowo w porównaniu z modyfikatorami na poziomie interfejsu należy w dużej mierze korzystać z modyfikatorów podstawowych, ponieważ zawierają one już gotowe funkcje ułatwień dostępu.
Przetestuj implementację komponentu niestandardowego za pomocą wielu usług ułatwień dostępu, by sprawdzić jej działanie.
Dodatkowe materiały
- Ułatwienia dostępu: podstawowe pojęcia i techniki, które są wspólne dla wszystkich tworzenia aplikacji na Androida.
- Tworzenie dostępnych aplikacji: kluczowe czynności, które możesz podjąć, aby zwiększyć dostępność aplikacji.
- Zasady dotyczące ułatwień dostępu do aplikacji: kluczowe zasady, o których warto pamiętać, tworząc ułatwienia dostępu w aplikacji.
- Testowanie pod kątem ułatwień dostępu: testowanie zasad i narzędzi ułatwień dostępu na Androidzie.