ควบคุมลำดับการส่งผ่าน

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

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

ใช้ isTraversalGroup และ traversalIndex ใน แอปสำหรับควบคุมลำดับการส่งผ่านของโปรแกรมอ่านหน้าจอ

จัดกลุ่มองค์ประกอบด้วย isTraversalGroup

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

การตั้งค่า isTraversalGroup = true ในโหนดหมายความว่ารายการย่อยทั้งหมดของโหนดนั้น ก่อนที่จะย้ายไปยังองค์ประกอบอื่นๆ คุณสามารถตั้ง isTraversalGroup ใน โหนดที่โฟกัสได้ของโปรแกรมอ่านหน้าจอที่ไม่ใช่ เช่น คอลัมน์ แถว หรือกล่อง

ตัวอย่างต่อไปนี้ใช้ isTraversalGroup โดยจะแสดงองค์ประกอบข้อความ 4 รายการ องค์ประกอบซ้าย 2 รายการเป็นขององค์ประกอบ CardBox 1 รายการ แต่ด้านขวา 2 องค์ประกอบ เป็นขององค์ประกอบ CardBox อื่น

// CardBox() function takes in top and bottom sample text.
@Composable
fun CardBox(
    topSampleText: String,
    bottomSampleText: String,
    modifier: Modifier = Modifier
) {
    Box(modifier) {
        Column {
            Text(topSampleText)
            Text(bottomSampleText)
        }
    }
}

@Composable
fun TraversalGroupDemo() {
    val topSampleText1 = "This sentence is in "
    val bottomSampleText1 = "the left column."
    val topSampleText2 = "This sentence is "
    val bottomSampleText2 = "on the right."
    Row {
        CardBox(
            topSampleText1,
            bottomSampleText1
        )
        CardBox(
            topSampleText2,
            bottomSampleText2
        )
    }
}

โค้ดจะสร้างเอาต์พุตที่คล้ายกับตัวอย่างต่อไปนี้

วันที่ เค้าโครงที่มีข้อความ 2 คอลัมน์ โดยคอลัมน์ด้านซ้ายคือ "This
  อยู่ในคอลัมน์ด้านซ้าย" และคอลัมน์ด้านขวาคือ "ประโยคนี้อยู่ทางขวา"
รูปที่ 1 เลย์เอาต์ที่มี 2 ประโยค (ประโยคหนึ่งทางซ้าย และอีก 1 คอลัมน์ในคอลัมน์ด้านขวา)

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

"ประโยคนี้อยู่ใน" → "ประโยคนี้คือ" → "คอลัมน์ด้านซ้าย" → "ใน ใช่ไหม"

หากต้องการเรียงลำดับส่วนย่อยอย่างถูกต้อง ให้แก้ไขข้อมูลโค้ดต้นฉบับเพื่อตั้งค่า isTraversalGroup ถึง true:

@Composable
fun TraversalGroupDemo2() {
    val topSampleText1 = "This sentence is in "
    val bottomSampleText1 = "the left column."
    val topSampleText2 = "This sentence is"
    val bottomSampleText2 = "on the right."
    Row {
        CardBox(
//      1,
            topSampleText1,
            bottomSampleText1,
            Modifier.semantics { isTraversalGroup = true }
        )
        CardBox(
//      2,
            topSampleText2,
            bottomSampleText2,
            Modifier.semantics { isTraversalGroup = true }
        )
    }
}

เนื่องจากตั้งค่า isTraversalGroup โดยเฉพาะใน CardBox แต่ละรายการ ค่า CardBox จะมีการใช้ขอบเขตเมื่อจัดเรียงองค์ประกอบ ในกรณีนี้ ด้านซ้าย CardBox อ่านก่อน ตามด้วย CardBox ที่ถูกต้อง

ตอนนี้ TalkBack จะอ่านส่วนย่อยของประโยคตามลำดับที่ถูกต้อง ดังนี้

"ประโยคนี้อยู่ใน" → "คอลัมน์ด้านซ้าย" → "ประโยคนี้คือ" → "ใน ใช่ไหม"

กำหนดค่าลำดับการส่งผ่านเพิ่มเติม

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

พร็อพเพอร์ตี้ traversalIndex มีลักษณะดังต่อไปนี้

  • ระบบจะจัดลำดับความสำคัญให้องค์ประกอบที่มีค่า traversalIndex ต่ำกว่าก่อน
  • ซึ่งอาจเป็นค่าบวกหรือค่าลบก็ได้
  • ค่าเริ่มต้นคือ 0f
  • มีผลกับโหนดที่โปรแกรมอ่านหน้าจอโฟกัสได้เท่านั้น เช่น องค์ประกอบบนหน้าจออย่างเช่น ข้อความหรือปุ่ม ตัวอย่างเช่น การตั้งค่าเพียง traversalIndex ในคอลัมน์จะ จะไม่มีผล เว้นแต่คอลัมน์จะมี isTraversalGroup ตั้งค่าไว้ด้วย

ตัวอย่างต่อไปนี้แสดงวิธีที่คุณสามารถใช้ traversalIndex และ isTraversalGroup

ตัวอย่าง: หน้าปัดนาฬิกา

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

วันที่ หน้าปัดนาฬิกาที่มีเครื่องมือเลือกเวลาอยู่ด้านบน
รูปที่ 2 รูปภาพหน้าปัดนาฬิกา

ในข้อมูลโค้ดแบบง่ายต่อไปนี้ มี CircularLayout ที่ 12 ตัวเลขจะถูกวาดโดยเริ่มจาก 12 และหมุนตามเข็มนาฬิการอบวงกลม

@Composable
fun ClockFaceDemo() {
    CircularLayout {
        repeat(12) { hour ->
            ClockText(hour)
        }
    }
}

@Composable
private fun ClockText(value: Int) {
    Box(modifier = Modifier) {
        Text((if (value == 0) 12 else value).toString())
    }
}

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

@Composable
fun ClockFaceDemo() {
    CircularLayout(Modifier.semantics { isTraversalGroup = true }) {
        repeat(12) { hour ->
            ClockText(hour)
        }
    }
}

@Composable
private fun ClockText(value: Int) {
    Box(modifier = Modifier.semantics { this.traversalIndex = value.toFloat() }) {
        Text((if (value == 0) 12 else value).toString())
    }
}

หากต้องการตั้งค่าลำดับการส่งผ่านอย่างเหมาะสม ขั้นแรกให้ตั้งค่า CircularLayout เป็น การส่งผ่านกลุ่มและตั้งค่า isTraversalGroup = true จากนั้น เมื่อข้อความนาฬิกาแต่ละข้อความ วาดลงบนเลย์เอาต์ ตั้งค่า traversalIndex ที่เกี่ยวข้องเป็นตัวนับ

เนื่องจากค่าตัวนับจะเพิ่มขึ้นอย่างต่อเนื่อง ค่านาฬิกาแต่ละค่า traversalIndex จะมีขนาดใหญ่ขึ้นเมื่อมีการเพิ่มตัวเลขลงในหน้าจอ ค่านาฬิกาจะเป็น 0 มี traversalIndex เป็น 0 และค่านาฬิกา 1 มี traversalIndex เป็น 1 ด้วยวิธีนี้ ระบบจะตั้งค่าลำดับที่ TalkBack อ่านข้อมูลเหล่านั้น ทีนี้ตัวเลขที่ ภายใน CircularLayout จะอ่านตามลำดับที่คาดไว้

เนื่องจาก traversalIndexes ที่ตั้งไว้สัมพันธ์กับค่าอื่นๆ เท่านั้น จะจัดดัชนีภายในกลุ่มเดียวกัน การเรียงลำดับหน้าจอที่เหลือ เก็บรักษาไว้ กล่าวอีกนัยหนึ่งคือ การเปลี่ยนแปลงเชิงความหมายที่แสดงในรหัสก่อนหน้า ข้อมูลโค้ดจะปรับเปลี่ยนการจัดลำดับภายในหน้าปัดนาฬิกาที่มี ตั้งค่าisTraversalGroup = trueแล้ว

โปรดทราบว่าหากไม่ได้ตั้งค่าความหมาย CircularLayout's เป็น isTraversalGroup = true การเปลี่ยนแปลง traversalIndex จะยังคงมีผล อย่างไรก็ตาม หากไม่มี CircularLayout เพื่อเชื่อมโยง ระบบจะอ่านตัวเลข 12 หลักของหน้าปัดนาฬิกา สุดท้าย หลังจากเข้าชมองค์ประกอบอื่นๆ ทั้งหมดบนหน้าจอแล้ว เหตุการณ์นี้ เนื่องจากองค์ประกอบอื่นๆ ทั้งหมดมี traversalIndex เริ่มต้นเป็น 0f และ ระบบจะอ่านองค์ประกอบข้อความนาฬิกาหลังจากองค์ประกอบ 0f อื่นๆ ทั้งหมด

ตัวอย่าง: ปรับแต่งลำดับการส่งผ่านสำหรับปุ่มการทำงานแบบลอย

ในตัวอย่างนี้ traversalIndex และ isTraversalGroup ควบคุม การเรียงลำดับการส่งผ่านของปุ่มการทำงานแบบลอย (FAB) ของดีไซน์ Material พื้นฐาน ของตัวอย่างนี้คือเลย์เอาต์ต่อไปนี้

วันที่ เลย์เอาต์ที่มีแถบแอปด้านบน ข้อความตัวอย่าง ปุ่มการทำงานแบบลอย และ
  แถบแอปด้านล่าง
รูปที่ 3 เลย์เอาต์ที่มีแถบแอปด้านบน ข้อความตัวอย่าง ปุ่มการทำงานแบบลอย และแถบแอปด้านล่าง

โดยค่าเริ่มต้น เลย์เอาต์ในตัวอย่างนี้จะมีลำดับ TalkBack ดังต่อไปนี้

แถบแอปด้านบน → ข้อความตัวอย่าง 0 ถึง 6 → ปุ่มการทำงานแบบลอย (FAB) → ด้านล่าง แถบแอป

คุณอาจต้องการให้โปรแกรมอ่านหน้าจอโฟกัสที่ FAB ก่อน วิธีตั้งค่า traversalIndex ในองค์ประกอบ Material เช่น FAB ให้ทำดังนี้

@Composable
fun FloatingBox() {
    Box(modifier = Modifier.semantics { isTraversalGroup = true; traversalIndex = -1f }) {
        FloatingActionButton(onClick = {}) {
            Icon(imageVector = Icons.Default.Add, contentDescription = "fab icon")
        }
    }
}

ในข้อมูลโค้ดนี้ ระบบจะสร้างกล่องที่มี ตั้งค่า isTraversalGroup เป็น true และตั้งค่า traversalIndex ในช่องเดียวกัน (-1f ต่ำกว่าค่าเริ่มต้น 0f) หมายความว่ากล่องแบบลอย ปรากฏก่อนองค์ประกอบบนหน้าจออื่นๆ ทั้งหมด

ต่อไป คุณสามารถวางกล่องแบบลอยและองค์ประกอบอื่นๆ ลงในนั่งร้าน ใช้เลย์เอาต์ดีไซน์ Material

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ColumnWithFABFirstDemo() {
    Scaffold(
        topBar = { TopAppBar(title = { Text("Top App Bar") }) },
        floatingActionButtonPosition = FabPosition.End,
        floatingActionButton = { FloatingBox() },
        content = { padding -> ContentColumn(padding = padding) },
        bottomBar = { BottomAppBar { Text("Bottom App Bar") } }
    )
}

TalkBack จะโต้ตอบกับองค์ประกอบต่างๆ ตามลำดับต่อไปนี้

FAB → แถบแอปด้านบน → ข้อความตัวอย่าง 0 ถึง 6 → แถบแอปด้านล่าง

แหล่งข้อมูลเพิ่มเติม