Oprócz podstawowych informacji, które zawiera komponent, takich jak ciąg tekstowy komponentu Text
, przydatne mogą być dodatkowe informacje o elementach interfejsu.
Informacje o znaczeniu i roli komponentu w Compose to separacja, która umożliwia przekazywanie dodatkowych informacji o elementach kompozytowych usługom takim jak ułatwienia dostępu, autouzupełnianie i testowanie. Na przykład ikona aparatu może być tylko obrazem, ale jej znaczenie semantyczne może być „Zrób zdjęcie”.
Łącząc odpowiednią semantykę z odpowiednimi interfejsami Compose, możesz przekazać usługom ułatwień dostępu jak najwięcej informacji o swoim komponencie. Usługi te mogą następnie zdecydować, jak przedstawić go użytkownikowi.
Interfejsy API Material i Compose UI oraz Foundation mają wbudowaną semantykę, która odpowiada ich roli i funkcji, ale możesz też zmodyfikować tę semantykę w przypadku istniejących interfejsów API lub ustawić nową w przypadku komponentów niestandardowych zgodnie ze swoimi wymaganiami.
Właściwości semantyczne
Właściwości semantyczne przekazują znaczenie odpowiedniego komponentu. Na przykład kompozyt Text
zawiera atrybuty semantyczne text
, ponieważ to znaczenie tego kompozytu. Element Icon
zawiera właściwość contentDescription
(jeśli została ustawiona przez programistę), która w tekstowej formie przekazuje znaczenie ikony.
Zastanów się, jak właściwości semantyczne przekazują znaczenie kompozytowego. Rozważ Switch
. Tak to wygląda dla użytkownika:

Switch
w stanie „Włączone” i „Wyłączone”.Aby opisać znaczenie tego elementu, możesz powiedzieć: "To jest przełącznik, który jest przełącznikiem przełączalnym w stanie „Włączono”. Możesz kliknąć ten przycisk, aby z nim wchodzić w interakcje”.
Dokładnie do tego służą właściwości semantyczne. Węzeł semantyczny tego elementu przełącznika zawiera te właściwości, jak pokazano w inspektorze układu:

Switch
Role
wskazuje typ elementu. Właściwość StateDescription
opisuje, jak należy odwoływać się do stanu „Wł.”. Domyślnie jest to zlokalizowana wersja słowa „Wł.”, ale w zależności od kontekstu można użyć bardziej szczegółowego słowa (np. „Włączone”). ToggleableState
to bieżący stan przełącznika. Właściwość OnClick
odwołuje się do metody używanej do interakcji z tym elementem.
Śledzenie właściwości semantycznych każdego komponentu w aplikacji otwiera wiele możliwości:
- Usługi ułatwień dostępu używają właściwości do wyświetlania interfejsu użytkownika na ekranie i ułatwiania użytkownikom interakcji z nim. W przypadku przełącznika usługa TalkBack może ogłosić: „Włączone; przełącznik; kliknij dwukrotnie, aby przełączyć”. Użytkownik może dwukrotnie dotknąć ekranu, aby wyłączyć przełącznik.
-
Platforma testowa używa właściwości do znajdowania węzłów, interakcji z nimi i dokonywania stwierdzeń:
val mySwitch = SemanticsMatcher.expectValue( SemanticsProperties.Role, Role.Switch ) composeTestRule.onNode(mySwitch) .performClick() .assertIsOff()
Elementy składane i modyfikatory utworzone na podstawie biblioteki podstawowej w komponowaniu są już domyślnie ustawione w odpowiednich właściwościach. Opcjonalnie możesz zmienić te właściwości ręcznie, aby poprawić obsługę ułatwień dostępu w przypadku określonych zastosowań, lub zmienić strategię łączenia lub czyszczenia komponentów.
Aby zasygnalizować usługi ułatwień dostępu o konkretnym typie treści komponentu, możesz zastosować różne rodzaje semantyki. Te dodatki będą wspierać główne informacje semantyczne i pomagać usługom ułatwień w dostosowywaniu sposobu wyświetlania, ogłaszania i interakcji z elementem.
Pełną listę właściwości semantycznych znajdziesz w obiekcie SemanticsProperties
. Pełną listę możliwych działań dotyczących ułatwień dostępu znajdziesz w obiekcie SemanticsActions
.
Nagłówki
Aplikacje często zawierają ekrany z dużą ilością tekstu, takie jak długie artykuły czy strony z wiadomościami, które są zwykle podzielone na różne podsekcje z tytułami:

Użytkownicy z potrzebami dotyczącymi ułatwień dostępu mogą mieć trudności z łatwym poruszaniem się po takim ekranie. Aby ułatwić nawigację, niektóre usługi ułatwień dostępu umożliwiają łatwiejszą nawigację bezpośrednio między sekcjami lub nagłówkami. Aby to umożliwić, określ, że komponent jest heading
, definiując jego właściwość semantyki:
@Composable private fun Subsection(text: String) { Text( text = text, style = MaterialTheme.typography.headlineSmall, modifier = Modifier.semantics { heading() } ) }
Alerty i wyskakujące okienka
Jeśli komponent to alert lub wyskakujące okienko, na przykład Snackbar
, możesz przekazać usługom ułatwień dostępu informacje o tym, że nowa struktura lub aktualizacje treści mogą być przekazywane użytkownikom.
Komponenty podobne do alertów można oznaczyć atrybutem semantycznym liveRegion
. Dzięki temu usługi ułatwień dostępu mogą automatycznie powiadamiać użytkownika o zmianach w tym komponencie lub jego elementach podrzędnych:
PopupAlert( message = "You have a new message", modifier = Modifier.semantics { liveRegion = LiveRegionMode.Polite } )
Tagu liveRegionMode.Polite
należy używać w większości przypadków, gdy uwagę użytkowników należy przykuć tylko na chwilę, aby mogli zobaczyć ostrzeżenia lub ważne treści na ekranie.
Aby uniknąć zakłóceń w opinii, należy oszczędnie używać liveRegion.Assertive
.
Powinien być używany w sytuacjach, w których ważne jest poinformowanie użytkowników o treściach o charakterze czasowym:
PopupAlert( message = "Emergency alert incoming", modifier = Modifier.semantics { liveRegion = LiveRegionMode.Assertive } )
Regionów na żywo nie należy używać w przypadku treści, które są często aktualizowane, np. zegarów odliczających czas, aby nie przytłaczać użytkowników ciągłymi informacjami zwrotnymi.
Komponenty podobne do okien
Komponenty niestandardowe przypominające okna, takie jak ModalBottomSheet
, wymagają dodatkowych sygnałów, aby odróżnić je od otaczających treści. W tym celu możesz użyć semantyki paneTitle
, aby wszelkie zmiany w oknie lub panelu były odpowiednio przedstawiane przez usługi ułatwień dostępu wraz z głównymi informacjami semantycznymi:
ShareSheet( message = "Choose how to share this photo", modifier = Modifier .fillMaxWidth() .align(Alignment.TopCenter) .semantics { paneTitle = "New bottom sheet" } )
Informacje na ten temat znajdziesz w artykule Jak Material 3 używa paneTitle
w komponentach.
Komponenty błędu
W przypadku innych typów treści, takich jak komponenty podobne do błędów, warto uzupełnić główne informacje semantyczne o informacje dla użytkowników z potrzebami w zakresie ułatwień dostępu.
Podczas definiowania stanów błędów możesz poinformować usługi ułatwień dostępu o semantyce error
i udostępnić rozszerzone komunikaty o błędach.
W tym przykładzie TalkBack odczytuje najpierw główną informację o błędzie, a potem dodatkowe, rozszerzone informacje:
Error( errorText = "Fields cannot be empty", modifier = Modifier .semantics { error("Please add both email and password") } )
Komponenty śledzenia postępów
W przypadku komponentów niestandardowych, które śledzą postęp, możesz chcieć powiadomić użytkowników o zmianach postępu, w tym o bieżącej wartości postępu, jego zakresie i wielkości kroku. Możesz to zrobić za pomocą semantyki progressBarRangeInfo
. Dzięki temu usługi ułatwień dostępu będą wiedzieć o zmianach postępu i będą mogły odpowiednio aktualizować użytkowników. Różne technologie wspomagające mogą też mieć unikalne sposoby sugerowania zwiększania i zmniejszania progresji.
ProgressInfoBar( modifier = Modifier .semantics { progressBarRangeInfo = ProgressBarRangeInfo( current = progress, range = 0F..1F ) } )
Informacje o liście i elementach
W przypadku niestandardowych list i tablic z dużą liczbą elementów może być przydatne, aby usługi ułatwień dostępu otrzymywały też bardziej szczegółowe informacje, np. łączną liczbę elementów i indeksów.
Dzięki semantycznym elementom collectionInfo
i collectionItemInfo
na liście usługa dostępności może poinformować użytkowników, na którym miejscu w kolekcji znajduje się element, o którym mowa. Dodatkowo usługa może wyświetlić informacje semantyczne w formie tekstu:
MilkyWayList( modifier = Modifier .semantics { collectionInfo = CollectionInfo( rowCount = milkyWay.count(), columnCount = 1 ) } ) { milkyWay.forEachIndexed { index, text -> Text( text = text, modifier = Modifier.semantics { collectionItemInfo = CollectionItemInfo(index, 0, 0, 0) } ) } }
Opis stanu
Element kompozycyjny może zdefiniować stateDescription
dla semantyki, której framework Androida używa do odczytu stanu elementu kompozycyjnego. Na przykład kompozyt z przełącznikiem może być w stanie „zaznaczony” lub „niezaznaczony”. W niektórych przypadkach warto zastąpić domyślne etykiety stanu, których używa aplikacja Compose. Aby to zrobić, przed zdefiniowaniem kompozytowalności jako przełącznika należy wyraźnie określić etykiety opisu stanu:
@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() } ) ) { /* ... */ } }
Działania niestandardowe
Działania niestandardowe mogą być używane do bardziej złożonych gestów na ekranie dotykowym, takich jak przesunięcie, aby zamknąć, czy przeciąganie i upuszczanie, ponieważ mogą one stanowić wyzwanie dla użytkowników z ograniczeniami ruchowymi lub innymi niepełnosprawnościami.
Aby ułatwić gestyk Swipe to dismiss, możesz połączyć go z działaniem niestandardowym, przekazując do niego działanie i etykietę:
SwipeToDismissBox( modifier = Modifier.semantics { // Represents the swipe to dismiss for accessibility customActions = listOf( CustomAccessibilityAction( label = "Remove article from list", action = { removeArticle() true } ) ) }, state = rememberSwipeToDismissBoxState(), backgroundContent = {} ) { ArticleListItem() }
Usługa ułatwień dostępu, taka jak TalkBack, wyróżnia komponent i wskazuje, że w menu są dostępne inne czynności, co oznacza, że można przesunąć palcem, aby zamknąć:

Innym zastosowaniem działań niestandardowych są długie listy elementów z większą liczbą dostępnych działań, ponieważ przeglądanie każdego działania osobno w przypadku każdego elementu może być żmudne:

Aby ulepszyć nawigację (co jest szczególnie przydatne w przypadku technologii wspomagających opartych na interakcjach, takich jak Switch Access czy Voice Access), możesz użyć niestandardowych działań w kontenerze, aby przenieść działania z poszczególnych przejść do osobnego menu działań:
ArticleListItemRow( modifier = Modifier .semantics { customActions = listOf( CustomAccessibilityAction( label = "Open article", action = { openArticle() true } ), CustomAccessibilityAction( label = "Add to bookmarks", action = { addToBookmarks() true } ), ) } ) { Article( modifier = Modifier.clearAndSetSemantics { }, onClick = openArticle, ) BookmarkButton( modifier = Modifier.clearAndSetSemantics { }, onClick = addToBookmarks, ) }
W takich przypadkach musisz ręcznie wyczyścić pierwotną semantykę podrzędnych za pomocą modyfikatora clearAndSetSemantics
, ponieważ przenosisz je do działań niestandardowych.
Na przykład w przypadku Switch Access po wybraniu kontenera otwiera się menu z dostępnymi działaniami:


Drzewo semantyczne
Kompozycja opisuje interfejs użytkownika aplikacji i jest generowana przez uruchomione komponenty. Kompozycja to struktura drzewikowa, która składa się z komponentów opisujących Twój interfejs.
Oprócz kompozycji istnieje równoległe drzewo, zwane drzewem semantycznym. To drzewo opisuje interfejs użytkownika w sposób alternatywny, zrozumiały dla usług ułatwień dostępu i ramy testowania. Usługi ułatwień dostępu używają drzewa do opisania aplikacji użytkownikom o konkretnych potrzebach. Platforma testowa korzysta z drzewa do interakcji z aplikacją i formułowania twierdzeń na jej temat. Drzewo semantyczne nie zawiera informacji o tym, jak rysować komponenty, ale zawiera informacje o semantycznym znaczeniu komponentów.

Jeśli Twoja aplikacja składa się z elementów składanych i modyfikatorów z fundamentów Compose oraz biblioteki Material, drzewo semantyczne jest wypełniane i generowane automatycznie. Jednak gdy dodajesz niestandardowe komponenty na niższym poziomie, musisz ręcznie podać ich semantykę. Mogą też wystąpić sytuacje, w których drzewo nie odzwierciedla prawidłowo lub w pełni znaczenia elementów na ekranie. W takim przypadku możesz je dostosować.
Weź pod uwagę na przykład ten niestandardowy komponent kalendarza:

W tym przykładzie cały kalendarz jest implementowany jako pojedyncza kompozycja niskiego poziomu, która korzysta z kompozycji Layout
i rysuje bezpośrednio do Canvas
.
Jeśli nie zrobisz nic więcej, usługi ułatwień dostępu nie otrzymają wystarczających informacji o treści składanego i wybranych przez użytkownika elementach w kalendarzu. Jeśli np. użytkownik kliknie dzień zawierający 17, platforma ułatwień dostępu otrzyma tylko informacje o opisie całej opcji kalendarza. W takim przypadku usługa ułatwień dostępu TalkBack ogłosiłaby „Kalendarz” lub, co jest tylko nieznacznie lepsze, „Kalendarz na kwiecień”, a użytkownik musiałby się domyślać, który dzień został wybrany. Aby ułatwić dostęp do tej usługi, musisz ręcznie dodać informacje semantyczne.
Złączone i niezłączone drzewo
Jak już wspomnieliśmy, każdy element w drzewie interfejsu może mieć ustawione właściwości semantyczne lub nie mieć ich wcale. Jeśli kompozyt nie ma ustawionych właściwości semantycznych, nie jest uwzględniany w drzewie semantycznym. Dzięki temu drzewo semantyczne zawiera tylko węzły, które faktycznie mają znaczenie semantyczne. Czasami jednak, aby przekazać prawidłowe znaczenie tego, co widać na ekranie, warto połączyć pewne poddrzewa węzłów i traktować je jako jedno. Dzięki temu możesz rozpatrywać zestaw węzłów jako całość, zamiast zajmować się poszczególnymi węzłami potomnymi. Zasadniczo każdy węzeł w tym drzewie reprezentuje element, na którym można się skupić podczas korzystania z usług ułatwień dostępu.
Przykładem takiego składanego jest Button
. Przycisk możesz traktować jako pojedynczy element, nawet jeśli zawiera on wiele węzłów podrzędnych:
Button(onClick = { /*TODO*/ }) { Icon( imageVector = Icons.Filled.Favorite, contentDescription = null ) Spacer(Modifier.size(ButtonDefaults.IconSpacing)) Text("Like") }
W drzewie semantycznym właściwości potomków przycisku są scalane, a sam przycisk jest w drzewie przedstawiany jako pojedynczy węzeł liściasty:

Elementy składane i modyfikatory mogą wskazywać, że chcą scalić właściwości semantyczne swoich potomków, wywołując funkcję Modifier.semantics
(mergeDescendants = true) {}
. Ustawienie tej właściwości na true
wskazuje, że właściwości semantyczne powinny zostać scalone. W przykładzie Button
kompozyt Button
używa wewnętrznie modyfikatora clickable
, który zawiera modyfikator semantics
. Dlatego potomne węzły przycisku są scalane.
Aby dowiedzieć się więcej o tym, kiedy należy zmienić zachowanie podczas łączenia w komponowalnym elemencie, przeczytaj dokumentację dotyczącą ułatwień dostępu.
Ta właściwość jest ustawiona w przypadku kilku modyfikatorów i komponentów w bibliotekach Foundation i Material Compose. Na przykład modyfikatory clickable
i toggleable
automatycznie scalą swoje potomki. Ponadto kompozyt ListItem
scali swoje potomki.
Sprawdzanie drzewa
Drzewo semantyczne to w istocie 2 różne drzewa. Istnieje scalony semantyczny drzewo, które scala potomne węzły, gdy parametr mergeDescendants
ma wartość true
.
Dostępne jest też drzewo semantyczne bez scalania, które nie stosuje scalania, ale zachowuje wszystkie węzły. Usługi ułatwień dostępu korzystają z drzewa nieskreślonego i stosują własne algorytmy scalania, biorąc pod uwagę usługę mergeDescendants
. Platforma testowa domyślnie używa scalonego drzewa.
Możesz sprawdzić oba drzewa za pomocą metody printToLog()
. Domyślnie, podobnie jak w poprzednich przykładach, zgrywane jest złączone drzewo. Aby zamiast tego wydrukować drzewo niescalone, ustaw parametr useUnmergedTree
dopasowania onRoot()
na true
:
composeTestRule.onRoot(useUnmergedTree = true).printToLog("MY TAG")
Kontroler układu umożliwia wyświetlanie złączonego i niezłączonego drzewa semantycznego. Wystarczy, że wybierzesz preferowane drzewo w filtrze widoku:

W przypadku każdego węzła w drzewie w panelu właściwości Inspektor układu wyświetla zarówno złączoną semantykę, jak i semantykę ustawioną dla tego węzła:

Domyślnie dopasowywacze w ramach platformy testowej korzystają ze złączonego drzewa semantycznego.
Dlatego możesz wchodzić w interakcję z elementem Button
, dopasowując tekst wyświetlany wewnątrz:
composeTestRule.onNodeWithText("Like").performClick()
Aby zmienić to zachowanie, ustaw parametr useUnmergedTree
w dopasowywaczach na true
, tak jak w dopasowywaczu onRoot
.
Dostosowywanie drzewa
Jak już wspomnieliśmy, możesz zastąpić lub wyczyścić określone właściwości semantyczne albo zmienić sposób scalania drzewa. Jest to szczególnie przydatne, gdy tworzysz własne komponenty niestandardowe. Jeśli nie skonfigurujesz właściwych właściwości i zachowania podczas scalania, aplikacja może być niedostępna, a testy mogą działać inaczej niż się spodziewasz. Więcej informacji o testowaniu znajdziesz w przewodniku.
Polecane dla Ciebie
- Uwaga: tekst linku jest wyświetlany, gdy obsługa JavaScript jest wyłączona
- Ułatwienia dostępu w sekcji „Tworzenie wiadomości”
- Material Design 2 w Compose
- Testowanie układu okna tworzenia wiadomości