แนวคิดและการติดตั้งใช้งาน Jetpack Compose
เฟรมเวิร์ก Android ช่วยให้คุณสร้างบริการการช่วยเหลือพิเศษที่สามารถนำเสนอเนื้อหาจากแอปแก่ผู้ใช้ รวมถึงดำเนินการแอปในนามของผู้ใช้ได้ เพื่อช่วยเหลือผู้ใช้ที่มีความต้องการด้านการช่วยเหลือพิเศษ
Android มีบริการการช่วยเหลือพิเศษของระบบหลายอย่าง ซึ่งรวมถึงบริการต่อไปนี้
- TalkBack: ช่วยผู้ที่มีสายตาเลือนรางหรือตาบอด โดยจะประกาศเนื้อหาผ่านเสียงสังเคราะห์และดำเนินการในแอปเพื่อตอบสนองต่อท่าทางสัมผัสของผู้ใช้
- การเข้าถึงด้วยสวิตช์: ช่วยผู้ที่มีความบกพร่องด้านการเคลื่อนไหว โดยจะไฮไลต์องค์ประกอบแบบอินเทอร์แอกทีฟและดำเนินการเพื่อตอบสนองต่อการกดปุ่มของผู้ใช้ ซึ่งช่วยให้ควบคุมอุปกรณ์ได้โดยใช้ปุ่มเพียง 1 หรือ 2 ปุ่ม
แอปของคุณต้องปฏิบัติตามแนวทางปฏิบัติแนะนำที่อธิบายไว้ในหน้านี้ ซึ่งอิงตามหลักเกณฑ์ที่อธิบายไว้ในหัวข้อทำให้แอปเข้าถึงได้ง่ายขึ้น เพื่อช่วยให้ผู้ที่มีความต้องการด้านการช่วยเหลือพิเศษใช้แอปของคุณได้สำเร็จ
องค์ประกอบป้ายกำกับ
คุณควรระบุป้ายกำกับที่เป็นประโยชน์และสื่อความหมายสำหรับองค์ประกอบ UI แบบอินเทอร์แอกทีฟแต่ละรายการในแอป ป้ายกำกับแต่ละรายการต้องอธิบายความหมายและวัตถุประสงค์ขององค์ประกอบนั้นๆ โปรแกรมอ่านหน้าจอ เช่น TalkBack สามารถประกาศป้ายกำกับเหล่านี้ให้ผู้ใช้ทราบได้
ในกรณีส่วนใหญ่ คุณจะระบุคำอธิบายขององค์ประกอบ UI ในไฟล์ทรัพยากรเลย์เอาต์ที่มีองค์ประกอบนั้น โดยปกติแล้ว คุณจะเพิ่มป้ายกำกับโดยใช้
แอตทริบิวต์ contentDescription ตามที่อธิบายไว้ในคู่มือการทำให้แอป
เข้าถึงได้ง่ายขึ้น นอกจากนี้ยังมีเทคนิคการติดป้ายกำกับอื่นๆ อีกหลายอย่างที่อธิบายไว้ในส่วนต่อไปนี้
องค์ประกอบที่แก้ไขได้
เมื่อติดป้ายกำกับองค์ประกอบที่แก้ไขได้ เช่น
EditText ออบเจ็กต์ การแสดง
ข้อความที่เป็นตัวอย่างของอินพุตที่ถูกต้องในองค์ประกอบนั้นๆ จะเป็นประโยชน์ นอกเหนือจากการ
ทำให้ข้อความตัวอย่างนี้พร้อมใช้งานสำหรับโปรแกรมอ่านหน้าจอ ในกรณีเหล่านี้ คุณสามารถใช้แอตทริบิวต์ android:hint ได้ ดังที่แสดงในข้อมูลโค้ดต่อไปนี้
<!-- The hint text for en-US locale would be "Apartment, suite, or building". --> <EditText android:id="@+id/addressLine2" android:hint="@string/aptSuiteBuilding" ... />
ในกรณีนี้ ออบเจ็กต์ View ต้องตั้งค่าแอตทริบิวต์ android:labelFor
เป็นรหัสขององค์ประกอบ EditText ดูรายละเอียดเพิ่มเติมได้ในส่วนต่อไปนี้
องค์ประกอบ 2 รายการที่รายการหนึ่งอธิบายอีกรายการหนึ่ง
โดยทั่วไปแล้ว องค์ประกอบ EditText จะมีออบเจ็กต์
View ที่เกี่ยวข้องซึ่งอธิบายสิ่งที่ผู้ใช้ต้อง
ป้อนในองค์ประกอบ EditText คุณสามารถระบุความสัมพันธ์นี้ได้โดยการตั้งค่าแอตทริบิวต์ android:labelFor ของออบเจ็กต์ View
ตัวอย่างการติดป้ายกำกับองค์ประกอบคู่ดังกล่าวปรากฏในข้อมูลโค้ดต่อไปนี้
<!-- Label text for en-US locale would be "Username:" --> <TextView android:id="@+id/usernameLabel" ... android:text="@string/username" android:labelFor="@+id/usernameEntry" /> <EditText android:id="@+id/usernameEntry" ... /> <!-- Label text for en-US locale would be "Password:" --> <TextView android:id="@+id/passwordLabel" ... android:text="@string/password android:labelFor="@+id/passwordEntry" /> <EditText android:id="@+id/passwordEntry" android:inputType="textPassword" ... />
องค์ประกอบในคอลเล็กชัน
เมื่อเพิ่มป้ายกำกับให้กับองค์ประกอบของคอลเล็กชัน ป้ายกำกับแต่ละรายการต้องไม่ซ้ำกัน วิธีนี้จะช่วยให้บริการการช่วยเหลือพิเศษของระบบอ้างอิงองค์ประกอบบนหน้าจอได้เพียงรายการเดียวเมื่อประกาศป้ายกำกับ การจับคู่นี้ช่วยให้ผู้ใช้ทราบเมื่อเลื่อนดู UI หรือเมื่อย้ายโฟกัสไปยังองค์ประกอบที่ค้นพบแล้ว
โดยเฉพาะอย่างยิ่ง ให้ใส่ข้อความเพิ่มเติมหรือข้อมูลตามบริบทใน
องค์ประกอบภายในเลย์เอาต์ที่นำกลับมาใช้ซ้ำ เช่น
RecyclerView
ออบเจ็กต์ เพื่อให้ระบุองค์ประกอบย่อยแต่ละรายการได้ไม่ซ้ำกัน
หากต้องการทำเช่นนั้น ให้ตั้งค่าคำอธิบายเนื้อหาเป็นส่วนหนึ่งของการติดตั้งใช้งานอะแดปเตอร์ ดังที่แสดงในข้อมูลโค้ดต่อไปนี้
Kotlin
data class MovieRating(val title: String, val starRating: Integer) class MyMovieRatingsAdapter(private val myData: Array<MovieRating>): RecyclerView.Adapter<MyMovieRatingsAdapter.MyRatingViewHolder>() { class MyRatingViewHolder(val ratingView: ImageView) : RecyclerView.ViewHolder(ratingView) override fun onBindViewHolder(holder: MyRatingViewHolder, position: Int) { val ratingData = myData[position] holder.ratingView.contentDescription = "Movie ${position}: " + "${ratingData.title}, ${ratingData.starRating} stars" } }
Java
public class MovieRating { private String title; private int starRating; // ... public String getTitle() { return title; } public int getStarRating() { return starRating; } } public class MyMovieRatingsAdapter extends RecyclerView.Adapter<MyAdapter.MyRatingViewHolder> { private MovieRating[] myData; public static class MyRatingViewHolder extends RecyclerView.ViewHolder { public ImageView ratingView; public MyRatingViewHolder(ImageView iv) { super(iv); ratingView = iv; } } @Override public void onBindViewHolder(MyRatingViewHolder holder, int position) { MovieRating ratingData = myData[position]; holder.ratingView.setContentDescription("Movie " + position + ": " + ratingData.getTitle() + ", " + ratingData.getStarRating() + " stars") } }
กลุ่มเนื้อหาที่เกี่ยวข้อง
หากแอปแสดงองค์ประกอบ UI หลายรายการที่รวมกันเป็นกลุ่มตามธรรมชาติ เช่น รายละเอียดเพลงหรือแอตทริบิวต์ของข้อความ ให้จัดเรียงองค์ประกอบเหล่านี้ไว้ในคอนเทนเนอร์ ซึ่งโดยปกติจะเป็นคลาสย่อยของ ViewGroup ตั้งค่าแอตทริบิวต์
ของออบเจ็กต์คอนเทนเนอร์
android:screenReaderFocusable
เป็น true และตั้งค่าแอตทริบิวต์
android:focusable
ของออบเจ็กต์ภายในแต่ละรายการเป็น false วิธีนี้จะช่วยให้บริการการช่วยเหลือพิเศษนำเสนอคำอธิบายเนื้อหาขององค์ประกอบภายในทีละรายการในการประกาศครั้งเดียวได้
การรวมองค์ประกอบที่เกี่ยวข้องนี้ช่วยให้ผู้ใช้เทคโนโลยีความช่วยเหลือพิเศษค้นพบข้อมูลบนหน้าจอได้อย่างมีประสิทธิภาพมากขึ้น
ข้อมูลโค้ดต่อไปนี้มีเนื้อหาที่เกี่ยวข้องกับเนื้อหาอื่นๆ ดังนั้นองค์ประกอบคอนเทนเนอร์ ซึ่งเป็นอินสแตนซ์ของ ConstraintLayout จึงตั้งค่าแอตทริบิวต์ android:screenReaderFocusable เป็น true และองค์ประกอบ TextView ภายในแต่ละรายการตั้งค่าแอตทริบิวต์ android:focusable เป็น false
<!-- In response to a single user interaction, accessibility services announce both the title and the artist of the song. --> <ConstraintLayout android:id="@+id/song_data_container" ... android:screenReaderFocusable="true"> <TextView android:id="@+id/song_title" ... android:focusable="false" android:text="@string/my_song_title" /> <TextView android:id="@+id/song_artist" android:focusable="false" android:text="@string/my_songwriter" /> </ConstraintLayout>
เนื่องจากบริการการช่วยเหลือพิเศษประกาศคำอธิบายขององค์ประกอบภายในในการพูดครั้งเดียว คุณจึงควรทำให้คำอธิบายแต่ละรายการสั้นที่สุดเท่าที่จะเป็นไปได้ แต่ยังคงสื่อความหมายขององค์ประกอบนั้นๆ
หมายเหตุ: โดยทั่วไป คุณควรหลีกเลี่ยงการสร้างคำอธิบายเนื้อหาสำหรับกลุ่มโดยการรวมข้อความขององค์ประกอบย่อย เนื่องจากวิธีนี้จะทำให้คำอธิบายของกลุ่มไม่เสถียร และเมื่อข้อความขององค์ประกอบย่อย มีการเปลี่ยนแปลง คำอธิบายของกลุ่มอาจไม่ตรงกับข้อความที่มองเห็นอีกต่อไป
ในบริบทของรายการหรือกริด โปรแกรมอ่านหน้าจออาจรวมข้อความของโหนดข้อความย่อยขององค์ประกอบรายการหรือกริด คุณควรหลีกเลี่ยงการแก้ไขการประกาศนี้
กลุ่มที่ซ้อนกัน
หากอินเทอร์เฟซของแอปแสดงข้อมูลหลายมิติ เช่น รายการกิจกรรมในเทศกาลแบบวันต่อวัน ให้ใช้แอตทริบิวต์ android:screenReaderFocusable ในคอนเทนเนอร์กลุ่มภายใน รูปแบบการติดป้ายกำกับนี้ให้ความสมดุลที่ดีระหว่างจำนวนการประกาศที่จำเป็นในการค้นพบเนื้อหาของหน้าจอและความยาวของการประกาศแต่ละครั้ง
ข้อมูลโค้ดต่อไปนี้แสดงวิธีหนึ่งในการติดป้ายกำกับกลุ่มภายในกลุ่มที่ใหญ่กว่า
<!-- In response to a single user interaction, accessibility services announce the events for a single stage only. --> <ConstraintLayout android:id="@+id/festival_event_table" ... > <ConstraintLayout android:id="@+id/stage_a_event_column" android:screenReaderFocusable="true"> <!-- UI elements that describe the events on Stage A. --> </ConstraintLayout> <ConstraintLayout android:id="@+id/stage_b_event_column" android:screenReaderFocusable="true"> <!-- UI elements that describe the events on Stage B. --> </ConstraintLayout> </ConstraintLayout>
ส่วนหัวภายในข้อความ
แอปบางแอปใช้ ส่วนหัว เพื่อสรุปกลุ่มข้อความที่ปรากฏบนหน้าจอ หาก
องค์ประกอบ View รายการหนึ่งแสดงส่วนหัว คุณสามารถระบุวัตถุประสงค์ขององค์ประกอบนั้น
สำหรับบริการการช่วยเหลือพิเศษได้โดยการตั้งค่าแอตทริบิวต์
android:accessibilityHeading ขององค์ประกอบเป็น
true
ผู้ใช้บริการการช่วยเหลือพิเศษสามารถเลือกที่จะไปยังส่วนหัวต่างๆ แทนที่จะไปยังย่อหน้าหรือคำต่างๆ ความยืดหยุ่นนี้ช่วยปรับปรุงประสบการณ์การนำทางข้อความ
ชื่อบานหน้าต่างการช่วยเหลือพิเศษ
ใน Android 9 (ระดับ API 28) ขึ้นไป คุณสามารถระบุชื่อที่เป็นมิตรต่อการช่วยเหลือพิเศษสำหรับ บานหน้าต่าง ของหน้าจอได้ สำหรับวัตถุประสงค์ด้านการช่วยเหลือพิเศษ บานหน้าต่างคือส่วนหนึ่งของหน้าต่างที่มองเห็นได้ชัดเจน เช่น เนื้อหาของ Fragment เพื่อให้บริการการช่วยเหลือพิเศษเข้าใจลักษณะการทำงานแบบหน้าต่างของบานหน้าต่าง ให้ระบุชื่อที่สื่อความหมายสำหรับบานหน้าต่างของแอป จากนั้นบริการการช่วยเหลือพิเศษจะให้ข้อมูลที่ละเอียดยิ่งขึ้นแก่ผู้ใช้เมื่อลักษณะที่ปรากฏหรือเนื้อหาของบานหน้าต่างมีการเปลี่ยนแปลง
หากต้องการระบุชื่อของบานหน้าต่าง ให้ใช้
android:accessibilityPaneTitle
แอตทริบิวต์ ดังที่แสดงในข้อมูลโค้ดต่อไปนี้
<!-- Accessibility services receive announcements about content changes that are scoped to either the "shopping cart view" section (top) or "browse items" section (bottom) --> <MyShoppingCartView android:id="@+id/shoppingCartContainer" android:accessibilityPaneTitle="@string/shoppingCart" ... /> <MyShoppingBrowseView android:id="@+id/browseItemsContainer" android:accessibilityPaneTitle="@string/browseProducts" ... />
องค์ประกอบตกแต่ง
หากองค์ประกอบใน UI มีไว้เพื่อการเว้นระยะหรือลักษณะที่ปรากฏ
เท่านั้น ให้ตั้งค่า
android:importantForAccessibility
เป็น "no"
เพิ่มการดำเนินการสำหรับการช่วยเหลือพิเศษ
คุณควรอนุญาตให้ผู้ใช้บริการการช่วยเหลือพิเศษดำเนินการโฟลว์ผู้ใช้ทั้งหมดภายในแอปได้อย่างง่ายดาย เช่น หากผู้ใช้ปัดรายการในรายการได้ คุณก็ควรเปิดเผยการดำเนินการนี้ต่อบริการการช่วยเหลือพิเศษด้วย เพื่อให้ผู้ใช้มีวิธีอื่นในการทำโฟลว์ผู้ใช้เดียวกันให้เสร็จสมบูรณ์
ทำให้การดำเนินการทั้งหมดเข้าถึงได้
ผู้ใช้ TalkBack, การเข้าถึงด้วยเสียง, หรือการเข้าถึงด้วยสวิตช์อาจต้องมีวิธีอื่นในการทำขั้นตอนของผู้ใช้บางอย่างให้เสร็จสมบูรณ์ภายใน แอป สำหรับการดำเนินการที่เชื่อมโยงกับท่าทางสัมผัส เช่น การลากและวางหรือการปัด แอปของคุณสามารถเปิดเผยการดำเนินการในลักษณะที่ผู้ใช้บริการการช่วยเหลือพิเศษเข้าถึงได้
การใช้การดำเนินการสำหรับการช่วยเหลือพิเศษ ช่วยให้แอปมีวิธีอื่นให้ผู้ใช้ดำเนินการให้เสร็จสมบูรณ์ได้
ตัวอย่างเช่น หากแอปอนุญาตให้ผู้ใช้ปัดรายการได้ คุณก็สามารถเปิดเผยฟังก์ชันการทำงานผ่านการดำเนินการสำหรับการช่วยเหลือพิเศษที่กำหนดเองได้ ดังนี้
Kotlin
ViewCompat.addAccessibilityAction( // View to add accessibility action itemView, // Label surfaced to user by an accessibility service getText(R.id.archive) ) { _, _ -> // Same method executed when swiping on itemView archiveItem() true }
Java
ViewCompat.addAccessibilityAction( // View to add accessibility action itemView, // Label surfaced to user by an accessibility service getText(R.id.archive), (view, arguments) -> { // Same method executed when swiping on itemView archiveItem(); return true; } );
With the custom accessibility action implemented, users can access the action through the actions menu.
Make available actions understandable
When a view supports actions such as touch & hold, an accessibility service such as TalkBack announces it as "Double tap and hold to long press."
This generic announcement doesn't give the user any context about what a touch & hold action does.
To make this announcement more descriptive, you can replace the accessibility action’s announcement like so:
Kotlin
ViewCompat.replaceAccessibilityAction( // View that contains touch & hold action itemView, AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_LONG_CLICK, // Announcement read by TalkBack to surface this action getText(R.string.favorite), null )
Java
ViewCompat.replaceAccessibilityAction( // View that contains touch & hold action itemView, AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_LONG_CLICK, // Announcement read by TalkBack to surface this action getText(R.string.favorite), null );
This results in TalkBack announcing "Double tap and hold to favorite," helping users understand the purpose of the action.
Extend system widgets
Note: When you design your app's UI, use or extend
system-provided widgets that are as far down Android's class hierarchy as
possible. System-provided widgets that are far down the hierarchy already
have most of the accessibility capabilities your app needs. It's easier
to extend these system-provided widgets than to create your own from the more
generic View,
ViewCompat,
Canvas, and
CanvasCompat
classes.
If you must extend View or Canvas directly, which
might be necessary for a highly customized experience or a game level, see
Make custom views more
accessible.
This section uses the example of implementing a special type of
Switch called TriSwitch while following
best practices around extending system widgets. A TriSwitch
object works similarly to a Switch object, except that each instance of
TriSwitch allows the user to toggle among three possible states.
Extend from far down the class hierarchy
The Switch object inherits from several framework UI classes in its hierarchy:
View ↳ TextView ↳ Button ↳ CompoundButton ↳ Switch
คลาส TriSwitch ใหม่ควรขยายจากคลาส Switch
โดยตรง วิธีนี้จะช่วยให้เฟรมเวิร์กการช่วยเหลือพิเศษของ Android
มีความสามารถด้านการช่วยเหลือพิเศษส่วนใหญ่ที่คลาส TriSwitch
ต้องการ ดังนี้
- การดำเนินการสำหรับการช่วยเหลือพิเศษ: ข้อมูลสำหรับระบบเกี่ยวกับวิธีที่บริการการช่วยเหลือพิเศษสามารถจำลองข้อมูลจากผู้ใช้ที่เป็นไปได้แต่ละรายการที่ดำเนินการกับออบเจ็กต์
TriSwitch(รับค่ามาจากView) - เหตุการณ์การช่วยเหลือพิเศษ: ข้อมูลสำหรับบริการการช่วยเหลือพิเศษเกี่ยวกับวิธีที่เป็นไปได้ทั้งหมดที่ลักษณะที่ปรากฏของออบเจ็กต์
TriSwitchสามารถเปลี่ยนแปลงได้เมื่อหน้าจอรีเฟรชหรืออัปเดต (รับค่ามาจากView) - ลักษณะ: รายละเอียดเกี่ยวกับออบเจ็กต์
TriSwitchแต่ละรายการ เช่น เนื้อหาของข้อความที่แสดง (รับค่ามาจากTextView) - ข้อมูลสถานะ: คำอธิบายสถานะปัจจุบันของออบเจ็กต์
TriSwitchเช่น "เลือก" หรือ "ยกเลิกการเลือก" (รับค่ามาจากCompoundButton) - คำอธิบายสถานะแบบข้อความ: คำอธิบายแบบข้อความเกี่ยวกับสิ่งที่สถานะแต่ละสถานะแสดง (รับค่ามาจาก
Switch)
ลักษณะการทำงานนี้จาก Switch และคลาสระดับบนสุดเกือบจะเหมือนกับลักษณะการทำงานของออบเจ็กต์ TriSwitch ดังนั้นการติดตั้งใช้งานของคุณจึงมุ่งเน้นไปที่การขยายจำนวนสถานะที่เป็นไปได้จาก 2 เป็น 3 สถานะ
กำหนดเหตุการณ์ที่กำหนดเอง
เมื่อขยายวิดเจ็ตของระบบ คุณอาจเปลี่ยนลักษณะที่ผู้ใช้โต้ตอบกับวิดเจ็ตนั้น คุณควรกำหนดการเปลี่ยนแปลงการโต้ตอบเหล่านี้เพื่อให้บริการการช่วยเหลือพิเศษอัปเดตวิดเจ็ตของแอปได้ราวกับว่าผู้ใช้โต้ตอบกับวิดเจ็ตโดยตรง
แนวทางทั่วไปคือสำหรับทุกๆ การเรียกกลับที่อิงตามมุมมองที่คุณลบล้าง
คุณต้องกำหนดการดำเนินการสำหรับการช่วยเหลือพิเศษที่เกี่ยวข้องใหม่ด้วยการลบล้าง
ViewCompat.replaceAccessibilityAction()
ในการทดสอบแอป คุณสามารถตรวจสอบลักษณะการทำงานของการดำเนินการที่กำหนดใหม่เหล่านี้ได้โดย
การเรียก
ViewCompat.performAccessibilityAction()
วิธีที่หลักการนี้ใช้ได้กับออบเจ็กต์ TriSwitch
การแตะออบเจ็กต์ TriSwitch จะหมุนเวียนสถานะที่เป็นไปได้สามสถานะ ซึ่งแตกต่างจากออบเจ็กต์ Switch ทั่วไป ดังนั้นคุณจึงต้องอัปเดตการดำเนินการสำหรับการช่วยเหลือพิเศษ ACTION_CLICK ที่เกี่ยวข้อง
Kotlin
class TriSwitch(context: Context) : Switch(context) { // 0, 1, or 2 var currentState: Int = 0 private set init { updateAccessibilityActions() } private fun updateAccessibilityActions() { ViewCompat.replaceAccessibilityAction(this, ACTION_CLICK, action-label) { view, args -> moveToNextState() }) } private fun moveToNextState() { currentState = (currentState + 1) % 3 } }
Java
public class TriSwitch extends Switch { // 0, 1, or 2 private int currentState; public int getCurrentState() { return currentState; } public TriSwitch() { updateAccessibilityActions(); } private void updateAccessibilityActions() { ViewCompat.replaceAccessibilityAction(this, ACTION_CLICK, action-label, (view, args) -> moveToNextState()); } private void moveToNextState() { currentState = (currentState + 1) % 3; } }
แหล่งข้อมูลเพิ่มเติม
ดูแหล่งข้อมูลเพิ่มเติมต่อไปนี้เพื่อเรียนรู้เพิ่มเติมเกี่ยวกับการทำให้แอปเข้าถึงได้ง่ายขึ้น