DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Semantics完全ガイド — contentDescription/Role/カスタムアクション/テスト

この記事で学べること

Semantics(contentDescription、Role、カスタムアクション、TalkBack対応、テスト用Semantics)を解説します。


基本Semantics

@Composable
fun SemanticsExample() {
    Column(Modifier.padding(16.dp)) {
        // contentDescription
        IconButton(
            onClick = {},
            modifier = Modifier.semantics { contentDescription = "お気に入りに追加" }
        ) {
            Icon(Icons.Default.Favorite, contentDescription = null)
        }

        // stateDescription
        var isOn by remember { mutableStateOf(false) }
        Switch(
            checked = isOn,
            onCheckedChange = { isOn = it },
            modifier = Modifier.semantics {
                stateDescription = if (isOn) "有効" else "無効"
            }
        )

        // clearAndSetSemantics
        Row(
            Modifier.clearAndSetSemantics {
                contentDescription = "評価: 4.5点 (5点中)"
            }
        ) {
            repeat(5) { index ->
                Icon(
                    if (index < 4) Icons.Default.Star else Icons.Default.StarBorder,
                    contentDescription = null,
                    tint = Color(0xFFFFD700)
                )
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

カスタムアクション

@Composable
fun CustomActionItem(item: Item, onDelete: () -> Unit, onArchive: () -> Unit) {
    ListItem(
        headlineContent = { Text(item.title) },
        modifier = Modifier.semantics {
            customActions = listOf(
                CustomAccessibilityAction("削除") { onDelete(); true },
                CustomAccessibilityAction("アーカイブ") { onArchive(); true }
            )
        }
    )
}

// heading/Role
@Composable
fun SectionHeader(title: String) {
    Text(
        text = title,
        style = MaterialTheme.typography.headlineSmall,
        modifier = Modifier
            .padding(16.dp)
            .semantics { heading() }
    )
}

@Composable
fun ClickableCard(onClick: () -> Unit) {
    Card(
        modifier = Modifier
            .clickable(onClick = onClick)
            .semantics { role = Role.Button }
    ) {
        Text("タップしてください", Modifier.padding(16.dp))
    }
}
Enter fullscreen mode Exit fullscreen mode

テスト用Semantics

@Composable
fun TestableScreen() {
    Column {
        Text(
            "タイトル",
            modifier = Modifier.testTag("screen_title")
        )
        LazyColumn(Modifier.testTag("item_list")) {
            items(10) { index ->
                ListItem(
                    headlineContent = { Text("Item $index") },
                    modifier = Modifier.testTag("item_$index")
                )
            }
        }
    }
}

// テスト
class ScreenTest {
    @get:Rule val rule = createComposeRule()

    @Test
    fun titleIsDisplayed() {
        rule.setContent { TestableScreen() }
        rule.onNodeWithTag("screen_title").assertIsDisplayed()
        rule.onNodeWithTag("item_list").assertExists()
        rule.onNodeWithTag("item_0").assertIsDisplayed()
    }
}
Enter fullscreen mode Exit fullscreen mode

まとめ

API 用途
contentDescription スクリーンリーダー用説明
clearAndSetSemantics 子要素のSemantics統合
customActions カスタムアクション追加
testTag UIテスト用識別子
  • contentDescriptionでTalkBack読み上げテキスト設定
  • clearAndSetSemanticsで複合要素をひとまとめに
  • customActionsでスワイプメニューにアクション追加
  • testTagでUIテストから要素を特定

8種類のAndroidアプリテンプレート(アクセシビリティ対応)を公開しています。

テンプレート一覧Gumroad

関連記事:


I publish 8 Android app templates on Gumroad.

Browse templatesGumroad

Top comments (0)