Чтобы помочь пользователям с особыми потребностями, платформа Android позволяет создавать сервисы специальных возможностей, которые могут показывать контент из приложений и управлять ими от имени пользователя.
В Android есть несколько системных сервисов специальных возможностей, в том числе:
- TalkBack помогает людям со слабым зрением или слепым. Он озвучивает контент синтезированным голосом и выполняет действия в приложении в ответ на жесты пользователя.
- Switch Access помогает людям с двигательными нарушениями. Он выделяет интерактивные элементы и выполняет действия в ответ на нажатие кнопки. Она позволяет управлять устройством с помощью одной или двух кнопок.
Чтобы людям с особыми потребностями было удобно пользоваться вашим приложением, следуйте рекомендациям, приведенным на этой странице. Они дополняют основные правила создания доступных приложений.
Каждый из этих методов, описанных в следующих разделах, может повысить доступность вашего приложения:
- Элементы ярлыков
- Пользователи должны понимать контент и назначение каждого интерактивного и значимого элемента интерфейса в вашем приложении.
- Как добавить действия для специальных возможностей
- Добавив действия для специальных возможностей, вы позволите пользователям сервисов специальных возможностей выполнять важные действия в вашем приложении.
- Как использовать встроенные специальные возможности
- Compose по умолчанию поддерживает множество специальных возможностей. Используйте готовые решения для обеспечения доступности, чтобы сделать компоненты доступными без дополнительных усилий. Compose также позволяет реализовать специальные требования к доступности, которые не поддерживаются по умолчанию.
- Используйте подсказки, не связанные с цветом
- Пользователи должны иметь возможность четко различать категории элементов в интерфейсе. Для этого используйте узоры и положение, а также цвет.
- Как сделать медиаконтент более доступным
- Добавьте описания к видео- и аудиоконтенту в приложении, чтобы пользователи, которые его смотрят или слушают, не полагались только на визуальные или звуковые подсказки.
Элементы ярлыков
Важно, чтобы у каждого интерактивного элемента интерфейса в приложении были полезные и информативные ярлыки. Каждый ярлык должен объяснять семантику элемента, то есть его значение и назначение. Программы чтения с экрана, например 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 рассказывается в разделе Компоненты, похожие на окна.
Декоративные элементы
Если элемент интерфейса предназначен только для визуального оформления, задайте для него свойства, которые позволят сервисам специальных возможностей игнорировать его.
Для композиций 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.
Подробнее о том, как реагировать на нажатия и клики, рассказывается в статьях Нажатия и удерживания и Интерактивные элементы.
Как использовать встроенные специальные возможности
При разработке интерфейса приложения используйте встроенные специальные возможности, чтобы не реализовывать функции, которые уже существуют. API Material, Compose UI и Foundation реализуют и предлагают множество доступных функций по умолчанию.
В Jetpack Compose используйте встроенные функции, такие как 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 показаны две версии действия. В одной версии для различения двух возможных действий в рабочем процессе используется только цвет. В другом варианте, созданном с учетом рекомендаций, помимо цвета используются фигуры и текст, чтобы подчеркнуть различия между двумя вариантами:
Как сделать медиаконтент более доступным
Если вы разрабатываете приложение, в котором есть медиаконтент, например видеоклип или аудиозапись, постарайтесь сделать так, чтобы пользователи с разными потребностями в отношении доступности могли понимать этот контент. В частности, попробуйте сделать следующее:
- Добавьте элементы управления, которые позволяют пользователям приостанавливать или останавливать воспроизведение медиаконтента, изменять громкость и включать или отключать субтитры.
- Если в видео представлена информация, необходимая для выполнения рабочего процесса, предоставьте ее в альтернативном формате, например в виде расшифровки.
Дополнительные ресурсы
Чтобы узнать больше о том, как сделать приложение более доступным, ознакомьтесь со следующими ресурсами: