Material, Compose UI и Foundation API по умолчанию реализуют и предлагают множество доступных практик. Они содержат встроенную семантику, которая соответствует их конкретной роли и функции. Это означает, что большая часть поддержки специальных возможностей предоставляется с минимальной дополнительной работой или без нее.
Использование соответствующих API для соответствующих целей означает, что компоненты обычно поставляются с предопределенным поведением специальных возможностей, которое охватывает стандартные варианты использования. Однако всегда проверяйте, соответствуют ли эти значения по умолчанию вашим потребностям в специальных возможностях. Если нет, Compose предоставляет способы удовлетворения более конкретных требований.
Понимание семантики и шаблонов специальных возможностей по умолчанию в Compose API помогает использовать их с учетом специальных возможностей. Это также помогает вам поддерживать доступность в более настраиваемых компонентах.
Минимальные размеры областей прикосновения
Любой элемент на экране, с которым пользователь может взаимодействовать, должен быть достаточно большим. При выборе размера этих элементов убедитесь, что минимальный размер составляет 48 dp, чтобы правильно следовать рекомендациям по специальным возможностям Material Design.
Компоненты Material, такие как Checkbox, RadioButton, Switch, Slider и Surface, устанавливают этот минимальный размер внутри, но только когда компонент может получать действия пользователя. Например, если для параметра onCheckedChange элемента Checkbox задано ненулевое значение, флажок будет иметь отступы, чтобы его ширина и высота составляли не менее 48 dp.
@Composable private fun CheckableCheckbox() { Checkbox(checked = true, onCheckedChange = {}) }
Если для параметра onCheckedChange задано значение null, отступы не добавляются, поскольку с компонентом нельзя взаимодействовать напрямую.
@Composable private fun NonClickableCheckbox() { Checkbox(checked = true, onCheckedChange = null) }
При реализации элементов управления выбором, таких как Switch, RadioButton или Checkbox, вы обычно переносите поведение клика в родительский контейнер, устанавливая обратный вызов клика для composable-функции на null и добавляя модификатор toggleable или selectable к родительской composable-функции.
@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) } } }
Если размер интерактивной composable-функции меньше минимального размера области прикосновения, Compose все равно увеличивает размер области прикосновения. Это достигается за счет расширения размера области прикосновения за пределы границ composable-функции.
В примере ниже показан очень маленький кликабельный элемент Box. Область прикосновения автоматически расширяется за пределы элемента Box, поэтому нажатие рядом с ним все равно вызывает событие клика.Box
@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) ) } }
Чтобы области касания разных composable-функций не перекрывались, всегда задавайте для них достаточно большой минимальный размер. В нашем примере это означает, что нужно использовать модификатор sizeIn, чтобы задать минимальный размер внутреннего поля:
@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) ) } }
Если элемент должен оставаться небольшим, используйте модификатор minimumInteractiveComponentSize из Material 3. Он резервирует не менее 48 dp пространства макета в каждом измерении и центрирует элемент в этом пространстве, поэтому элемент сохраняет свой нарисованный размер, а элементы вокруг него перемещаются, чтобы освободить место. Модификатор не расширяет саму область прикосновения, а оставляет в макете место для расширения, описанного ранее.
Компоненты Material, такие как Checkbox, используют этот модификатор внутри. Поместите его перед модификатором размера, поскольку модификатор размера перед ним ограничивает пространство, которое он может зарезервировать.
@Composable private fun MinimumInteractiveBox() { var clicked by remember { mutableStateOf(false) } Row(verticalAlignment = Alignment.CenterVertically) { Box( Modifier .clickable { clicked = !clicked } .minimumInteractiveComponentSize() .size(24.dp) .background(if (clicked) Color.DarkGray else Color.Black) ) Text("Option") } }
Графические элементы
Когда вы определяете composable-функцию Image или Icon, фреймворк Android не может автоматически определить, что показывает приложение. Вам нужно передать текстовое описание графического элемента.
Представьте себе экран, на котором пользователь может поделиться текущей страницей с друзьями. На этом экране есть значок совместного доступа, на который можно нажать:
Фреймворк Android не может описать значок пользователю с нарушением зрения. Фреймворку Android требуется дополнительное текстовое описание значка.
Параметр contentDescription описывает графический элемент. Используйте локализованную строку, так как она видна пользователю.
@Composable private fun ShareButton(onClick: () -> Unit) { IconButton(onClick = onClick) { Icon( imageVector = Icons.Filled.Share, contentDescription = stringResource(R.string.label_share) ) } }
Некоторые графические элементы являются чисто декоративными, и вы, возможно, не захотите сообщать о них пользователю. Если для параметра contentDescription задано значение null, это указывает фреймворку Android, что с элементом не связаны действия или состояния.
@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) ) }
contentDescription в основном используется для графических элементов, например изображений. Компоненты Material, такие как Button или Text, и действия, такие как clickable или toggleable, поставляются с другой предопределенной семантикой, которая описывает их внутреннее поведение и может быть изменена с помощью других API Compose.
Интерактивные элементы
API Material и Foundation Compose создают элементы интерфейса, с которыми пользователи могут взаимодействовать через API модификаторов clickable и toggleable. Поскольку интерактивные компоненты могут состоять из нескольких элементов, clickable и toggleable по умолчанию объединяют семантику своих дочерних элементов, чтобы компонент рассматривался как одна логическая сущность.
Например, Material Button может состоять из дочернего значка и текста. Вместо того чтобы обрабатывать дочерние элементы как отдельные, Material Button по умолчанию объединяет их семантику, чтобы службы специальных возможностей могли группировать их соответствующим образом:
Аналогично, при использовании модификатора clickable composable-функция также объединяет семантику своих дочерних элементов в один объект, который отправляется в специальные возможности с соответствующим представлением действия:
Row( // Uses `mergeDescendants = true` under the hood modifier = Modifier.clickable { openArticle() } ) { Icon( painter = painterResource(R.drawable.ic_logo), contentDescription = "Open", ) Text("Accessibility in Compose") }
Вы также можете установить определенный onClickLabel для родительского кликабельного элемента, чтобы предоставить дополнительную информацию службам специальных возможностей и предложить более точное представление действия:
Row( modifier = Modifier .clickable(onClickLabel = "Open this article") { openArticle() } ) { Icon( painter = painterResource(R.drawable.ic_logo), contentDescription = "Open" ) Text("Accessibility in Compose") }
Например, если вы используете TalkBack, модификатор clickable и ярлык клика позволят TalkBack предоставить подсказку "Нажмите дважды, чтобы открыть эту статью" вместо стандартной подсказки "Нажмите дважды, чтобы активировать".
Ответ зависит от типа действия. При долгом нажатии TalkBack озвучит подсказку "Нажмите дважды и удерживайте, чтобы", а затем ярлык:
Row( modifier = Modifier .combinedClickable( onLongClickLabel = "Bookmark this article", onLongClick = { addToBookmarks() }, onClickLabel = "Open this article", onClick = { openArticle() }, ) ) {}
В некоторых случаях у вас может не быть прямого доступа к модификатору clickable (например, когда он установлен где-то на более низком вложенном уровне), но вы все равно хотите изменить метку объявления по умолчанию. Для этого разделите настройку clickable и изменение объявления, используя модификатор semantics и задав в нем ярлык клика, чтобы изменить представление действия:
@Composable private fun ArticleList(openArticle: () -> Unit) { NestedArticleListItem( // Clickable is set separately, in a nested layer: onClickAction = openArticle, // Semantics are set here: modifier = Modifier.semantics { onClick( label = "Open this article", action = { // Not needed here: openArticle() true } ) } ) }
Передавать действие клика дважды не нужно. Существующие API Compose, такие как clickable или Button, обрабатывают это за вас. Логика объединения проверяет, что для имеющейся информации используются ярлык и действие самого внешнего модификатора. В предыдущем примере NestedArticleListItem автоматически передает действие нажатия openArticle() в свою семантику clickable. Вы можете оставить действие клика пустым во втором действии модификатора семантики. Однако ярлык клика берется из второго модификатора семантики onClick(label = "Open this document"), поскольку в первом он отсутствует.
Вы можете столкнуться со сценариями, в которых ожидается, что семантика дочерних элементов будет объединена с семантикой родительского элемента, но этого не происходит. Подробнее об объединении и очистке…
Специальные компоненты
При создании собственного компонента изучите реализацию похожего компонента в библиотеке Material или других библиотеках Compose. Затем скопируйте или измените его поведение в отношении специальных возможностей. Например, если вы заменяете Material Checkbox собственной реализацией, то, взглянув на существующую реализацию Checkbox, вы вспомните, что нужно добавить модификатор triStateToggleable, который обрабатывает свойства специальных возможностей для компонента. Кроме того, активно используйте модификаторы Foundation, поскольку они включают встроенные функции специальных возможностей и существующие практики Compose, описанные в этом разделе.
Пример специального компонента-переключателя можно найти в разделе Очистка и настройка семантики. Более подробная информация о том, как обеспечить поддержку специальных возможностей в специальных компонентах, приведена в руководстве по API.
Рекомендуем
- Примечание. Текст ссылки показывается, когда JavaScript отключен.
- Специальные возможности в Compose
- Тестирование макета Compose