MoTLab -GO Inc. Engineering Blog-MoTLab -GO Inc. Engineering Blog-

TalkBack対応とJetpack Composeにおけるアクセシビリティ

Android
November 29, 2023

タクシーアプリ『GO』のAndroidアプリを開発している石橋です。

Androidアプリのアクセシビリティ向上について興味を持ち、技術的にどのような対応ができるかを調べて検証してみました。

本記事ではAndroidのスクリーンリーダー機能である「TalkBack」の概要と、アクセシビリティ向上のためにJetpack Composeで設定できる項目について紹介します。


はじめに

昨今、誰にとっても使いやすいモバイルアプリを開発するために、視覚、色覚、または聴覚に障がいのある方、細かい作業に支障のある方、認知障がいのある方などにとっても便利にアプリを使えるようにする対応が求められています。Androidの公式ドキュメントの中でも、アクセシビリティ対応についてのガイドラインが提供されています。

今回はAndroidのスクリーンリーダー機能である「TalkBack」へ対応するために、取り組むことが望ましい内容についてお話しします。

TalkBackとは

TalkBackとは、視覚障がいを持つユーザー向けに画面上の内容を読み上げたりアプリの操作を支援するユーザー補助機能です。

TalkBackをオンにすることで、画面を見なくてもスマホの操作が可能になります。ダブルタップで画面上のコンポーネントを選択したり、スワイプすることでフォーカスを順に移動することができ、フォーカスされたコンポーネントがシステムによって読み上げられます。

TalkBack対応のためにやるべきこと

TalkBack対応のためにやるべきこと、配慮するべきことはいろいろありますが、主に以下の内容が挙げられます。

  • コンテンツラベルを適切に設定する
  • Viewのグループ化
  • 読み上げてほしくないViewをスキップする
  • 読み上げ順序の設定
  • クリックラベルの設定
  • 要素の状態の説明を設定

Jetpack Composeでアクセシビリティに対応するときは、Semanticsを使用します。Semanticsはアクセシビリティだけでなく、Jetpack ComposeでUIテストをするときにも使用されるため、適切にSemanticsの項目を設定することでUIテストもしやすくなります。

コンテンツラベルを適切に設定する

TalkBackを利用している場合は、要素のコンテンツラベルに設定されている文言が読み上げられます。そのため、コンテンツラベルが適切に設定されていないと、ユーザーが目的の情報を理解したり、アプリの操作をすることが難しくなる可能性があります。

例えば、コンテンツラベルに空文字を設定しているとTalkBackでは「ラベルなし、ボタン」のように読み上げられてしまいます。

コンテンツラベルを設定するべきであるコンポーネントとしては、テキスト情報がないアイコンや画像などが挙げられます。その一方で、装飾目的の画像には特に情報が必要ないことと、TextViewなどのテキストが設定されているコンポーネントについては表示されるテキストがそのまま読み上げられるため、コンテンツラベルの設定は必須ではありません。

コンテンツラベルの実装方法としては、Android Viewの場合はandroid:contentDescription に読み上げてほしい文言を設定します。

Jetpack Composeでは、ComposableのプロパティもしくはModifier.semanticsを使うことでcontentDescriptionを設定することができます。

IconButton(onClick = onClick) {
    Icon(
        imageVector = Icons.Filled.ArrowBack,
        contentDescription = "前の画面に戻るボタン",
    )
}

Viewのグループ化

TalkBackで複数のコンポーネントをまとめて読み上げてほしい場合は、結合したい親のModifireに対してModifier.semantics(mergeDescendants = true) を設定します。

例えば「乗車まで」と「3-6分」というTextコンポーザブルがそれぞれあるとしたら、TalkBackでは「乗車まで3-6分」と読み上げられるのが望ましいので親のコンポーザブルにModifier.semantics(mergeDescendants = true) を設定します。

Column(
    Modifier.semantics(mergeDescendants = true) {},
) {
    Text("乗車まで")
    Text("3-6分")
}

読み上げてほしくないViewをスキップする

TalkBackをオンにすると、装飾目的の画像など実際に読み上げてほしくないコンポーネントにフォーカスが当たる場合があります。

特定のコンポーネントをTalkBackでは読み上げてほしくない場合、Android Viewの場合はandroid:importantForAccessibilitynoを設定します。

Jetpack Composeの場合はcontentDescriptionnullを設定すると、TalkBackで読み上げられなくなります。contentDescriptionに空文字を設定した場合は、TalkBackでの読み上げ対象となり「ラベル無し」と読み上げられてしまうのでご注意ください。

読み上げ順序の設定

TalkBackではデフォルトでは、左から右、次に上から下の順にコンポーネントの内容が読み上げられます。

読み上げ順序を手動で設定したい場合は親のコンポーザブルにisTraversalGroup = true を設定します。そして、読み上げてほしい順に、コンポーザブルにtraversalIndex を割り振っていきます。

Column(
    modifier = Modifier.semantics { isTraversalGroup = true },
) {
    Text(
        text = "1番目の要素",
        modifier = Modifier.semantics { traversalIndex = 0f },
    )
    Text(
        text = "3番目の要素",
        modifier = Modifier.semantics { traversalIndex = 2f },
    )
    Text(
        text = "2番目の要素",
        modifier = Modifier.semantics { traversalIndex = 1f },
    )
}

traversalIndexのデフォルト値は0fで、正負両方の値を取れるため、画面内で一番最優先で読み上げてほしいコンポーネントがあればtraversalIndex-1fを設定します。

クリックラベルの設定

ボタンやリストのアイテムなどの、クリックできるコンポーネントに対してクリックラベルを設定すると、ユーザーがコンポーザブルを操作したときの動作を読み上げることができます。

例えば記事の一覧のリストがあって、記事にフォーカスが当たった際に「記事を開く」という次のアクションをユーザーに説明することができます。

Jetpack Composeでは、ComposableのModifier.clickableを使うことでクリックラベルを設定することができます。

@Composable
fun ArticleListItem(openArticle: () -> Unit) {
    Row(
        Modifier.clickable(
            onClickLabel = "記事を開く",
            onClick = openArticle,
        ),
    ) {
        // ..
    }
}

また、Modifier.clickableを使用することができない場合は、Modifier.semanticsでクリックラベルを設定できます。

@Composable
fun LowLevelClickLabel(openArticle: () -> Boolean) {
    Canvas(
        Modifier.semantics {
            onClick(label = "記事を開く", action = openArticle)
        },
    ) {
        // ..
    }
}

要素の状態の説明を設定

Switchのオン・オフや、スライダーで現在どの値が選択されているかなどをTalkBackで読み上げたい場合は、コンポーネントに要素の状態の説明を設定する必要があります。

Jetpack Composeでは、Modifier.semanticsstateDescriptionで要素の状態の説明を設定することができます。

オン・オフという状態だけではなく、スライダーなどで現在セットされている値なども読み上げるように設定しておくとアクセシビリティ向上に繋がりそうです。

var isChecked by remember { mutableStateOf(false) }

Checkbox(
    checked = isChecked,
	  onCheckedChange = { isChecked = it },
	  modifier = Modifier.semantics(mergeDescendants = true) {
		    stateDescription = if (isChecked) "Selected" else "Not Selected"
	  },
) 

アクセシビリティ対応の簡易テスト

An image from Notion

TalkBack用の対応を含め、アクセシビリティ対応ができているかを簡易的にテストするためには、ユーザー補助検証ツールを利用します。アプリの任意の画面のスクリーンショットを撮ることで、問題点や改善点を示してくれます。

具体的には以下の観点などで提案がされます。

  • コンテンツラベルが設定されているか
  • タップターゲットのサイズが十分か
  • テキストや画像のコントラスト比が適切か

また、実際にTalkBackをオンにしてみて開発中のアプリを使ってみるというのも、アクセシビリティ対応ができているかの有効な確認方法になります。

おわりに

今回はAndroidアプリでTalkBack対応する際にやるべきことをいくつか挙げてみました。

『GO』のAndroidアプリでは、まだまだアクセシビリティ対応ができているとは言えないので、今後TalkBackなどのスクリーンリーダー機能でもアプリが便利に使えるように、実際にアクセシビリティ向上に取り組んでいきたいです。


We're Hiring!

📢
GO株式会社ではともに働くエンジニアを募集しています。

興味のある方は 採用ページ も見ていただけると嬉しいです。

Twitter @goinc_techtalk のフォローもよろしくお願いします!