Чтобы помочь пользователям с особыми потребностями, фреймворк Android позволяет создавать специальные возможности, которые могут показывать контент из приложений и управлять ими от имени пользователей.
В Android есть несколько системных сервисов специальных возможностей, в том числе:
- TalkBack помогает людям с нарушениями зрения. Она озвучивает контент синтезированным голосом и выполняет действия в приложении в ответ на жесты пользователя.
- Switch Access помогает людям с двигательными нарушениями. Он выделяет интерактивные элементы и выполняет действия в ответ на нажатие кнопки пользователем. Она позволяет управлять устройством с помощью всего одной или двух кнопок.
Чтобы пользователи с особыми потребностями могли успешно работать с вашим приложением, оно должно соответствовать рекомендациям, описанным на этой странице и в статье Как сделать приложение более доступным.
Каждая из этих рекомендаций, описанных в следующих разделах, может ещё больше улучшить доступность вашего приложения:
- Элементы ярлыков
- Пользователи должны понимать, для чего нужен каждый интерактивный и значимый элемент интерфейса в вашем приложении.
- Как добавить действия специальных возможностей
- Добавив действия специальных возможностей, вы позволите пользователям специальных возможностей выполнять критически важные действия в вашем приложении.
- Как использовать встроенные специальные возможности
- Compose предлагает множество специальных возможностей по умолчанию. Используйте встроенные специальные возможности, чтобы сделать компоненты доступными без дополнительных усилий. Compose также предоставляет способы поддержки более конкретных требований к специальным возможностям, не охватываемых функциями по умолчанию.
- Используйте не только цвет
- Пользователи должны иметь возможность четко различать категории элементов в интерфейсе. Для этого используйте узоры и расположение, а также цвет, чтобы выразить эти различия.
- Как сделать медиаконтент более доступным
- Добавляйте описания к видео- и аудиоконтенту в приложении, чтобы пользователям не приходилось полагаться только на визуальные или звуковые подсказки.
Элементы Label
Важно, чтобы у каждого интерактивного элемента интерфейса в приложении была полезная и понятная метка. Она должна объяснять семантику элемента, то есть его значение и назначение. Программы чтения с экрана, такие как TalkBack, могут озвучивать эти ярлыки для пользователей.
В большинстве случаев API Compose и Material имеют поддержку специальных возможностей по умолчанию. Однако если вам нужно вручную указать семантические свойства элемента пользовательского интерфейса, используйте модификатор semantics и свойство contentDescription. Подробнее о семантике…
В следующих разделах описываются несколько других методов маркировки.
Редактируемые элементы
При добавлении ярлыков к редактируемым элементам, например текстовым полям, полезно показывать текст с примером допустимого ввода в самом элементе, а также делать этот текст доступным для программ чтения с экрана. В таких ситуациях вы можете использовать текст-плейсхолдер, также называемый текстом подсказки.
В примере ниже у TextField есть параметр placeholder, который предоставляет текст подсказки.
val usernameState = rememberTextFieldState() TextField( state = usernameState, lineLimits = TextFieldLineLimits.SingleLine, placeholder = { Text("Enter Username") } )
Также у текстового поля обычно есть соответствующая описательная метка, которая описывает, что пользователи должны ввести в качестве входных данных.
В примере ниже у элемента TextField есть параметр label, который предоставляет описание специальных возможностей.
TextField( state = rememberTextFieldState(initialText = "Hello"), label = { Text("Label") } )
Подробнее о текстовых полях и вводе данных…
элементов в подборке
При добавлении ярлыков к элементам коллекции каждый ярлык должен быть уникальным. Это позволит специальным возможностям системы ссылаться на один элемент на экране при озвучивании метки. Это соответствие позволяет пользователям знать, когда они перемещаются по интерфейсу или когда они перемещают фокус на элемент, который они уже обнаружили.
Например, если у вас есть LazyColumn или LazyRow, используйте модификатор semantics, чтобы назначить уникальный 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) } ) } }
Подробнее о семантических свойствах списков и сеток…
Группы связанного контента
Если в приложении есть несколько элементов интерфейса, которые образуют естественную группу, например сведения о песне или атрибуты сообщения, разместите эти элементы в родительском контейнере (например, Column, Row или Box). Используйте модификатор semantics родительского контейнера, чтобы задать для mergeDescendants значение true.
В этом случае сервисы специальных возможностей смогут озвучить описания контента внутренних элементов одно за другим в одном сообщении. Объединение связанных элементов помогает пользователям технологии специальных возможностей эффективнее находить информацию на экране.
В следующем фрагменте кода composable-функция Row выступает в качестве родительского контейнера.
Внутри Row находятся связанные элементы, которые показывают метаданные для записи в блоге: аватар автора, имя автора и примерное время чтения.
Если задать для атрибута mergeDescendants значение true, то внутренние элементы будут сгруппированы и сервисы специальных возможностей смогут обрабатывать их как единое целое.
@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") } } }
При группировке связанных элементов, как в предыдущем примере, сделайте интерактивным только родительский контейнер. Не добавляйте модификаторы clickable или focusable к внутренним дочерним элементам. Вместо этого примените модификаторы к родительскому объекту Row или Column.
Поскольку сервисы специальных возможностей озвучивают описания внутренних элементов за один раз, важно, чтобы каждое описание было как можно короче, но при этом передавало смысл элемента.
Примечание. Как правило, при создании описания контента для группы не следует объединять текст ее дочерних элементов. В противном случае описание группы станет хрупким, и при изменении текста дочернего элемента описание группы может перестать соответствовать видимому тексту.
В списке или сетке программа чтения с экрана может объединять текст дочерних текстовых узлов элемента списка или сетки. Рекомендуем не изменять это объявление.
Подробнее об объединении семантики…
Заголовки в тексте
В некоторых приложениях используются заголовки, которые обобщают группы текстов на экране. Если определенный элемент представляет собой заголовок, вы можете указать его назначение для сервисов специальных возможностей, задав свойство heading в модификаторе semantics.
@Composable private fun Subsection(text: String) { Text( text = text, style = MaterialTheme.typography.headlineSmall, modifier = Modifier.semantics { heading() } ) }
Пользователи специальных возможностей могут перемещаться между заголовками, а не между абзацами или словами. Такая гибкость улучшает навигацию по тексту.
Подробнее headingо свойстве семантики …
Заголовки панелей специальных возможностей
В Android 9 (уровень API 28) и более поздних версиях можно добавлять специальные названия для панелей экрана. Для специальных возможностей панель – это визуально различимая часть окна.
Чтобы специальные возможности могли распознавать поведение панели, похожее на поведение окна, дайте панелям приложения описательные названия. Это позволяет сервисам специальных возможностей предоставлять пользователям более подробную информацию об изменениях в панели.
ShareSheet( message = "Choose how to share this photo", modifier = Modifier .fillMaxWidth() .align(Alignment.TopCenter) .semantics { paneTitle = "New bottom sheet" } )
Подробнее о свойстве семантики paneTitle рассказывается в разделе Компоненты, похожие на окна.
Декоративные элементы
Если элемент интерфейса предназначен только для визуального оформления, задайте для него соответствующие свойства, чтобы сервисы специальных возможностей могли его игнорировать.
Для composable-функций Image или Icon задайте contentDescription = null. Для других чисто декоративных элементов, которые не предоставляют контекст или функциональность, можно использовать hideFromAccessibility. Это семантическое свойство сообщает сервисам специальных возможностей, что элемент нужно игнорировать.
Если интерактивная composable-функция содержит декоративные неинтерактивные дочерние элементы, используйте clearAndSetSemantics, чтобы сервисы специальных возможностей не обходили их. Обратите внимание, что clearAndSetSemantics полностью стирает семантику по умолчанию для элемента и его дочерних элементов. Это позволяет определить новый, единый
элемент доступности. Обычно этот подход используется для сложных пользовательских компонентов.
В примере ниже Icon и Text – декоративные дочерние элементы внутри пользовательского переключателя. Чтобы сервисы специальных возможностей не обходили эти дочерние элементы по отдельности, вы можете очистить их семантику, используя clearAndSetSemantics в родительском элементе Row. Это указывает специальным возможностям, что весь элемент Row нужно обрабатывать как переключатель, по которому можно перемещаться.
// Developer might intend this to be a toggleable. // Using `clearAndSetSemantics`, on the Row, a clickable modifier is applied, // a custom description is set, and a Role is applied. @Composable fun FavoriteToggle() { val checked = remember { mutableStateOf(true) } Row( modifier = Modifier .toggleable( value = checked.value, onValueChange = { checked.value = it } ) .clearAndSetSemantics { stateDescription = if (checked.value) "Favorited" else "Not favorited" toggleableState = ToggleableState(checked.value) role = Role.Switch }, ) { Icon( imageVector = Icons.Default.Favorite, contentDescription = null // not needed here ) Text("Favorite?") } }
Подробнее о том, как очистить и задать семантику…
Добавьте действия специальных возможностей
Важно, чтобы пользователи сервисов специальных возможностей могли выполнять все пути пользователя в вашем приложении.
Если взаимодействие с пользовательской composable-функцией неочевидным образом меняет состояние приложения, добавьте описательные метки для стандартных нажатий, используя такие параметры, как onClickLabel или onLongClickLabel в Modifier.clickable или Modifier.combinedClickable.
Для сложных взаимодействий, которые нельзя сопоставить со стандартными нажатиями, используйте customActions.
Например, если в вашем приложении можно перетащить элемент в другое место или провести по элементу в списке, вы можете предоставить альтернативный способ выполнения этих путей пользователя, сделав действие доступным для сервисов специальных возможностей. Это позволяет пользователям TalkBack, Voice Access и Switch Access выполнять действия, которые в противном случае были бы доступны только с помощью жестов.
В Compose вы можете определить специальные действия для специальных возможностей с помощью свойства customActions в модификаторе semantics, используя CustomAccessibilityAction.
Например, если в вашем приложении можно убрать элемент, проведя по нему пальцем, вы можете добавить специальное действие для специальных возможностей:
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, сообщает об этом, произнося "Нажмите дважды и удерживайте".
Это общее сообщение не дает пользователю никакого контекста о том, что делает действие нажатия и удержания.
Чтобы сделать это объявление более полезным, укажите понятное описание действия.
В Compose стандартные модификаторы взаимодействия, такие как clickable и combinedClickable, имеют встроенные параметры (onClickLabel и onLongClickLabel), которые можно использовать для описания действий, как в следующем примере:
var contextMenuPhotoId by rememberSaveable { mutableStateOf<Int?>(null) } val haptics = LocalHapticFeedback.current LazyVerticalGrid(columns = GridCells.Adaptive(minSize = 128.dp)) { items(photos, { it.id }) { photo -> ImageItem( photo, Modifier .combinedClickable( onClick = { activePhotoId = photo.id }, onLongClick = { haptics.performHapticFeedback(HapticFeedbackType.LongPress) contextMenuPhotoId = photo.id }, onLongClickLabel = stringResource(R.string.open_context_menu) ) ) } } if (contextMenuPhotoId != null) { PhotoActionsSheet( photo = photos.first { it.id == contextMenuPhotoId }, onDismissSheet = { contextMenuPhotoId = null } ) }
В результате TalkBack озвучит "Открыть контекстное меню", что поможет пользователям понять назначение действия.
Вы также можете указать ярлык непосредственно в модификаторе semantics.
Дополнительную информацию о реагировании на нажатия и клики можно найти в разделах Нажатие и Интерактивные элементы.
Как использовать встроенные специальные возможности
При разработке интерфейса приложения используйте встроенные специальные возможности, чтобы не реализовывать заново уже существующие функции. Material, Compose UI и Foundation API по умолчанию реализуют и предлагают множество практик доступности.
В Jetpack Compose используйте встроенные composable-функции, такие как Button, Switch и Checkbox, чтобы создавать доступные интерфейсы. Эти компоненты поставляются с модификаторами semantics, такими как role и stateDescription, которые можно использовать, чтобы сделать приложения более доступными.
Применение семантики к пользовательским компонентам
При создании специального компонента помните о том, какая поддержка специальных возможностей требуется этому компоненту для выполнения его роли. Часто стандартных API Compose, которые вы уже используете, например clickable, toggleable или selectable, достаточно, поскольку они автоматически заполняют дерево семантики.
Однако для некоторых компонентов требуется более конкретная информация, чем та, которую предоставляют стандартные модификаторы. В таких случаях ищите специализированные модификаторы (например, triStateToggleable) или, если их нет, явно задавайте семантику с помощью низкоуровневого модификатора Modifier.semantics.
Например, у переключателя TriStateSwitch есть три состояния: "Включено", "Выключено" и "Не определено".
Если стандартный модификатор toggleable предполагает два состояния, то модификатор triStateToggleable обрабатывает сложность третьего состояния. Она автоматически задает доступность Role (Switch) и State. В этом случае специальные возможности получают точную информацию, и вам не нужно вручную определять семантику.
В следующем фрагменте кода показано, как реализовать TriStateSwitch с помощью этого подхода:
@Composable fun TriStateSwitch( state: ToggleableState, onClick: () -> Unit, modifier: Modifier = Modifier ) { // A real implementation would include custom drawing for the switch. // This example uses a Box to demonstrate the semantics. Box( modifier = modifier .size(width = 64.dp, height = 40.dp) // triStateToggleable handles the semantics (Role and State) // automatically, so explicit Modifier.semantics is not needed here. .triStateToggleable( state = state, onClick = onClick, role = Role.Switch ) // Add visual feedback based on the state .background( when (state) { ToggleableState.On -> Color.Green ToggleableState.Off -> Color.Gray ToggleableState.Indeterminate -> Color.Yellow } ) ) } // Usage within another composable: var state by remember { mutableStateOf(ToggleableState.Off) } TriStateSwitch( state = state, onClick = { state = when (state) { ToggleableState.Off -> ToggleableState.Indeterminate ToggleableState.Indeterminate -> ToggleableState.On ToggleableState.On -> ToggleableState.Off } } )
При создании специального компонента убедитесь, что вы указали все необходимые семантические свойства для обеспечения специальных возможностей. Например, если компонент имитирует стандартный элемент управления, такой как переключатель или кнопка, эти свойства включают роль компонента (например, Role.Switch или Role.Button), stateDescription (например, "Вкл.", "Выкл.", "Выбрано" или "Не выбрано") и любые соответствующие ярлыки действий. Подробнее о специальных компонентах…
Используйте не только цвет
Чтобы помочь пользователям с нарушениями цветового зрения, используйте не только цвет, но и другие способы выделения элементов интерфейса. Например, можно использовать разные формы или размеры, добавлять текст или визуальные узоры, а также звуковые или тактильные сигналы, чтобы подчеркнуть различия между элементами.
На рисунке 1 показаны две версии действия. В одной версии для различения двух возможных действий в рабочем процессе используется только цвет. В другой версии используются фигуры и текст в дополнение к цвету, чтобы подчеркнуть различия между двумя вариантами.
Как сделать медиаконтент более доступным
Если вы разрабатываете приложение с медиаконтентом, например видеоклипами или аудиозаписями, постарайтесь сделать его доступным для пользователей с разными потребностями. В частности, попробуйте сделать следующее:
- Добавьте элементы управления, позволяющие пользователям приостанавливать или останавливать воспроизведение, изменять громкость и включать субтитры.
- Если в видео представлена информация, необходимая для выполнения рабочего процесса, предоставьте тот же контент в альтернативном формате, например в виде расшифровки.
Дополнительные ресурсы
Дополнительную информацию о том, как сделать приложение более доступным, можно найти в следующих ресурсах: