Помимо основной информации, которую несет составной объект, например текстовой строки составного объекта Text
, может быть полезно иметь дополнительную информацию об элементах пользовательского интерфейса.
Информация о значении и роли компонента в Compose называется семантикой и представляет собой способ предоставить дополнительный контекст о компонуемых объектах для таких служб, как доступность, автозаполнение и тестирование. Например, значок камеры может визуально представлять собой просто изображение, но смысловое значение может быть «Сделай фото».
Комбинируя соответствующую семантику с соответствующими API-интерфейсами Compose, вы предоставляете как можно больше информации о своем компоненте службам доступности, которые затем решают, как представить ее пользователю.
API-интерфейсы Material и Compose, а также Foundation API имеют встроенную семантику, соответствующую их конкретной роли и функциям, но вы также можете изменить эту семантику для существующих API или установить новую для пользовательских компонентов в соответствии с вашими конкретными требованиями.
Семантические свойства
Семантические свойства передают значение соответствующего составного объекта. Например, составной объект Text
содержит семантическое свойство text
, потому что это значение этого составного объекта. Icon
содержит свойство contentDescription
(если оно установлено разработчиком), которое в виде текста передает значение значка.
Рассмотрим, как свойства семантики передают значение составного объекта. Рассмотрим Switch
. Вот как это выглядит для пользователя:

Switch
в состояниях «Включено» и «Выключено».Чтобы описать значение этого элемента, вы могли бы сказать следующее: «Это переключатель, который представляет собой переключаемый элемент в состоянии «Включено». Вы можете щелкнуть его, чтобы взаимодействовать с ним».
Именно для этого и используются свойства семантики. Узел семантики этого элемента Switch содержит следующие свойства, визуализированные с помощью инспектора макета:

Switch
. Role
указывает тип элемента. StateDescription
описывает, как следует ссылаться на состояние «Включено». По умолчанию это локализованная версия слова «Включено», но ее можно сделать более конкретной (например, «Включено») в зависимости от контекста. ToggleableState
— это текущее состояние коммутатора. Свойство OnClick
ссылается на метод, используемый для взаимодействия с этим элементом.
Отслеживание семантических свойств каждого компонуемого объекта в вашем приложении открывает множество мощных возможностей:
- Службы специальных возможностей используют свойства для представления пользовательского интерфейса, отображаемого на экране, и позволяют пользователям взаимодействовать с ним. Для составного переключателя Talkback может объявить: «Вкл.; Переключить; дважды коснитесь, чтобы переключиться». Пользователь может дважды коснуться экрана, чтобы выключить переключатель.
- Платформа тестирования использует свойства для поиска узлов, взаимодействия с ними и создания утверждений:
val mySwitch = SemanticsMatcher.expectValue( SemanticsProperties.Role, Role.Switch ) composeTestRule.onNode(mySwitch) .performClick() .assertIsOff()
Компонуемые объекты и модификаторы, созданные на основе базовой библиотеки Compose, уже по умолчанию задают для вас соответствующие свойства. При желании вы можете изменить эти свойства вручную, чтобы улучшить поддержку специальных возможностей для конкретных случаев использования, или изменить стратегию слияния или очистки составных элементов.
Чтобы указать конкретный тип контента вашего компонента службам доступности, вы можете применить множество различных семантик. Эти дополнения будут поддерживать основную семантическую информацию и помогут службам доступности точно настроить способ представления, объявления или взаимодействия вашего компонента.
Полный список свойств семантики см. в объекте SemanticsProperties
. Полный список возможных действий доступности см. в объекте SemanticsActions
.
Заголовки
Приложения часто содержат экраны с насыщенным текстом контентом, например, длинными статьями или новостными страницами, которые обычно разделены на разные подразделы с заголовками:

У пользователей с ограниченными возможностями могут возникнуть трудности с навигацией по такому экрану. Чтобы улучшить навигацию, некоторые службы специальных возможностей позволяют упростить навигацию непосредственно между разделами или заголовками. Чтобы включить это, укажите, что ваш компонент является heading
, определив его свойство семантики:
@Composable private fun Subsection(text: String) { Text( text = text, style = MaterialTheme.typography.headlineSmall, modifier = Modifier.semantics { heading() } ) }
Оповещения и всплывающие окна
Если ваш компонент представляет собой предупреждение или всплывающее окно, например Snackbar
, вы можете сообщить службам доступности о том, что новая структура или обновления контента могут быть переданы пользователям.
Компоненты, подобные оповещениям, можно пометить семантическим свойством liveRegion
. Это позволяет службам доступности автоматически уведомлять пользователя об изменениях в этом компоненте или его дочерних элементах:
PopupAlert( message = "You have a new message", modifier = Modifier.semantics { liveRegion = LiveRegionMode.Polite } )
Вам следует использовать liveRegionMode.Polite
в большинстве случаев, когда внимание пользователей должно быть привлечено лишь на короткое время к предупреждениям или важному изменению содержимого на экране.
Вам следует использовать liveRegion.Assertive
умеренно, чтобы избежать разрушительной обратной связи. Его следует использовать в ситуациях, когда крайне важно, чтобы пользователи были осведомлены о контенте, чувствительном ко времени:
PopupAlert( message = "Emergency alert incoming", modifier = Modifier.semantics { liveRegion = LiveRegionMode.Assertive } )
Живые регионы не следует использовать в контенте, который часто обновляется, например в таймерах обратного отсчета, чтобы не перегружать пользователей постоянной обратной связью.
Окноподобные компоненты
Пользовательские компоненты, похожие на окна, подобные ModalBottomSheet
, нуждаются в дополнительных сигналах, чтобы отличать их от окружающего контента. Для этого вы можете использовать семантику paneTitle
, чтобы любые соответствующие изменения окна или панели могли быть соответствующим образом представлены службами доступности вместе с основной семантической информацией:
ShareSheet( message = "Choose how to share this photo", modifier = Modifier .fillMaxWidth() .align(Alignment.TopCenter) .semantics { paneTitle = "New bottom sheet" } )
Для справки посмотрите, как Материал 3 использует paneTitle
для своих компонентов.
Компоненты ошибки
Для других типов контента, таких как компоненты, подобные ошибкам, вам может потребоваться расширить основную семантическую информацию для пользователей с потребностями специальных возможностей. При определении состояний ошибок вы можете сообщить службам доступности их семантику error
и предоставить расширенные сообщения об ошибках.
В этом примере TalkBack считывает основную текстовую информацию об ошибке, за которой следуют дополнительные расширенные сообщения:
Error( errorText = "Fields cannot be empty", modifier = Modifier .semantics { error("Please add both email and password") } )
Компоненты отслеживания прогресса
Для пользовательских компонентов, которые отслеживают прогресс, вы можете уведомить пользователей об изменениях прогресса, включая текущее значение прогресса, его диапазон и размер шага. Вы можете сделать это с помощью семантики progressBarRangeInfo
— это гарантирует, что службы доступности будут знать об изменениях хода выполнения и смогут соответствующим образом обновлять пользователей. Различные вспомогательные технологии также могут иметь уникальные способы намекать на увеличение или уменьшение прогресса.
ProgressInfoBar( modifier = Modifier .semantics { progressBarRangeInfo = ProgressBarRangeInfo( current = progress, range = 0F..1F ) } )
Список и информация о товаре
В пользовательских списках и сетках с большим количеством элементов службам специальных возможностей может быть полезно получать более подробную информацию, например общее количество элементов и индексов.
Используя семантику collectionInfo
и collectionItemInfo
в списке и элементах соответственно, в этом длинном списке службы доступности могут информировать пользователей о том, в каком индексе элемента из общей коллекции они находятся, в дополнение к текстовой семантической информации:
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) } ) } }
Описание состояния
Составной объект может определить stateDescription
для семантики, которую платформа Android использует для считывания состояния, в котором находится составной объект. Например, переключаемый составной объект может находиться либо в «проверенном», либо в «непроверенном» состоянии. В некоторых случаях вам может потребоваться переопределить метки описания состояния по умолчанию, которые использует Compose. Вы можете сделать это, явно указав метки описания состояния перед определением составного объекта как переключаемого:
@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() } ) ) { /* ... */ } }
Пользовательские действия
Пользовательские действия можно использовать для более сложных жестов на сенсорном экране, таких как смахивание для закрытия или перетаскивание, поскольку пользователям с двигательными нарушениями или другими ограниченными возможностями может быть сложно взаимодействовать с ними.
Чтобы сделать жест пролистывания для отклонения более доступным, вы можете связать его с настраиваемым действием, передав туда действие и метку отклонения:
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() }
Служба специальных возможностей, такая как TalkBack, затем выделяет компонент и намекает, что в его меню доступны дополнительные действия, представляя собой пролистывание для отклонения действия:

Еще один вариант использования настраиваемых действий — длинные списки с элементами, для которых доступно больше действий, поскольку пользователям может быть утомительно перебирать каждое действие для каждого отдельного элемента:

Чтобы улучшить возможности навигации, что особенно полезно для вспомогательных технологий на основе взаимодействия, таких как Switch Access или Voice Access, вы можете использовать настраиваемые действия в контейнере, чтобы переместить действия из отдельного обхода в отдельное меню действий:
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, ) }
В этих случаях обязательно вручную очистите исходную дочернюю семантику с помощью clearAndSetSemantics
при перемещении их в настраиваемые действия.
Если использовать в качестве примера Switch Access, его меню открывается при выборе контейнера и содержит список доступных вложенных действий:


Дерево семантики
Композиция описывает пользовательский интерфейс вашего приложения и создается путем запуска компонуемых объектов. Композиция представляет собой древовидную структуру, состоящую из составных элементов, описывающих ваш пользовательский интерфейс.
Рядом с композицией существует параллельное дерево, называемое деревом семантики . Это дерево описывает ваш пользовательский интерфейс альтернативным способом, понятным для служб специальных возможностей и платформы тестирования . Службы доступности используют дерево для описания приложения пользователям с конкретными потребностями. Платформа тестирования использует дерево для взаимодействия с вашим приложением и формирования утверждений о нем. Дерево семантики не содержит информации о том, как рисовать составные элементы, но содержит информацию о семантическом значении ваших составных элементов.

Если ваше приложение состоит из компонуемых компонентов и модификаторов из основы Compose и библиотеки материалов, дерево семантики заполняется и генерируется автоматически. Однако при добавлении пользовательских низкоуровневых компонуемых объектов вам придется вручную указывать их семантику . Также могут возникнуть ситуации, когда ваше дерево неправильно или не полностью отражает значение элементов на экране, и в этом случае вы можете адаптировать дерево.
Рассмотрим, например, этот пользовательский составной календарь:

В этом примере весь календарь реализован как один низкоуровневый компонуемый объект с использованием компонуемого Layout
и рисования непосредственно на Canvas
. Если вы больше ничего не сделаете, службы специальных возможностей не получат достаточно информации о содержимом компонуемого объекта и выборе пользователя в календаре. Например, если пользователь щелкает день, содержащий 17, платформа специальных возможностей получает только информацию описания для всего элемента управления календарем. В этом случае служба специальных возможностей TalkBack объявит «Календарь» или, что немного лучше, «Апрельский календарь», и пользователю останется только гадать, какой день был выбран. Чтобы сделать этот составной объект более доступным, вам нужно будет добавить семантическую информацию вручную.
Объединенное и несвязанное дерево
Как упоминалось ранее, для каждого компонуемого элемента в дереве пользовательского интерфейса может быть установлено ноль или более семантических свойств. Если у составного объекта не установлены свойства семантики, он не включается в дерево семантики. Таким образом, дерево семантики содержит только те узлы, которые действительно содержат семантическое значение. Однако иногда, чтобы передать правильное значение того, что показано на экране, также полезно объединить определенные поддеревья узлов и рассматривать их как одно. Таким образом, вы можете рассуждать о наборе узлов в целом, вместо того, чтобы иметь дело с каждым узлом-потомком по отдельности. Как правило, каждый узел в этом дереве представляет собой фокусируемый элемент при использовании служб доступности.
Примером такого компонуемого объекта является Button
. Вы можете рассматривать кнопку как один элемент, даже если она может содержать несколько дочерних узлов:
Button(onClick = { /*TODO*/ }) { Icon( imageVector = Icons.Filled.Favorite, contentDescription = null ) Spacer(Modifier.size(ButtonDefaults.IconSpacing)) Text("Like") }
В дереве семантики свойства потомков кнопки объединяются, и кнопка представляется как один листовой узел в дереве:

Составные объекты и модификаторы могут указать, что они хотят объединить свойства семантики своих потомков, вызвав Modifier.semantics (mergeDescendants = true) {}
. Установка для этого свойства значения true
указывает, что свойства семантики следует объединить. В примере Button
составная Button
использует внутренний модификатор clickable
, который включает в себя этот модификатор semantics
. Таким образом, узлы-потомки кнопки объединяются. Прочтите документацию по специальным возможностям, чтобы узнать больше о том, когда следует изменить поведение слияния в компонуемом объекте.
Это свойство установлено у нескольких модификаторов и компонуемых объектов в библиотеках Foundation и Material Compose. Например, clickable
и toggleable
модификаторы автоматически объединят своих потомков. Кроме того, составной элемент ListItem
объединит своих потомков.
Осмотрите дерево
Семантическое дерево на самом деле представляет собой два разных дерева. Существует объединенное дерево семантики, которое объединяет узлы-потомки, если для mergeDescendants
установлено значение true
. Существует также несвязанное семантическое дерево, в котором слияние не применяется, но сохраняется каждый узел нетронутым. Службы доступности используют неслитое дерево и применяют свои собственные алгоритмы слияния с учетом свойства mergeDescendants
. Платформа тестирования по умолчанию использует объединенное дерево.
Вы можете проверить оба дерева с помощью метода printToLog()
. По умолчанию, как и в предыдущих примерах, объединенное дерево протоколируется. Чтобы вместо этого распечатать неслитое дерево, установите для параметра useUnmergedTree
сопоставителя onRoot()
значение true
:
composeTestRule.onRoot(useUnmergedTree = true).printToLog("MY TAG")
Инспектор макета позволяет отображать как объединенное, так и несвязанное дерево семантики, выбрав предпочтительное дерево в фильтре представления:

Для каждого узла вашего дерева Инспектор макета отображает как объединенную семантику, так и семантику, установленную для этого узла на панели свойств:

По умолчанию средства сопоставления в среде тестирования используют объединенное дерево семантики. Вот почему вы можете взаимодействовать с Button
, сопоставляя текст, показанный внутри нее:
composeTestRule.onNodeWithText("Like").performClick()
Переопределите это поведение, установив для параметра useUnmergedTree
сопоставителей значение true
, как и в случае с сопоставителем onRoot
.
Адаптируйте дерево
Как упоминалось ранее, вы можете переопределить или очистить определенные свойства семантики или изменить поведение дерева при слиянии. Это особенно актуально, когда вы создаете свои собственные компоненты. Без установки правильных свойств и поведения слияния ваше приложение может быть недоступно, а тесты могут вести себя иначе, чем вы ожидаете. Если вы хотите узнать больше о тестировании, обратитесь к руководству по тестированию .
{% дословно %}Рекомендуется для вас
- Примечание. Текст ссылки отображается, когда JavaScript отключен.
- Доступность в Compose
- Material Design 2 в Compose
- Тестирование макета Compose