本記事は Android Advent Calendar 2020 の 6 日目の記事です。
Jetpack Compose 以前はアクセシビリティ対応の一環として、ImageView
に contentDescription
を設定したり View のグルーピングで TalkBack の対応をしていました。
大きな一つの View の中でレンダリングされる Jetpack Compose ではどのように TalkBack の対応をするのかについて書いていきます。
※ Jetpack Compose 1.0.0-alpha08 時点の記事で、今後大きな変更もあり得ます
Semantics
Jetpack Compose でアクセシビリティに対応するときは Semantics フレームワークを使用します。
これまでであれば読み上げツールが View のコンポーネントを識別していましたが、View のコンポーネントを操作できない Compose では Semantics を使用して構造化された UI に対して意味を与える必要があります。
また Semantics はアクセシビリティだけでなく、Jetpack Compose で UI テストをするときにも使用されます。
ref : https://developer.android.com/jetpack/compose/testing#semantics
accessibilityLabel
accessibilityLabel
では読み上げる文言の設定ができます。
IconButton(
onClick = {},
modifier = Modifier.semantics {
accessibilityLabel = "戻るボタン"
}
) {
Icon(imageVector = Icons.Default.ArrowBack)
}
これは ← のアイコンがついたボタンに Modifier#semantics
でアクセシビリティに関することを設定します。
accessibilityLabel
でボタンに対して読み上げて欲しい文言を設定します。
これまでの contentDescription
に近いものがこれになります。
mergeDescendants
mergeDescendants
は複数の要素をグループ化します。
Column(
modifier = Modifier.semantics(mergeDescendants = true) {
}
) {
Text(
text = "タイトル",
style = MaterialTheme.typography.subtitle1
)
Providers(AmbientContentAlpha provides ContentAlpha.medium) {
Text(
text = "サブタイトル",
style = MaterialTheme.typography.caption
)
}
}
mergeDescendants
を true にすると要素がまとめて読み上げられます。
上記の例では 「タイトル サブタイトル」 と 2 つのテキストがまとめて読み上げられます。
accessibilityValue
accessibilityValue
は要素の状態を設定することができます。
var checked by remember { mutableStateOf(false) }
Row(
modifier = Modifier.semantics(mergeDescendants = true) {
accessibilityValue = if (checked) {
"チェック済み"
} else {
"未チェック"
}
}
) {
Checkbox(checked = checked, onCheckedChange = {
checked = it
})
Text("アイテム")
}
チェックボックスとテキストをグループ化し、チェックボックスの状態を accessibilityValue
に設定しています。
上記の例では 「チェック済み アイテム」 と読み上げられます。
clickable
Compose でタップイベントを実装する Modifier#clickable
も内部の実装では semantics
で読み上げに対応しています。
Box(
modifier = Modifier.fillMaxWidth()
.clickable(
onClick = {
...
},
onClickLabel = "詳細を開く"
)
) {
...
}
Modifier#clickable
には onClickLabel
と onLongClickLabel
を設定でき、それぞれにタップ時に読み上げる文言を設定することができます。
val semanticModifier = Modifier.semantics(mergeDescendants = true) {
if (enabled) {
onClick(action = { onClick(); true }, label = onClickLabel)
if (onLongClick != null) {
onLongClick(action = { onLongClick(); true }, label = onLongClickLabel)
}
} else {
disabled()
}
}
Modifier#clickable
の内部ではこのような実装になっており、 semantics
が設定されています。
customActions
customActions
はフォーカスされている項目に対してアクションを設定することができます。
Box(
modifier = Modifier.semantics(mergeDescendants = true) {
customActions = listOf(
CustomAccessibilityAction("アクション") {
// return a boolean result indicating whether the action is successfully handled
true
}
)
}
) {
...
}
TalkBack では選択された項目に対してジェスチャーでコンテキストメニューを呼び出して操作する場合もあります。
CustomAccessibilityAction
でアクションを定義することで、コンテキストメニューでの項目を追加することができます。
Jetpack Compose における基本的な TalkBack の対応について書いてみました。
Jetpack Compose はまだ alpha とはいえアクセシビリティに対応する仕組みが用意されているので、置き換えるにしても新規で作るにしても意識しておきましょう。
Compose のサンプルプロジェクトでも要所要所に semantics
の実装がされているので、そちらをみるのも参考になると思います。