「モバイルアプリアクセシビリティ入門」に記載されている Android の API は View のもので、Jetpack Compose だとどのようになるのか置き換え先の紹介になります。
あくまで API の紹介だけなので、具体的な使い方は本を読んでください。
6.3/タップ領域を確保する
Modifier#minimumInteractiveComponentSize
が用意されており、これを適用するとシステム上で必要なタッチターゲットサイズ分のレイアウトサイズが確保されます。
デフォルトが 48dp で設定されています。
Text(
text = "Button",
modifier = Modifier
.clickable { ... }
.minimumInteractiveComponentSize()
)
システム上でサイズが設定される minimumInteractiveComponentSize
ですが、Android XR では 56dp が必要なタッチターゲットサイズになるので、この API を使うと XR 上では変わる可能性がありえそうです。(Jetpack Compose for XR 1.0.0-alpha01 ではそのような変更は入っていない)
6.4/contentDescription
Icon
や Image
のような画像を表示する Composable fun には必須の引数になっています。
読み上げが必要ない場合は null を渡すようにしましょう。
Image(
painter = painterResource(R.drawable.sample),
contentDesctiprion = "サンプル画像"
)
Icon(
painter = painterResource(R.drawable.sample),
contentDesctiprion = null
)
Icon
や Image
以外の場合は Modifier#semantics
から contentDescription
を設定します。
Text(
text = "下の図をご覧ください",
modifier = Modifier.semantics {
contentDescription = "次の画像をご覧ください"
}
)
Modifier#semantics
による contentDescription
の設定は Composable fun が本来持っていた読み上げ内容を上書きしてしまうため注意が必要です。
Icon
、Image
、Canvas
など、記述することが本質的に困難な場合に適用しましょう。
6.4/importantForAccessibility
Icon
、Image
の場合は contentDescription
の引数に null を渡すと読み上げさせないことができます。空文字の場合は空文字が読み上げられるので注意が必要です。
またコンポーネントに対してフォーカスを移動させずに読み上げさせない場合は Modifier#semantics
の invisibleToUser
を設定するか Modifier#clearAndSetSemantic
を設定します。
Icon(
painter = painterResource(R.drawable.sample),
contentDesctiprion = null
)
Text(
text = "Disabled",
modifier = Modifier.semantics {
invisibleToUser()
}
)
Button(
modifier = Modifier.clearAndSetSemantics {
}
) {
...
}
Modifier#semantics
の invisibleToUser
は Compose 1.8.0 以降 deprecated になることが予定されており、hideFromAccessibility
に置き換わります。
名前の変更のみで挙動の差はないです。
6.4/labelFor
compose-material や compose-material3 標準の TextField
/OutlinedTextField
であれば特別な設定なしに labelFor
相当の動きがされます。
相当の動きでしかなく labelFor
のようにラベルとプレースホルダーを繋いで読み上げてはくれません。
また、標準の TextField
/OutlinedTextField
を使わない場合 Compose での API は現時点(1.7.7)でないので実装で labelFor
相当の読み上げの実装が必要になります。
var text by remember { mutableStateOf("") }
TextField(
modifier = Modifier.fillMaxWidth(),
value = text,
label = { Text("名前") },
placeholder = { Text("入力してください") },
onValueChange = {
text = it
}
)
6.4/screenReaderFocusable
Modifier.semantics(mergeDescendants = true)
で子をまとめて一括に読み上げさせることができます。
Column(
modifier = Modifier.semantics(mergeDescendants = true) {}
) {
Text("1行目")
Text("2行目")
}
6.4/accessibilityHeading
Modifier#semantics
で heading()
を設定します。
Column(
modifier = Modifier.semantics {
heading()
}
) {
Text("Header")
}
6.4/accessibilityPaneTitle
Modifier#semantics
で paneTitle
を設定します。
Dialog(
onDismissRequest,
) {
Column(
Modifier.semantics {
paneTitle = "この枠内には重要な情報が含まれています"
}
) {
...
}
}
6.4/accessibilityTraversalBeforeとaccessibilityTraversalAfter
Compose では accessibilityTraversalBefore と accessibilityTraversalAfter の代わりになる API が View よりも柔軟に対応できるようになっています。
Modifier#semantics
から isTraversalGroup
を設定することで要素のグルーピングをすることが可能になります。
Row {
Column(Modifier.semantics { isTraversalGroup = true }) {
Text("これは")
Text("左のテキストです")
}
Column(Modifier.semantics { isTraversalGroup = true }) {
Text("これは")
Text("右のテキストです")
}
}
isTraversalGroup
と組み合わせて使用する traversalIndex
で読み上げの順序を細かくカスタマイズすることができます。
Scaffold(
topBar = { TopAppBar(title = { ... }) },
floatingActionButton = {
Box(
modifier = Modifier.semantics {
isTraversalGroup = true
traversalIndex = -1f
}
) {
FloatingActionButton(onClick = {}) {
Icon(imageVector = Icons.Default.Add, contentDescription = "Add")
}
}
},
) {
...
}
6.4/accessibilityLiveRegion
Modifier#semantics
で accessibilityLiveRegion
同様に liveRegion
を設定します。
Text(
modifier = Modifier.semantics {
liveRegion = LiveRegionMode.Polite // or LiveRegionMode.Assertive
}
)
その他の本では紹介されていない Compose におけるアクセシビリティ対応の具体的な実装例は公式ドキュメントを参照することをお勧めします。