Material, Compose UI และ Foundation API จะใช้และเสนอแนวทางปฏิบัติที่เข้าถึงได้หลายอย่างโดยค่าเริ่มต้น ซึ่งมีอรรถศาสตร์ในตัวที่เป็นไปตามบทบาทและฟังก์ชันเฉพาะของตนเอง ซึ่งหมายความว่าการรองรับการช่วยเหลือพิเศษส่วนใหญ่จะพร้อมใช้งานโดยไม่ต้องดำเนินการเพิ่มเติมหรือดำเนินการเพียงเล็กน้อย
การใช้ API ที่เหมาะสมเพื่อวัตถุประสงค์ที่เหมาะสมหมายความว่าคอมโพเนนต์มักจะมาพร้อมกับลักษณะการทำงานด้านการช่วยเหลือพิเศษที่กำหนดไว้ล่วงหน้าซึ่งครอบคลุมกรณีการใช้งานมาตรฐาน อย่างไรก็ตาม โปรดตรวจสอบอีกครั้งเสมอว่าค่าเริ่มต้นเหล่านี้ตรงกับความต้องการด้านการช่วยเหลือพิเศษของคุณหรือไม่ หากไม่ Compose จะมีวิธีที่ครอบคลุมข้อกำหนดที่เฉพาะเจาะจงมากขึ้น
การทำความเข้าใจความหมายและรูปแบบการช่วยเหลือพิเศษเริ่มต้นใน Compose API จะช่วยให้คุณใช้งานโดยคำนึงถึงการช่วยเหลือพิเศษ นอกจากนี้ยังช่วยให้คุณรองรับการช่วยเหลือพิเศษในคอมโพเนนต์ที่กำหนดเองได้มากขึ้นด้วย
ขนาดเป้าหมายการสัมผัสขั้นต่ำ
องค์ประกอบบนหน้าจอที่ผู้ใช้สามารถคลิก แตะ หรือโต้ตอบด้วยได้ต้องมีขนาดใหญ่พอสำหรับการโต้ตอบที่เชื่อถือได้ เมื่อปรับขนาดองค์ประกอบเหล่านี้ ให้ตั้งค่าขนาดขั้นต่ำเป็น 48dp เพื่อให้เป็นไปตามหลักเกณฑ์การช่วยเหลือพิเศษของ Material Design อย่างถูกต้อง
คอมโพเนนต์ Material เช่น Checkbox, RadioButton, Switch, Slider และ Surface จะตั้งค่าขนาดขั้นต่ำนี้ภายใน แต่จะตั้งค่าก็ต่อเมื่อคอมโพเนนต์รับการดำเนินการของผู้ใช้ได้เท่านั้น ตัวอย่างเช่น เมื่อ Checkbox มีการตั้งค่าพารามิเตอร์ onCheckedChange เป็นค่าที่ไม่ใช่ Null ช่องทำเครื่องหมายจะมีระยะห่างจากขอบเพื่อให้มีความกว้างและความสูงอย่างน้อย 48 dp
@Composable private fun CheckableCheckbox() { Checkbox(checked = true, onCheckedChange = {}) }
เมื่อตั้งค่าพารามิเตอร์ onCheckedChange เป็น Null ระบบจะไม่รวมระยะห่างจากขอบ เนื่องจากไม่สามารถโต้ตอบกับคอมโพเนนต์ได้โดยตรง
@Composable private fun NonClickableCheckbox() { Checkbox(checked = true, onCheckedChange = null) }
เมื่อใช้การควบคุมการเลือก เช่น Switch, RadioButton หรือ Checkbox โดยปกติแล้วคุณจะยกเลิกการทำงานที่คลิกได้ไปยังคอนเทนเนอร์ระดับบนโดยตั้งค่า Callback การคลิกใน Composable เป็น null และเพิ่มตัวแก้ไข toggleable หรือ selectable ลงใน Composable ระดับบน
@Composable private fun CheckableRow() { MaterialTheme { var checked by remember { mutableStateOf(false) } Row( Modifier .toggleable( value = checked, role = Role.Checkbox, onValueChange = { checked = !checked } ) .padding(16.dp) .fillMaxWidth() ) { Text("Option", Modifier.weight(1f)) Checkbox(checked = checked, onCheckedChange = null) } } }
เมื่อขนาดของ Composable ที่คลิกได้เล็กกว่าขนาดเป้าหมายการสัมผัสขั้นต่ำ Compose จะยังคงเพิ่มขนาดเป้าหมายการสัมผัส โดยการขยายขนาดเป้าหมายการสัมผัสออกไปนอกขอบเขตของ Composable
ตัวอย่างต่อไปนี้มี Box ที่คลิกได้ขนาดเล็กมาก พื้นที่เป้าหมายการสัมผัสจะขยายออกไปนอกขอบเขตของ Box โดยอัตโนมัติ ดังนั้นการแตะข้าง Box จะยังคงทริกเกอร์กิจกรรมการคลิก
@Composable private fun SmallBox() { var clicked by remember { mutableStateOf(false) } Box( Modifier .size(100.dp) .background(if (clicked) Color.DarkGray else Color.LightGray) ) { Box( Modifier .align(Alignment.Center) .clickable { clicked = !clicked } .background(Color.Black) .size(1.dp) ) } }
เพื่อป้องกันไม่ให้พื้นที่การสัมผัสของ Composable ต่างๆ ทับซ้อนกัน ให้ใช้ขนาดขั้นต่ำที่ใหญ่พอสำหรับ Composable เสมอ ในตัวอย่างนี้ หมายความว่าคุณต้องใช้ตัวปรับแต่ง sizeIn เพื่อกำหนดขนาดขั้นต่ำสำหรับกล่องด้านใน ดังนี้
@Composable private fun LargeBox() { var clicked by remember { mutableStateOf(false) } Box( Modifier .size(100.dp) .background(if (clicked) Color.DarkGray else Color.LightGray) ) { Box( Modifier .align(Alignment.Center) .clickable { clicked = !clicked } .background(Color.Black) .sizeIn(minWidth = 48.dp, minHeight = 48.dp) ) } }
หากองค์ประกอบต้องคงขนาดที่มองเห็นได้ให้เล็กลง ให้ใช้ตัวแก้ไข minimumInteractiveComponentSize จาก Material 3 แทน โดยจะสงวนพื้นที่เลย์เอาต์ไว้อย่างน้อย 48 dp ในแต่ละมิติข้อมูลและจัดองค์ประกอบให้อยู่กึ่งกลางในพื้นที่นั้น เพื่อให้องค์ประกอบคงขนาดที่วาดไว้และองค์ประกอบรอบๆ ย้ายเพื่อสร้างพื้นที่ว่าง ตัวแก้ไขจะไม่ขยายเป้าหมายการสัมผัสเอง แต่จะเว้นที่ว่างในเลย์เอาต์สำหรับการขยายตามที่อธิบายไว้ก่อนหน้านี้
คอมโพเนนต์ Material เช่น Checkbox ใช้ตัวแก้ไขนี้ภายใน โดยให้วางไว้ก่อนตัวปรับแต่งขนาด เนื่องจากตัวปรับแต่งขนาดที่อยู่ก่อนหน้าจะจำกัดพื้นที่ที่สามารถจองได้
@Composable private fun MinimumInteractiveBox() { var clicked by remember { mutableStateOf(false) } Row(verticalAlignment = Alignment.CenterVertically) { Box( Modifier .clickable { clicked = !clicked } .minimumInteractiveComponentSize() .size(24.dp) .background(if (clicked) Color.DarkGray else Color.Black) ) Text("Option") } }
องค์ประกอบกราฟิก
เมื่อคุณกำหนด Composable Image หรือ Icon จะไม่มีวิธีอัตโนมัติที่เฟรมเวิร์ก Android จะเข้าใจสิ่งที่แอปแสดง คุณต้องส่งคำอธิบายแบบข้อความขององค์ประกอบกราฟิก
ลองนึกภาพหน้าจอที่ผู้ใช้สามารถแชร์หน้าปัจจุบันกับเพื่อนๆ ได้ หน้าจอนี้มีไอคอนแชร์ที่คลิกได้
เฟรมเวิร์ก Android ไม่สามารถอธิบายไอคอนเพียงอย่างเดียวให้ผู้ใช้ที่มีความบกพร่องทางสายตาเข้าใจได้ เฟรมเวิร์ก Android ต้องมีคำอธิบายข้อความเพิ่มเติมของไอคอน
พารามิเตอร์ contentDescription อธิบายองค์ประกอบกราฟิก ใช้สตริงที่แปลแล้ว เนื่องจากผู้ใช้จะเห็นสตริงนี้
@Composable private fun ShareButton(onClick: () -> Unit) { IconButton(onClick = onClick) { Icon( imageVector = Icons.Filled.Share, contentDescription = stringResource(R.string.label_share) ) } }
องค์ประกอบกราฟิกบางอย่างมีไว้เพื่อตกแต่งเท่านั้น และคุณอาจไม่ต้องการสื่อสารองค์ประกอบเหล่านั้นกับผู้ใช้ เมื่อตั้งค่าพารามิเตอร์ contentDescription เป็น null หมายความว่าคุณระบุต่อเฟรมเวิร์ก Android ว่าองค์ประกอบนี้ไม่มีการดำเนินการหรือสถานะที่เชื่อมโยง
@Composable private fun PostImage(post: Post, modifier: Modifier = Modifier) { val image = post.imageThumb ?: painterResource(R.drawable.placeholder_1_1) Image( painter = image, // Specify that this image has no semantic meaning contentDescription = null, modifier = modifier .size(40.dp, 40.dp) .clip(MaterialTheme.shapes.small) ) }
contentDescription มีไว้เพื่อใช้กับองค์ประกอบกราฟิกเป็นหลัก เช่น รูปภาพ คอมโพเนนต์ Material เช่น Button หรือ Text และลักษณะการทำงานที่ดำเนินการได้ เช่น clickable หรือ toggleable มาพร้อมกับความหมายอื่นๆ ที่กำหนดไว้ล่วงหน้าซึ่งอธิบายลักษณะการทำงานที่แท้จริง และสามารถเปลี่ยนแปลงได้ผ่าน Compose API อื่นๆ
องค์ประกอบแบบอินเทอร์แอกทีฟ
API ของ Material และ Foundation Compose จะสร้างองค์ประกอบ UI ที่ผู้ใช้โต้ตอบได้ผ่าน API ตัวแก้ไข clickable และ toggleable เนื่องจากคอมโพเนนต์ที่โต้ตอบได้อาจประกอบด้วยองค์ประกอบหลายรายการ clickable และ toggleable จึงผสานความหมายขององค์ประกอบย่อยโดยค่าเริ่มต้น เพื่อให้คอมโพเนนต์ได้รับการปฏิบัติเป็นเอนทิตีเชิงตรรกะรายการเดียว
เช่น Button ของ Material อาจประกอบด้วยไอคอนย่อยและข้อความบางส่วน แทนที่จะปฏิบัติต่อองค์ประกอบย่อยเป็นองค์ประกอบเดี่ยวๆ Button จะผสานความหมายขององค์ประกอบย่อยโดยค่าเริ่มต้น เพื่อให้บริการการช่วยเหลือพิเศษสามารถจัดกลุ่มองค์ประกอบเหล่านั้นตามความเหมาะสม
ในทำนองเดียวกัน การใช้ตัวแก้ไข clickable ยังทำให้ Composable รวมความหมายขององค์ประกอบสืบทอดเป็นเอนทิตีเดียว ซึ่งจะส่งไปยังบริการการช่วยเหลือพิเศษพร้อมกับการแสดงการดำเนินการที่เกี่ยวข้อง ดังนี้
Row( // Uses `mergeDescendants = true` under the hood modifier = Modifier.clickable { openArticle() } ) { Icon( painter = painterResource(R.drawable.ic_logo), contentDescription = "Open", ) Text("Accessibility in Compose") }
นอกจากนี้ คุณยังตั้งค่า onClickLabel ที่เฉพาะเจาะจงในองค์ประกอบหลักที่คลิกได้เพื่อให้ข้อมูลเพิ่มเติมแก่บริการการช่วยเหลือพิเศษและนำเสนอการดำเนินการที่ละเอียดยิ่งขึ้นได้ด้วย
Row( modifier = Modifier .clickable(onClickLabel = "Open this article") { openArticle() } ) { Icon( painter = painterResource(R.drawable.ic_logo), contentDescription = "Open" ) Text("Accessibility in Compose") }
เมื่อใช้ TalkBack เป็นตัวอย่าง ตัวแก้ไข clickable นี้และป้ายกำกับการคลิกจะช่วยให้ TalkBack แสดงคำแนะนำการดำเนินการว่า "แตะสองครั้งเพื่อเปิดบทความนี้" แทนที่จะเป็นความคิดเห็นเริ่มต้นทั่วไปที่ว่า "แตะสองครั้งเพื่อเปิดใช้งาน"
ความคิดเห็นนี้จะเปลี่ยนแปลงไปตามประเภทของการดำเนินการ การคลิกค้างจะแสดงคำแนะนำของ TalkBack ว่า "แตะสองครั้งค้างไว้เพื่อ" ตามด้วยป้ายกำกับ ดังนี้
Row( modifier = Modifier .combinedClickable( onLongClickLabel = "Bookmark this article", onLongClick = { addToBookmarks() }, onClickLabel = "Open this article", onClick = { openArticle() }, ) ) {}
ในบางกรณี คุณอาจไม่มีสิทธิ์เข้าถึงตัวแก้ไข clickable โดยตรง (เช่น เมื่อตั้งค่าไว้ที่ใดที่หนึ่งในเลเยอร์ที่ฝังอยู่ด้านล่าง) แต่ยังคงต้องการเปลี่ยนป้ายกำกับประกาศจากค่าเริ่มต้น โดยแยกการตั้งค่า clickable ออกจากการแก้ไขประกาศโดยใช้ตัวแก้ไข semantics และตั้งค่าป้ายกำกับการคลิกที่นั่นเพื่อแก้ไขการแสดงการดำเนินการ ดังนี้
@Composable private fun ArticleList(openArticle: () -> Unit) { NestedArticleListItem( // Clickable is set separately, in a nested layer: onClickAction = openArticle, // Semantics are set here: modifier = Modifier.semantics { onClick( label = "Open this article", action = { // Not needed here: openArticle() true } ) } ) }
คุณไม่จำเป็นต้องส่งการดำเนินการคลิก 2 ครั้ง API ของ Compose ที่มีอยู่ เช่น clickable หรือ Button จะจัดการเรื่องนี้ให้คุณ ตรรกะการผสานจะตรวจสอบว่ามีการใช้ป้ายกำกับและดำเนินการกับตัวแก้ไขที่อยู่นอกสุดสำหรับข้อมูลที่มีอยู่ ในตัวอย่างก่อนหน้านี้ NestedArticleListItem จะส่งการดำเนินการคลิก openArticle() ไปยังความหมาย clickable โดยอัตโนมัติ คุณสามารถปล่อยให้การดำเนินการคลิกเป็นค่า Null ในการดำเนินการตัวแก้ไขความหมายที่ 2 ได้ อย่างไรก็ตาม ป้ายกำกับการคลิกจะนำมาจากตัวแก้ไขความหมายที่ 2 onClick(label = "Open this document") เนื่องจากไม่มีอยู่ในตัวแก้ไขแรก
คุณอาจพบสถานการณ์ที่คาดหวังให้ระบบผสานความหมายขององค์ประกอบย่อยเข้ากับองค์ประกอบหลัก แต่กลับไม่เป็นเช่นนั้น ดูข้อมูลเชิงลึกเพิ่มเติมได้ที่การผสานและการล้าง
คอมโพเนนต์ที่กำหนดเอง
เมื่อสร้างคอมโพเนนต์ที่กำหนดเอง ให้ตรวจสอบการใช้งานคอมโพเนนต์ที่คล้ายกันในไลบรารี Material หรือไลบรารี Compose อื่นๆ จากนั้นเลียนแบบหรือแก้ไขลักษณะการทำงานของการช่วยเหลือพิเศษตามความเหมาะสม ตัวอย่างเช่น หากคุณแทนที่ Checkbox ของ Material ด้วยการติดตั้งใช้งานของคุณเอง การดูการติดตั้งใช้งาน Checkbox ที่มีอยู่จะช่วยเตือนให้คุณเพิ่มตัวแก้ไข triStateToggleable ซึ่งจะจัดการพร็อพเพอร์ตี้การช่วยเหลือพิเศษสำหรับคอมโพเนนต์ นอกจากนี้ ให้ใช้ตัวแก้ไข Foundation อย่างเต็มที่ เนื่องจากตัวแก้ไขเหล่านี้รวมถึงข้อควรพิจารณาด้านการช่วยเหลือพิเศษในตัวและแนวทางปฏิบัติของ Compose ที่มีอยู่ซึ่งครอบคลุมในส่วนนี้
นอกจากนี้ คุณยังดูตัวอย่างคอมโพเนนต์ปุ่มเปิด/ปิดที่กำหนดเองได้ในส่วนล้างและตั้งค่าความหมาย รวมถึงข้อมูลโดยละเอียดเพิ่มเติมเกี่ยวกับวิธีรองรับการช่วยเหลือพิเศษในคอมโพเนนต์ที่กำหนดเองในหลักเกณฑ์ API
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- การช่วยเหลือพิเศษใน Compose
- การทดสอบเลย์เอาต์ Compose