ลักษณะการทำงานของโปรแกรมอ่านหน้าจอการช่วยเหลือพิเศษในแอป 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
อยู่ในคอลัมน์ด้านซ้าย" และคอลัมน์ด้านขวาคือ "ประโยคนี้อยู่ทางขวา"](https://developer.android.com/static/develop/ui/compose/images/a11y-istraversalgroup-example.png?authuser=5&hl=th)
เนื่องจากไม่ได้ตั้งค่าความหมาย ลักษณะการทำงานเริ่มต้นของโปรแกรมอ่านหน้าจอคือ เพื่อข้ามผ่านองค์ประกอบจากซ้ายไปขวาและบนลงล่าง ด้วยเหตุนี้ โดยค่าเริ่มต้น 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
ตัวอย่าง: หน้าปัดนาฬิกา
หน้าปัดนาฬิกาเป็นสถานการณ์ทั่วไปที่ลำดับการส่งผ่านแบบมาตรฐานไม่ งาน ตัวอย่างในส่วนนี้คือเครื่องมือเลือกเวลา ซึ่งผู้ใช้สามารถข้ามผ่านได้ ผ่านตัวเลขบนหน้าปัดนาฬิกาและเลือกตัวเลขเป็นชั่วโมงและนาที
![หน้าปัดนาฬิกาที่มีเครื่องมือเลือกเวลาอยู่ด้านบน](https://developer.android.com/static/develop/ui/compose/images/a11y-clock-face.png?authuser=5&hl=th)
ในข้อมูลโค้ดแบบง่ายต่อไปนี้ มี 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 พื้นฐาน
ของตัวอย่างนี้คือเลย์เอาต์ต่อไปนี้
![เลย์เอาต์ที่มีแถบแอปด้านบน ข้อความตัวอย่าง ปุ่มการทำงานแบบลอย และ
แถบแอปด้านล่าง](https://developer.android.com/static/develop/ui/compose/images/a11y-fab-example.png?authuser=5&hl=th)
โดยค่าเริ่มต้น เลย์เอาต์ในตัวอย่างนี้จะมีลำดับ 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 → แถบแอปด้านล่าง
แหล่งข้อมูลเพิ่มเติม
- การช่วยเหลือพิเศษ: แนวคิดสำคัญและ เทคนิคที่มักมีในการพัฒนาแอป Android ทั้งหมด
- สร้างแอปที่เข้าถึงได้: ขั้นตอนสำคัญ ที่คุณทำได้เพื่อให้แอปของคุณเข้าถึงได้ง่ายขึ้น
- หลักการปรับปรุงแอป การช่วยเหลือพิเศษ: หลักการสำคัญในการ ที่ควรคำนึงถึงเมื่อต้องทำให้แอปของคุณเข้าถึงได้ง่ายขึ้น
- การทดสอบการช่วยเหลือพิเศษ หลักการและเครื่องมือการทดสอบสำหรับการช่วยเหลือพิเศษของ Android