Kompozycja opisuje interfejs aplikacji i jest tworzona przez uruchomienie funkcji kompozycyjnych. Kompozycja to struktura drzewa, która składa się z komponentów kompozycyjnych opisujących interfejs użytkownika.
Obok kompozycji znajduje się równoległe drzewo o nazwie drzewo semantyczne. To drzewo opisuje interfejs w alternatywny sposób, który jest zrozumiały dla usług ułatwień dostępu i platformy testowania. Usługi ułatwień dostępu używają drzewa do opisania aplikacji użytkownikom, którzy potrzebują tej aplikacji. Platforma testowa wykorzystuje drzewo do interakcji z aplikacją i wyciągania na nią asercji. Drzewo semantyczne nie zawiera informacji o rysowaniukomponentów, ale zawiera informacje o znaczeniuznakówelementów kompozycyjnych
Jeśli aplikacja składa się z kompozycji i modyfikatorów z podstawowych funkcji tworzenia i biblioteki materiałów, drzewo semantyki zostanie automatycznie wypełnione i wygenerowane. Jeśli jednak dodajesz niestandardowe elementy kompozycyjne niskiego poziomu, musisz określić ich semantykę ręcznie. Mogą też wystąpić sytuacje, gdy drzewo nie oddaje poprawnie lub w pełni znaczenia elementów na ekranie. W takich przypadkach możesz dostosować drzewo.
Weźmy na przykład ten niestandardowy kalendarz kompozycyjny:
W tym przykładzie cały kalendarz został wdrożony jako jeden niskopoziomowy element kompozycyjny przy użyciu funkcji kompozycyjnej Layout
i rysunku bezpośrednio w elemencie Canvas
.
Jeśli nic więcej nie zrobisz, usługi ułatwień dostępu nie będą otrzymywać wystarczających informacji o zawartości elementu kompozycyjnego i wyborze użytkownika w kalendarzu. Jeśli na przykład użytkownik kliknie dzień zawierający wartość 17, platforma ułatwień dostępu otrzyma tylko opis dla całego ustawienia kalendarza. W takim przypadku usługa ułatwień dostępu TalkBack
powiadamia „Kalendarz” lub, tylko trochę lepiej, „kalendarz kwietniowy”, a użytkownik mógł się zastanawiać, jaki dzień został wybrany. Aby ułatwić dostęp do tego elementu kompozycyjnego, musisz ręcznie dodać informacje semantyczne.
Właściwości semantyczne
Wszystkie węzły w drzewie interfejsu o określonym znaczeniu semantycznym mają w drzewie semantycznym węzeł równoległy. Węzeł w drzewie semantycznym zawiera te właściwości, które przekazują znaczenie odpowiedniego elementu kompozycyjnego. Na przykład element kompozycyjny Text
zawiera właściwość semantyczną text
, bo to jest znaczenie tego elementu kompozycyjnego. Element Icon
zawiera właściwość contentDescription
(jeśli została ustawiona przez programistę), która informuje w formie tekstowej, jakie jest znaczenie elementu Icon
.
Elementy kompozycyjne i modyfikatory, które są utworzone na podstawie biblioteki podstawowej tworzenia, ustawiają już odpowiednie właściwości. Opcjonalnie możesz ustawić lub zastąpić właściwości samodzielnie modyfikatorami semantics
i clearAndSetSemantics
. Na przykład dodaj do węzła niestandardowe działania związane z ułatwieniami dostępu, podaj alternatywny opis stanu elementu z możliwością przełączania lub wskaż, że określony tekst kompozycyjny powinien być traktowany jako nagłówek.
Aby zwizualizować drzewo semantyki, użyj narzędzia Inspektor układu lub metody printToLog()
w testach. Spowoduje to wydrukowanie bieżącego drzewa semantyki w narzędziu Logcat.
class MyComposeTest { @get:Rule val composeTestRule = createComposeRule() @Test fun MyTest() { // Start the app composeTestRule.setContent { MyTheme { Text("Hello world!") } } // Log the full semantics tree composeTestRule.onRoot().printToLog("MY TAG") } }
Wynik tego testu to:
Printing with useUnmergedTree = 'false'
Node #1 at (l=0.0, t=63.0, r=221.0, b=120.0)px
|-Node #2 at (l=0.0, t=63.0, r=221.0, b=120.0)px
Text = '[Hello world!]'
Actions = [GetTextLayoutResult]
Zastanów się, w jaki sposób właściwości semantyczne przekazują znaczenie elementu kompozycyjnego. Rozważmy cel Switch
. Użytkownik zobaczy to w taki sposób:
Aby opisać znaczenie tego elementu, możesz powiedzieć: „To jest Switch. Jest to element, który można przełączać. Wystarczy kliknąć."
Do tego właśnie służą właściwości semantyczne. Węzeł semantyczny tego elementu Switch zawiera następujące właściwości, jak pokazano w inspektorze układu:
Pole Role
wskazuje typ elementu. StateDescription
określa, jak należy się odwoływać do stanu „Włączone”. Domyślnie jest to zlokalizowana wersja słowa „Włączone”, ale można ją sprecyzować (np. „Włączone”) w zależności od kontekstu. ToggleableState
to bieżący stan przełącznika. Właściwość OnClick
odwołuje się do metody używanej do interakcji z tym elementem. Pełną listę właściwości semantyki znajdziesz w obiekcie SemanticsProperties
. Pełną listę możliwych działań związanych z ułatwieniami dostępu znajdziesz w obiekcie SemanticsActions
.
Monitorowanie właściwości semantycznych każdego elementu kompozycyjnego w aplikacji otwiera mnóstwo zaawansowanych możliwości. Przykłady:
- TalkBack korzysta z właściwości do odczytywania na głos tego, co jest wyświetlane na ekranie, i umożliwia użytkownikowi płynne korzystanie z nich. W przypadku funkcji przełączania kompozycyjnego TalkBack może powiedzieć: „Włączono; przełącz; kliknij dwukrotnie, aby przełączyć”. Użytkownik może kliknąć dwukrotnie ekran, aby wyłączyć przełącznik.
-
Platforma do testowania używa właściwości do znajdowania węzłów, interakcji z nimi i dokonywania asercji. Przykładowy test funkcji Switch może wyglądać tak:
val mySwitch = SemanticsMatcher.expectValue( SemanticsProperties.Role, Role.Switch ) composeTestRule.onNode(mySwitch) .performClick() .assertIsOff()
Scalone i niescalone drzewo semantyki
Jak już wspomnieliśmy, każdy element kompozycyjny w drzewie interfejsu może mieć 0 lub więcej właściwości semantytycznych. Gdy funkcja kompozycyjna nie ma ustawionych właściwości semantycznych, nie jest uwzględniany w drzewie semantyki. Dzięki temu drzewo semantyczne będzie zawierać tylko te węzły, które mają znaczenie semantyczne. Czasem jednak, aby przekazać poprawne znaczenie tego, co widać na ekranie, warto też scalić niektóre poddrzewa węzłów i potraktować je jako jedno. W ten sposób możesz wyciągać wnioski dotyczące zbioru węzłów jako całości, zamiast zajmować się każdym węzłem podrzędnym oddzielnie. Zgodnie z ogólną zasadą każdy węzeł w tym drzewie reprezentuje element, który można zaznaczyć podczas korzystania z usług ułatwień dostępu.
Przykładem tego elementu kompozycyjnego jest Button
. Można myśleć o jednym przycisku
jako 1 elementu, nawet jeśli może on zawierać 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 elementów podrzędnych przycisku są scalone, a przycisk jest przedstawiony jako węzeł pojedynczego liścia:
Elementy kompozycyjne i modyfikatory mogą wskazywać, że chcą scalić właściwości semantyczne elementów podrzędnych, wywołując metodę 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
funkcja kompozycyjna Button
korzysta wewnętrznie z modyfikatora clickable
, który zawiera ten modyfikator semantics
. W ten sposób węzły podrzędne przycisku zostaną scalone.
Przeczytaj dokumentację ułatwień dostępu, aby dowiedzieć się, kiedy należy zmienić sposób łączenia w komponencie.
Kilka modyfikatorów i funkcji kompozycyjnych w bibliotekach Podstawowe i Material Compose ma ustawioną tę właściwość. Na przykład modyfikatory clickable
i toggleable
automatycznie scalą swoje elementy podrzędne. Oprócz tego funkcja kompozycyjna ListItem
scali swoje elementy podrzędne.
Skontroluj drzewa
Drzewo semantyczne to w rzeczywistości dwa różne drzewa. Jest scalone drzewo semantyczne, które scala węzły podrzędne, gdy parametr mergeDescendants
ma wartość true
.
Jest też niescalone drzewo semantyczne, które nie stosuje scalania, ale zachowuje wszystkie węzły. Usługi ułatwień dostępu korzystają z niescalonego drzewa i stosują własne algorytmy scalania z uwzględnieniem właściwości mergeDescendants
. Platforma testowa domyślnie używa scalonego drzewa.
Oba drzewa możesz zbadać za pomocą metody printToLog()
. Domyślnie, tak jak we wcześniejszych przykładach, scalone drzewo jest logowane. Aby zamiast tego wydrukować niescalone drzewo, ustaw parametr useUnmergedTree
w dopasowywaniu onRoot()
na true
:
composeTestRule.onRoot(useUnmergedTree = true).printToLog("MY TAG")
Inspektor układu pozwala wyświetlić zarówno scalone, jak i niescalone drzewo semantyczne, wybierając je w filtrze widoku:
W każdym węźle drzewa Inspektor układu wyświetla w panelu właściwości zarówno scaloną semantyki, jak i zestaw semantyki w tym węźle:
Domyślnie dopasowania w platformie testowania używają scalonego drzewa semantycznego.
Dlatego możesz wchodzić w interakcje z elementem Button
, dopasowując wyświetlany w nim tekst:
composeTestRule.onNodeWithText("Like").performClick()
Aby zastąpić to działanie, ustaw parametr useUnmergedTree
dopasowań na true
, tak jak w przypadku dopasowania onRoot
.
Zachowanie scalania
Jak przebiega scalanie, gdy funkcja kompozycyjna wskazuje, że powinny zostać scalone jej elementy potomne?
Każda właściwość semantyczna ma zdefiniowaną strategię scalania. Na przykład właściwość ContentDescription
dodaje do listy wszystkie podrzędne wartości ContentDescription. Sprawdź strategię scalania właściwości semantycznej, sprawdzając jej implementację mergePolicy
w narzędziu SemanticsProperties.kt
. Właściwości mogą przyjmować wartości nadrzędne lub podrzędne, scalać je w listę lub ciąg znaków, nie zezwalać na scalanie i zamiast tego zgłaszać wyjątek ani jakąkolwiek inną niestandardową strategię scalania.
Pamiętaj, że komponenty podrzędne, które same ustawiły mergeDescendants
= true
, nie są uwzględniane w scalaniu. Przeanalizujmy przykład:
To jest element listy, który można kliknąć. Gdy użytkownik kliknie wiersz, aplikacja przejdzie na stronę z informacjami o artykule, na której może go przeczytać. W elemencie listy znajduje się przycisk dodawania artykułu do zakładek, który tworzy zagnieżdżony element możliwy do kliknięcia. W ten sposób przycisk będzie widoczny osobno w scalonym drzewie. Pozostała treść wiersza jest scalona:
Dostosowywanie drzewa semantyki
Jak już wspomnieliśmy, możesz zastąpić lub wyczyścić niektóre właściwości semantyczne bądź zmienić sposób scalania drzewa. Jest to szczególnie przydatne, gdy tworzysz własne komponenty niestandardowe. Bez ustawienia odpowiednich właściwości i sposobu scalania aplikacja może być niedostępna, a testy mogą działać inaczej niż oczekiwano. Aby dowiedzieć się więcej o niektórych typowych przypadkach użycia, w których warto dostosować drzewo semantyczne, zapoznaj się z dokumentacją ułatwień dostępu. Więcej informacji o testowaniu znajdziesz w przewodniku po testach.
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.
Polecane dla Ciebie
- Uwaga: tekst linku jest wyświetlany, gdy JavaScript jest wyłączony
- Ułatwienia dostępu przy tworzeniu wiadomości
- Material Design 2 w obszarze Utwórz
- Testowanie układu tworzenia wiadomości