อรรถศาสตร์

นอกจากข้อมูลหลักที่คอมโพสิเบิลมี เช่น สตริงข้อความของคอมโพสิเบิล Text แล้ว การมีข้อมูลเพิ่มเติมเกี่ยวกับองค์ประกอบ UI ก็อาจเป็นประโยชน์

ข้อมูลเกี่ยวกับความหมายและบทบาทของคอมโพเนนต์ใน Compose เรียกว่าความหมาย ซึ่งเป็นวิธีระบุบริบทเพิ่มเติมเกี่ยวกับ Composable ให้กับบริการต่างๆ เช่น การช่วยเหลือพิเศษ การป้อนข้อความอัตโนมัติ และการทดสอบ เช่น ไอคอนกล้องอาจเป็นเพียงรูปภาพ แต่ความหมายเชิงอรรถศาสตร์อาจเป็น "ถ่ายภาพ"

การรวมความหมายที่เหมาะสมเข้ากับ Compose API ที่เหมาะสมจะช่วยให้คุณระบุข้อมูลเกี่ยวกับคอมโพเนนต์ให้บริการการช่วยเหลือพิเศษมากที่สุดได้ ซึ่งบริการดังกล่าวจะเป็นผู้ตัดสินใจว่าจะแสดงข้อมูลดังกล่าวต่อผู้ใช้อย่างไร

Material และ Compose UI และ Foundation API มาพร้อมกับความหมายในตัวซึ่งเป็นไปตามบทบาทและฟังก์ชันที่เฉพาะเจาะจง แต่คุณยังแก้ไขความหมายเหล่านี้สำหรับ API ที่มีอยู่หรือตั้งค่าใหม่สำหรับคอมโพเนนต์ที่กำหนดเองได้ตามข้อกำหนดเฉพาะ

พร็อพเพอร์ตี้เชิงความหมาย

พร็อพเพอร์ตี้เชิงความหมายจะสื่อความหมายของคอมโพสิเบิลที่เกี่ยวข้อง เช่น คอมโพสิเบิล Text มีพร็อพเพอร์ตี้เชิงความหมาย text เนื่องจากเป็นความหมายของคอมโพสิเบิลนั้น Icon มีพร็อพเพอร์ตี้ contentDescription (หากนักพัฒนาแอปตั้งค่าไว้) ซึ่งจะแสดงความหมายของไอคอนเป็นข้อความ

พิจารณาว่าพร็อพเพอร์ตี้เชิงความหมายสื่อความหมายของคอมโพสิเบิลอย่างไร ลองใช้ Switch ลักษณะที่ผู้ใช้เห็นมีดังนี้

รูปที่ 1 Switch ในสถานะ "เปิด" และ "ปิด"

หากต้องการอธิบายความหมายขององค์ประกอบนี้ คุณอาจพูดว่า"นี่คือสวิตช์ ซึ่งเป็นองค์ประกอบที่สลับได้ในสถานะ "เปิด" คุณสามารถคลิกเพื่อโต้ตอบกับสิ่งนั้น"

พร็อพเพอร์ตี้เชิงอรรถศาสตร์มีไว้เพื่อวัตถุประสงค์นี้โดยเฉพาะ โหนดความหมายขององค์ประกอบ Switch นี้มีพร็อพเพอร์ตี้ต่อไปนี้ตามที่แสดงด้วยเครื่องมือตรวจสอบเลย์เอาต์

เครื่องมือตรวจสอบเลย์เอาต์ที่แสดงพร็อพเพอร์ตี้ Semantics ของ Composable Switch
รูปที่ 2 เครื่องมือตรวจสอบเลย์เอาต์ที่แสดงพร็อพเพอร์ตี้ Semantics ของ SwitchComposable

Role ระบุประเภทขององค์ประกอบ StateDescription อธิบายวิธีอ้างอิงสถานะ "เปิด" โดยค่าเริ่มต้น ข้อความนี้เป็นคำ "เปิด" เวอร์ชันแปลแล้ว แต่สามารถทำให้เจาะจงยิ่งขึ้นได้ (เช่น "เปิดใช้") โดยอิงตามบริบท ToggleableState คือสถานะปัจจุบันของสวิตช์ พร็อพเพอร์ตี้ OnClick จะอ้างอิงวิธีการที่ใช้โต้ตอบกับองค์ประกอบนี้

การติดตามพร็อพเพอร์ตี้เชิงความหมายของคอมโพสิเบิลแต่ละรายการในแอปจะปลดล็อกความเป็นไปได้อันมากมายที่มีประสิทธิภาพ ดังนี้

  • บริการการช่วยเหลือพิเศษใช้พร็อพเพอร์ตี้เพื่อแสดง UI ที่แสดงบนหน้าจอและอนุญาตให้ผู้ใช้โต้ตอบกับ UI ได้ สำหรับคอมโพสิชันสวิตช์ Talkback อาจอ่านว่า "เปิด สวิตช์ แตะสองครั้งที่สวิตช์เพื่อเปิด/ปิด" ผู้ใช้สามารถแตะสองครั้งที่หน้าจอเพื่อปิดสวิตช์
  • เฟรมเวิร์กการทดสอบใช้พร็อพเพอร์ตี้เพื่อค้นหาโหนด โต้ตอบกับโหนด และทำการยืนยัน
    val mySwitch = SemanticsMatcher.expectValue(
        SemanticsProperties.Role, Role.Switch
    )
    composeTestRule.onNode(mySwitch)
        .performClick()
        .assertIsOff()

คอมโพสิเบิลและตัวแก้ไขที่สร้างขึ้นบน Compose ไลบรารีพื้นฐานจะตั้งค่าพร็อพเพอร์ตี้ที่เกี่ยวข้องให้คุณโดยค่าเริ่มต้นอยู่แล้ว คุณเปลี่ยนพร็อพเพอร์ตี้เหล่านี้ด้วยตนเองได้ หากต้องการ เพื่อปรับปรุงการรองรับการช่วยเหลือพิเศษสำหรับกรณีการใช้งานที่เฉพาะเจาะจง หรือเปลี่ยนกลยุทธ์การผสานหรือล้างคอมโพสิเบิล

หากต้องการส่งสัญญาณประเภทเนื้อหาที่เฉพาะเจาะจงของคอมโพเนนต์ไปยังบริการการช่วยเหลือพิเศษ คุณสามารถใช้ความหมายต่างๆ ได้ การเพิ่มเหล่านี้จะรองรับข้อมูลเชิงความหมายหลักที่มีอยู่และช่วยให้บริการการช่วยเหลือพิเศษปรับแต่งวิธีแสดง ประกาศ หรือโต้ตอบกับคอมโพเนนต์ของคุณได้

ดูรายการคุณสมบัติเชิงอรรถศาสตร์ทั้งหมดได้ที่ออบเจ็กต์ SemanticsProperties ดูรายการการดำเนินการสำหรับการช่วยเหลือพิเศษที่เป็นไปได้ทั้งหมดได้ในออบเจ็กต์ SemanticsActions

ส่วนหัว

แอปมักมีหน้าจอที่มีเนื้อหาที่เป็นข้อความจำนวนมาก เช่น บทความหรือหน้าข่าวที่ยาว ซึ่งมักจะแบ่งออกเป็นส่วนย่อยต่างๆ ที่มีส่วนหัว ดังนี้

บล็อกโพสต์ที่มีข้อความบทความในที่เก็บข้อมูลแบบเลื่อนได้
รูปที่ 3 บล็อกโพสต์ที่มีข้อความบทความในที่เก็บข้อมูลแบบเลื่อน

ผู้ใช้ที่มีความต้องการด้านการช่วยเหลือพิเศษอาจพบปัญหาในการไปยังส่วนต่างๆ ของหน้าจอดังกล่าว บริการการช่วยเหลือพิเศษบางรายการช่วยให้ไปยังส่วนต่างๆ หรือส่วนหัวได้โดยตรงเพื่อปรับปรุงประสบการณ์การไปยังส่วนต่างๆ หากต้องการเปิดใช้ฟีเจอร์นี้ ให้ระบุว่าคอมโพเนนต์ของคุณเป็น heading โดยกำหนดพร็อพเพอร์ตี้ความหมาย ดังนี้

@Composable
private fun Subsection(text: String) {
    Text(
        text = text,
        style = MaterialTheme.typography.headlineSmall,
        modifier = Modifier.semantics { heading() }
    )
}

การแจ้งเตือนและป๊อปอัป

หากคอมโพเนนต์เป็นการแจ้งเตือนหรือป๊อปอัป เช่น Snackbar คุณอาจต้องส่งสัญญาณให้บริการการช่วยเหลือพิเศษทราบว่าสามารถส่งโครงสร้างใหม่หรือการอัปเดตเนื้อหาไปยังผู้ใช้ได้

คอมโพเนนต์ที่คล้ายกับข้อความแจ้งสามารถทําเครื่องหมายด้วยพร็อพเพอร์ตี้ความหมาย liveRegion ได้ ซึ่งจะช่วยให้บริการการช่วยเหลือพิเศษแจ้งให้ผู้ใช้ทราบโดยอัตโนมัติเกี่ยวกับการเปลี่ยนแปลงในคอมโพเนนต์นี้หรือคอมโพเนนต์ย่อย

PopupAlert(
    message = "You have a new message",
    modifier = Modifier.semantics {
        liveRegion = LiveRegionMode.Polite
    }
)

คุณควรใช้ liveRegionMode.Polite ในกรณีส่วนใหญ่ที่ควรดึงดูดความสนใจของผู้ใช้ไปยังการแจ้งเตือนหรือเนื้อหาที่สําคัญซึ่งเปลี่ยนแปลงบนหน้าจอเพียงระยะเวลาสั้นๆ

คุณควรใช้ liveRegion.Assertive อย่างจำกัดเพื่อหลีกเลี่ยงการแสดงผลความคิดเห็นที่รบกวน ควรใช้ในกรณีที่จำเป็นต้องแจ้งให้ผู้ใช้ทราบเกี่ยวกับเนื้อหาที่มีเวลาจำกัด

PopupAlert(
    message = "Emergency alert incoming",
    modifier = Modifier.semantics {
        liveRegion = LiveRegionMode.Assertive
    }
)

คุณไม่ควรใช้พื้นที่โฆษณาแบบเรียลไทม์กับเนื้อหาที่อัปเดตบ่อย เช่น นาฬิกานับถอยหลัง เพื่อไม่ให้ผู้ใช้ได้รับความคิดเห็นอย่างต่อเนื่องจนเกินไป

คอมโพเนนต์ที่มีลักษณะคล้ายหน้าต่าง

คอมโพเนนต์ที่กําหนดเองซึ่งมีลักษณะคล้ายกับหน้าต่าง เช่น ModalBottomSheet ต้องมีสัญญาณเพิ่มเติมเพื่อแยกความแตกต่างจากเนื้อหารอบข้าง คุณสามารถใช้เซมาติก paneTitle เพื่อแสดงการเปลี่ยนแปลงหน้าต่างหรือแผงที่เกี่ยวข้องอย่างเหมาะสมโดยบริการการช่วยเหลือพิเศษ พร้อมกับข้อมูลเซมาติกหลัก ดังนี้

ShareSheet(
    message = "Choose how to share this photo",
    modifier = Modifier
        .fillMaxWidth()
        .align(Alignment.TopCenter)
        .semantics { paneTitle = "New bottom sheet" }
)

โปรดดูข้อมูลอ้างอิงเกี่ยวกับวิธีที่ Material 3 ใช้ paneTitle สำหรับคอมโพเนนต์

คอมโพเนนต์ข้อผิดพลาด

สำหรับเนื้อหาประเภทอื่นๆ เช่น คอมโพเนนต์ที่คล้ายกับข้อผิดพลาด คุณอาจต้องขยายความเกี่ยวกับข้อมูลเชิงความหมายหลักสำหรับผู้ใช้ที่มีความต้องการด้านการช่วยเหลือพิเศษ เมื่อกำหนดสถานะข้อผิดพลาด คุณสามารถแจ้งบริการการช่วยเหลือพิเศษเกี่ยวกับความหมายของ error และระบุข้อความแสดงข้อผิดพลาดแบบขยาย

ในตัวอย่างนี้ TalkBack จะอ่านข้อมูลข้อความแสดงข้อผิดพลาดหลัก ตามด้วยข้อความเพิ่มเติมที่ขยายความ

Error(
    errorText = "Fields cannot be empty",
    modifier = Modifier
        .semantics {
            error("Please add both email and password")
        }
)

คอมโพเนนต์การติดตามความคืบหน้า

สําหรับคอมโพเนนต์ที่กําหนดเองซึ่งติดตามความคืบหน้า คุณอาจต้องแจ้งให้ผู้ใช้ทราบถึงการเปลี่ยนแปลงความคืบหน้า รวมถึงค่าความคืบหน้าปัจจุบัน ช่วงของความคืบหน้า และขนาดขั้น ซึ่งทำได้โดยใช้ความหมายของ progressBarRangeInfo เพื่อให้บริการการช่วยเหลือพิเศษทราบถึงการเปลี่ยนแปลงความคืบหน้าและสามารถอัปเดตผู้ใช้ตามความเหมาะสม เทคโนโลยีความช่วยเหลือพิเศษแต่ละประเภทอาจมีวิธีบอกใบ้การเพิ่มขึ้นและลดลงที่แตกต่างกันด้วย

ProgressInfoBar(
    modifier = Modifier
        .semantics {
            progressBarRangeInfo =
                ProgressBarRangeInfo(
                    current = progress,
                    range = 0F..1F
                )
        }
)

ข้อมูลรายการและสินค้า

ในรายการและตารางกริดที่กำหนดเองซึ่งมีรายการจำนวนมาก บริการการช่วยเหลือพิเศษอาจได้รับข้อมูลที่ละเอียดยิ่งขึ้น เช่น จำนวนรายการและดัชนีทั้งหมด

การใช้ความหมายของ collectionInfo และ 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)
            }
        )
    }
}

คำอธิบายสถานะ

Composable สามารถกําหนด stateDescription สําหรับความหมาย ซึ่งเฟรมเวิร์ก Android จะใช้เพื่ออ่านสถานะของ Composable เช่น คอมโพสิเบิลที่เปิด/ปิดได้อาจอยู่ในสถานะ "เลือก" หรือ "ไม่ได้เลือก" ในบางกรณี คุณอาจต้องการลบล้างคำอธิบายสถานะเริ่มต้นหรือป้ายกำกับที่ Compose ใช้ ซึ่งทำได้โดยระบุป้ายกำกับสถานะคำอธิบายอย่างชัดเจนก่อนกำหนดคอมโพสิเบิลให้เปิด/ปิดได้ ดังนี้

@Composable
private fun TopicItem(itemTitle: String, selected: Boolean, onToggle: () -> Unit) {
    val stateSubscribed = stringResource(R.string.subscribed)
    val stateNotSubscribed = stringResource(R.string.not_subscribed)
    Row(
        modifier = Modifier
            .semantics {
                // Set any explicit semantic properties
                stateDescription = if (selected) stateSubscribed else stateNotSubscribed
            }
            .toggleable(
                value = selected,
                onValueChange = { onToggle() }
            )
    ) {
        /* ... */
    }
}

การทำงานที่กำหนดเอง

การดําเนินการที่กำหนดเองสามารถใช้กับท่าทางสัมผัสหน้าจอที่ซับซ้อนมากขึ้น เช่น การปัดเพื่อปิดหรือลากและวาง เนื่องจากผู้ใช้ที่มีความบกพร่องทางการเคลื่อนไหวหรือความพิการอื่นๆ อาจโต้ตอบกับท่าทางเหล่านี้ได้ยาก

หากต้องการให้ท่าทางสัมผัสปัดเพื่อปิดเข้าถึงได้ง่ายขึ้น คุณสามารถลิงก์กับการดำเนินการที่กำหนดเองได้โดยส่งการดำเนินการปิดและป้ายกำกับไปที่นั่น

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 จะไฮไลต์คอมโพเนนต์นั้น และบอกใบ้ว่ามีการดำเนินการอื่นๆ เพิ่มเติมในเมนู ซึ่งแสดงการปัดเพื่อปิดการดำเนินการดังกล่าว

ภาพเมนูการทำงาน TalkBack
รูปที่ 4 ภาพเมนูการดำเนินการของ TalkBack

Use Case อีกอย่างหนึ่งสําหรับการดําเนินการที่กำหนดเองคือรายการที่มีรายการจำนวนมากซึ่งมีการดําเนินการเพิ่มเติม เนื่องจากผู้ใช้อาจต้องทําซ้ำการดำเนินการแต่ละรายการสำหรับแต่ละรายการทีละรายการ

=ภาพการนำทางของการเข้าถึงด้วยสวิตช์บนหน้าจอ
รูปที่ 5 ภาพการนำทางของการเข้าถึงด้วยสวิตช์บนหน้าจอ

หากต้องการปรับปรุงประสบการณ์การไปยังส่วนต่างๆ ซึ่งเป็นประโยชน์อย่างยิ่งสำหรับเทคโนโลยีความช่วยเหลือพิเศษที่ทำงานผ่านการโต้ตอบ เช่น การเข้าถึงด้วยสวิตช์หรือการเข้าถึงด้วยเสียง คุณสามารถใช้การดำเนินการที่กำหนดเองในคอนเทนเนอร์เพื่อย้ายการดำเนินการออกจากการไปยังส่วนต่างๆ แต่ละรายการ และไปยังเมนูการดำเนินการแยกต่างหากได้ ดังนี้

ArticleListItemRow(
    modifier = Modifier
        .semantics {
            customActions = listOf(
                CustomAccessibilityAction(
                    label = "Open article",
                    action = {
                        openArticle()
                        true
                    }
                ),
                CustomAccessibilityAction(
                    label = "Add to bookmarks",
                    action = {
                        addToBookmarks()
                        true
                    }
                ),
            )
        }
) {
    Article(
        modifier = Modifier.clearAndSetSemantics { },
        onClick = openArticle,
    )
    BookmarkButton(
        modifier = Modifier.clearAndSetSemantics { },
        onClick = addToBookmarks,
    )
}

ในกรณีเหล่านี้ โปรดล้างความหมายเดิมของรายการย่อยด้วยตัวแก้ไข clearAndSetSemantics ด้วยตนเอง เนื่องจากคุณกําลังย้ายรายการย่อยไปยังการดําเนินการที่กำหนดเอง

ยกตัวอย่างเช่น เมื่อใช้การเข้าถึงด้วยสวิตช์ เมนูจะเปิดขึ้นเมื่อเลือกคอนเทนเนอร์และแสดงรายการการดำเนินการที่ฝังอยู่

การไฮไลต์การเข้าถึงด้วยสวิตช์ของรายการในรายการบทความ
รูปที่ 6 การเข้าถึงด้วยสวิตช์ไฮไลต์รายการในรายการบทความ
ภาพเมนูการทำงานของการเข้าถึงด้วยสวิตช์
รูปที่ 7 ภาพเมนูการทำงานสำหรับการเข้าถึงด้วยสวิตช์

ต้นไม้เชิงความหมาย

การคอมโพสิชันอธิบาย UI ของแอปและสร้างขึ้นโดยการเรียกใช้คอมโพสิเบิล การคอมโพสิชันเป็นโครงสร้างแบบต้นไม้ที่ประกอบด้วยคอมโพสิเบิลที่อธิบาย UI ของคุณ

ถัดจากองค์ประกอบจะมีต้นไม้ขนานที่เรียกว่าต้นไม้เชิงความหมาย แผนภาพนี้อธิบาย UI ในลักษณะอื่นที่เข้าใจง่ายสำหรับบริการการช่วยเหลือพิเศษและเฟรมเวิร์กการทดสอบ บริการการช่วยเหลือพิเศษใช้แผนภาพต้นไม้เพื่ออธิบายแอปแก่ผู้ใช้ที่มีความต้องการเฉพาะ เฟรมเวิร์กการทดสอบใช้แผนภาพต้นไม้เพื่อโต้ตอบกับแอปและตรวจสอบแอป ต้นไม้เซมาติกส์ไม่มีข้อมูลเกี่ยวกับวิธีวาดคอมโพสิชัน แต่มีข้อมูลเกี่ยวกับความหมายเชิงเซมาติกส์ของคอมโพสิชัน

ลําดับชั้น UI ทั่วไปและลําดับชั้นเชิงความหมาย
รูปที่ 8 ลําดับชั้น UI ทั่วไปและลําดับชั้นเชิงความหมาย

หากแอปของคุณประกอบด้วยคอมโพสิเบิลและตัวแก้ไขจากรากฐานของ Compose และไลบรารีวัสดุ ระบบจะกรอกและสร้างต้นไม้เชิงความหมายให้คุณโดยอัตโนมัติ อย่างไรก็ตาม เมื่อเพิ่มคอมโพสิเบิลระดับล่างที่กําหนดเอง คุณจะต้องระบุความหมายด้วยตนเอง นอกจากนี้ ยังอาจมีกรณีที่แผนภูมิแสดงความหมายขององค์ประกอบบนหน้าจอไม่ถูกต้องหรือไม่ครบถ้วน ซึ่งในกรณีนี้ คุณสามารถปรับแต่งแผนภูมิได้

ตัวอย่างเช่น ลองดูคอมโพสิชันปฏิทินที่กำหนดเองนี้

ปฏิทินที่กำหนดเองซึ่งประกอบกับองค์ประกอบวันที่ที่เลือกได้
รูปที่ 9 ปฏิทินที่กำหนดเองซึ่งประกอบไปด้วยองค์ประกอบวันที่ที่เลือกได้

ในตัวอย่างนี้ ปฏิทินทั้งหมดติดตั้งใช้งานเป็นคอมโพสิเบิลระดับล่างรายการเดียวโดยใช้คอมโพสิเบิล Layout และวาดลงใน Canvas โดยตรง หากคุณไม่ดำเนินการใดๆ เพิ่มเติม บริการการช่วยเหลือพิเศษจะไม่ได้รับข้อมูลเพียงพอเกี่ยวกับเนื้อหาของคอมโพสิเบิลและการเลือกของผู้ใช้ภายในปฏิทิน ตัวอย่างเช่น หากผู้ใช้คลิกวันที่ที่มี 17 เฟรมเวิร์กการช่วยเหลือพิเศษจะได้รับเฉพาะข้อมูลคำอธิบายสำหรับตัวควบคุมปฏิทินทั้งตัว ในกรณีนี้ บริการการช่วยเหลือพิเศษของ TalkBack จะอ่านว่า "ปฏิทิน" หรือ "ปฏิทินเดือนเมษายน" ซึ่งดีกว่าเพียงเล็กน้อย และผู้ใช้ก็จะไม่รู้ว่าระบบเลือกวันไหน หากต้องการให้คอมโพสิเบิลนี้เข้าถึงได้ง่ายขึ้น คุณจะต้องเพิ่มข้อมูลเชิงความหมายด้วยตนเอง

โครงสร้างที่ผสานและไม่ผสาน

ดังที่ได้กล่าวไว้ก่อนหน้านี้ คอมโพสิเบิลแต่ละรายการในต้นไม้ UI อาจมีการกําหนดพร็อพเพอร์ตี้ความหมายตั้งแต่ 0 รายการขึ้นไป เมื่อคอมโพสิเบิลไม่มีการตั้งค่าพร็อพเพอร์ตี้เชิงอรรถศาสตร์ คอมโพสิเบิลจะไม่รวมอยู่ในต้นไม้เชิงอรรถศาสตร์ วิธีนี้จะทำให้ต้นไม้เชิงอรรถศาสตร์มีเฉพาะโหนดที่มีความหมายเชิงอรรถศาสตร์จริงๆ อย่างไรก็ตาม บางครั้งการรวมต้นไม้ย่อยของโหนดบางรายการเข้าด้วยกันและถือว่าเป็นส่วนหนึ่งของกันก็อาจเป็นประโยชน์ในการสื่อความหมายที่ถูกต้องของสิ่งที่แสดงบนหน้าจอ วิธีนี้จะช่วยให้คุณใช้เหตุผลเกี่ยวกับชุดโหนดโดยรวมได้ แทนที่จะต้องจัดการกับโหนดที่สืบทอดแต่ละโหนดทีละรายการ โดยทั่วไปแล้ว แต่ละโหนดในต้นไม้นี้จะแสดงถึงองค์ประกอบที่โฟกัสได้เมื่อใช้บริการการช่วยเหลือพิเศษ

ตัวอย่างคอมโพสิเบิลดังกล่าวคือ Button คุณสามารถหาเหตุผลเกี่ยวกับปุ่มเป็นองค์ประกอบเดียวได้ แม้ว่าจะมีโหนดย่อยหลายโหนดก็ตาม

Button(onClick = { /*TODO*/ }) {
    Icon(
        imageVector = Icons.Filled.Favorite,
        contentDescription = null
    )
    Spacer(Modifier.size(ButtonDefaults.IconSpacing))
    Text("Like")
}

ในต้นไม้เซมาติกส์ ระบบจะผสานพร็อพเพอร์ตี้ของรายการที่สืบทอดมาจากปุ่ม และแสดงปุ่มเป็นโหนดใบเดี่ยวในต้นไม้ ดังนี้

การนําเสนอความหมายแบบใบเดียวที่ผสานรวม
รูปที่ 10 การนําเสนอความหมายแบบใบเดี่ยวที่ผสานรวม

Composable และ Modifier สามารถระบุว่าต้องการผสานพร็อพเพอร์ตี้ Semantics ของรายการสืบทอดได้โดยเรียกใช้ Modifier.semantics (mergeDescendants = true) {} การตั้งค่าพร็อพเพอร์ตี้นี้เป็น true บ่งบอกว่าควรผสานพร็อพเพอร์ตี้เชิงอรรถศาสตร์ ในตัวอย่าง Button คอมโพสิชัน Button ใช้ตัวแก้ไข clickable ภายในซึ่งมีตัวแก้ไข semantics นี้ ดังนั้น ระบบจะรวมโหนดที่สืบทอดมาจากปุ่ม อ่านเอกสารประกอบเกี่ยวกับการช่วยเหลือพิเศษเพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับกรณีที่คุณควรเปลี่ยนลักษณะการผสานในคอมโพสิเบิล

คอมโพเนนต์และตัวปรับเปลี่ยนหลายรายการในไลบรารี Foundation และ Material Compose มีการตั้งค่าพร็อพเพอร์ตี้นี้ เช่น ตัวแก้ไข clickable และ toggleable จะผสานรายการที่สืบทอดโดยอัตโนมัติ นอกจากนี้ ListItem composable จะผสานรายการที่สืบทอดมา

ตรวจสอบโครงสร้าง

จริงๆ แล้ว ต้นไม้เชิงความหมายเป็นต้นไม้ 2 ต้นที่แตกต่างกัน มีต้นไม้ความหมายที่ผสาน ซึ่งจะผสานโหนดที่สืบทอดเมื่อตั้งค่า mergeDescendants เป็น true นอกจากนี้ยังมีต้นไม้ความหมายที่ไม่ได้ผสาน ซึ่งจะไม่ใช้การผสาน แต่เก็บโหนดทุกโหนดไว้อย่างครบถ้วน บริการการช่วยเหลือพิเศษจะใช้ต้นไม้ที่ไม่มีการผสานและใช้อัลกอริทึมการผสานของตนเอง โดยพิจารณาจากพร็อพเพอร์ตี้ mergeDescendants เฟรมเวิร์กการทดสอบจะใช้ต้นไม้ที่ผสานโดยค่าเริ่มต้น

คุณสามารถตรวจสอบทั้ง 2 แผนผังได้ด้วยเมธอด printToLog() ระบบจะบันทึกต้นไม้ที่ผสานไว้โดยค่าเริ่มต้นและเช่นเดียวกับในตัวอย่างก่อนหน้านี้ หากต้องการพิมพ์ต้นไม้ที่ไม่มีการผสาน ให้ตั้งค่าพารามิเตอร์ useUnmergedTree ของตัวจับคู่ onRoot() เป็น true แทน

composeTestRule.onRoot(useUnmergedTree = true).printToLog("MY TAG")

เครื่องมือตรวจสอบเลย์เอาต์ช่วยให้คุณแสดงทั้งต้นไม้ความหมายที่ผสานและไม่ผสานได้โดยเลือกรายการที่ต้องการในตัวกรองมุมมอง

ตัวเลือกมุมมองเครื่องมือตรวจสอบเลย์เอาต์ ซึ่งช่วยให้ทั้งแสดงและซ่อนต้นไม้เชิงความหมายที่ผสานและไม่ผสานได้
รูปที่ 11 ตัวเลือกมุมมองเครื่องมือตรวจสอบเลย์เอาต์ ซึ่งจะแสดงทั้งต้นไม้เชิงความหมายที่ผสานและไม่ผสาน

สำหรับแต่ละโหนดในต้นไม้ เครื่องมือตรวจสอบเลย์เอาต์จะแสดงทั้งเซมาติกส์ที่ผสานและเซมาติกส์ที่ตั้งค่าไว้ในโหนดนั้นในแผงพร็อพเพอร์ตี้

พร็อพเพอร์ตี้เชิงอรรถศาสตร์ที่ผสานรวมและตั้งค่า
รูปที่ 12 พร็อพเพอร์ตี้เชิงอรรถศาสตร์ผสานและตั้งค่าแล้ว

โดยค่าเริ่มต้น ตัวจับคู่ในเฟรมเวิร์กการทดสอบจะใช้ต้นไม้ความหมายที่ผสาน คุณจึงโต้ตอบกับ Button ได้โดยจับคู่ข้อความที่แสดงอยู่ภายใน ดังนี้

composeTestRule.onNodeWithText("Like").performClick()

ลบล้างลักษณะการทํางานนี้โดยการตั้งค่าพารามิเตอร์ useUnmergedTree ของตัวจับคู่เป็น true เช่นเดียวกับตัวจับคู่ onRoot

ปรับโครงสร้าง

ดังที่ได้กล่าวไว้ก่อนหน้านี้ คุณสามารถลบล้างหรือล้างพร็อพเพอร์ตี้เชิงอรรถศาสตร์บางอย่าง หรือเปลี่ยนลักษณะการผสานของต้นไม้ได้ ซึ่งจะมีประโยชน์อย่างยิ่งเมื่อคุณสร้างคอมโพเนนต์ที่กําหนดเอง หากไม่ได้ตั้งค่าพร็อพเพอร์ตี้และลักษณะการผสานที่ถูกต้อง แอปอาจเข้าถึงไม่ได้ และการทดสอบอาจทำงานแตกต่างจากที่คุณคาดไว้ หากต้องการดูข้อมูลเพิ่มเติมเกี่ยวกับการทดสอบ โปรดดูคู่มือการทดสอบ