บริการการช่วยเหลือพิเศษคือแอปที่ปรับปรุงอินเทอร์เฟซผู้ใช้เพื่อช่วยเหลือ ผู้ใช้ที่มีความบกพร่องหรือผู้ที่อาจไม่สามารถโต้ตอบกับอุปกรณ์ได้อย่างเต็มที่ เป็นการชั่วคราว บริการเหล่านี้จะทำงานในเบื้องหลังและสื่อสารกับระบบเพื่อตรวจสอบเนื้อหาบนหน้าจอและโต้ตอบกับแอปในนามของผู้ใช้ ตัวอย่างเช่น โปรแกรมอ่านหน้าจอ (เช่น TalkBack), เครื่องมือการเข้าถึงด้วยสวิตช์ และระบบควบคุมด้วยเสียง
คู่มือนี้ครอบคลุมพื้นฐานของการสร้างบริการการช่วยเหลือพิเศษของ Android
วงจรของบริการการช่วยเหลือพิเศษ
หากต้องการสร้างบริการการช่วยเหลือพิเศษ คุณต้องขยายคลาส
AccessibilityService
และประกาศบริการในไฟล์ Manifest ของแอป
สร้างคลาสบริการ
สร้างคลาสที่ขยาย AccessibilityService คุณต้องลบล้างเมธอดต่อไปนี้
onAccessibilityEvent: เรียกใช้เมื่อระบบตรวจพบเหตุการณ์ที่ตรงกับการกำหนดค่าของบริการ (เช่น การเปลี่ยนโฟกัสหรือการคลิกปุ่ม) ซึ่งเป็นตำแหน่งที่บริการของคุณตีความอินเทอร์เฟซผู้ใช้onInterrupt: เรียกใช้เมื่อระบบขัดจังหวะความคิดเห็นของบริการ (เช่น เพื่อหยุดเอาต์พุตเสียงพูดเมื่อผู้ใช้ย้ายโฟกัสอย่างรวดเร็ว)
package com.example.android.apis.accessibility import android.accessibilityservice.AccessibilityService import android.accessibilityservice.AccessibilityServiceInfo import android.accessibilityservice.FingerprintGestureController import android.accessibilityservice.AccessibilityButtonController import android.accessibilityservice.GestureDescription import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityNodeInfo import android.graphics.Path import android.os.Build import android.media.AudioManager import android.content.Context class MyAccessibilityService : AccessibilityService() { override fun onAccessibilityEvent(event: AccessibilityEvent) { // Interpret the event and provide feedback to the user } override fun onInterrupt() { // Interrupt any ongoing feedback } override fun onServiceConnected() { // Perform initialization here } }
ประกาศในไฟล์ Manifest
ลงทะเบียนบริการในไฟล์ AndroidManifest.xml คุณต้องบังคับใช้สิทธิ์
BIND_ACCESSIBILITY_SERVICE
อย่างเคร่งครัดเพื่อให้มีเพียงระบบเท่านั้นที่เชื่อมโยงกับบริการของคุณได้
ประกาศ ServiceSettingsActivity เพื่อให้ปุ่มการตั้งค่าทำงานได้
<application> <service android:name=".accessibility.MyAccessibilityService" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" android:exported="true" android:label="@string/accessibility_service_label"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibility_service_config" /> </service> <activity android:name=".accessibility.ServiceSettingsActivity" android:exported="true" android:label="@string/accessibility_service_settings_label" /> </application>
กำหนดค่าบริการ
สร้างไฟล์การกำหนดค่าใน res/xml/accessibility_service_config.xml ไฟล์นี้จะกำหนดเหตุการณ์ที่บริการของคุณจัดการและความคิดเห็นที่บริการแสดง
อย่าลืมอ้างอิง ServiceSettingsActivity ที่คุณประกาศไว้ใน
ไฟล์ Manifest
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:description="@string/accessibility_service_description" android:accessibilityEventTypes="typeAllMask" android:accessibilityFlags="flagDefault|flagRequestFingerprintGestures|flagRequestAccessibilityButton" android:accessibilityFeedbackType="feedbackSpoken" android:notificationTimeout="100" android:canRetrieveWindowContent="true" android:canPerformGestures="true" android:settingsActivity="com.example.android.apis.accessibility.ServiceSettingsActivity" />
ไฟล์การกำหนดค่ามีแอตทริบิวต์หลักต่อไปนี้
android:accessibilityEventTypes: เหตุการณ์ที่ต้องการรับ ใช้typeAllMaskสำหรับบริการอเนกประสงค์android:canRetrieveWindowContent: ต้องเป็นtrueหากบริการของคุณต้อง ตรวจสอบลําดับชั้น UI (เช่น เพื่ออ่านข้อความจากหน้าจอ)android:canPerformGestures: ต้องเป็นtrueหากคุณต้องการส่ง ท่าทางสัมผัส (เช่น การปัดหรือแตะ) โดยอัตโนมัติandroid:accessibilityFlags: รวมแฟล็กเพื่อเปิดใช้ฟีเจอร์flagRequestFingerprintGesturesต้องใช้สำหรับท่าทางสัมผัสด้วยลายนิ้วมือflagRequestAccessibilityButtonจำเป็นสำหรับปุ่มการช่วยเหลือพิเศษของซอฟต์แวร์
ดูรายการตัวเลือกการกำหนดค่าทั้งหมดได้ที่
AccessibilityServiceInfo
การกำหนดค่ารันไทม์
แม้ว่าการกำหนดค่า XML จะเป็นแบบคงที่ แต่คุณก็สามารถแก้ไขการกำหนดค่าบริการแบบไดนามิกได้ในขณะรันไทม์ ซึ่งมีประโยชน์ในการเปิด/ปิดฟีเจอร์ตาม ค่ากำหนดของผู้ใช้
ลบล้าง onServiceConnected() เพื่อใช้การอัปเดตรันไทม์โดยใช้
setServiceInfo() ดังนี้
override fun onServiceConnected() { val info = AccessibilityServiceInfo() // Set the type of events that this service wants to listen to. info.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED or AccessibilityEvent.TYPE_VIEW_FOCUSED // Set the type of feedback your service provides. info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN // Set flags at runtime. info.flags = AccessibilityServiceInfo.FLAG_DEFAULT or AccessibilityServiceInfo.FLAG_REQUEST_FINGERPRINT_GESTURES this.setServiceInfo(info) }
ตีความเนื้อหา UI
เมื่อ onAccessibilityEvent() ทริกเกอร์ ระบบจะแสดง AccessibilityEvent เหตุการณ์นี้ทำหน้าที่เป็นจุดแรกเข้าของAccessibility Tree ซึ่งเป็นการแสดงเนื้อหาบนหน้าจอแบบลำดับชั้น
บริการของคุณจะโต้ตอบกับออบเจ็กต์ AccessibilityNodeInfo เป็นหลัก
ซึ่งแสดงถึงองค์ประกอบ UI เช่น ปุ่ม รายการ และข้อความ ข้อมูลเกี่ยวกับ
องค์ประกอบ UI เหล่านี้จะได้รับการปรับให้เป็นมาตรฐานใน AccessibilityNodeInfo
ตัวอย่างต่อไปนี้แสดงวิธีดึงข้อมูลแหล่งที่มาของเหตุการณ์และไปยังส่วนต่างๆ ของ ทรีการช่วยเหลือพิเศษเพื่อค้นหาข้อมูล
override fun onAccessibilityEvent(event: AccessibilityEvent) { // Get the source node of the event val sourceNode: AccessibilityNodeInfo? = event.source if (sourceNode == null) return // Inspect properties if (sourceNode.isCheckable) { val state = if (sourceNode.isChecked) "Checked" else "Unchecked" val label = sourceNode.text ?: sourceNode.contentDescription // Provide feedback (for example, speak to the user) speakToUser("$label is $state") } // Always recycle nodes to prevent memory leaks sourceNode.recycle() } private fun speakToUser(text: String) { // Your text-to-speech implementation goes here }
ดำเนินการในนามของผู้ใช้
บริการการช่วยเหลือพิเศษสามารถดำเนินการต่างๆ เช่น คลิกปุ่มหรือ เลื่อนรายการ ในนามของผู้ใช้
หากต้องการดำเนินการ ให้เรียกใช้ performAction() ในออบเจ็กต์ AccessibilityNodeInfo
fun performClick(node: AccessibilityNodeInfo) { if (node.isClickable) { node.performAction(AccessibilityNodeInfo.ACTION_CLICK) } }
สำหรับคีย์ลัดส่วนกลางที่มีผลต่อทั้งระบบ (เช่น การกดปุ่มย้อนกลับ หรือการเปิดหน้าต่างแจ้งเตือน) ให้ใช้ performGlobalAction()
// Navigate back fun navigateBack() { performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK) }
จัดการโฟกัส
Android มีโฟกัส 2 ประเภทที่แตกต่างกัน ได้แก่ โฟกัสอินพุต (ตำแหน่งที่อินพุตจากคีย์บอร์ด ไป) และโฟกัสการช่วยเหลือพิเศษ (สิ่งที่บริการการช่วยเหลือพิเศษตรวจสอบ)
ข้อมูลโค้ดต่อไปนี้แสดงวิธีค้นหาองค์ประกอบที่มีโฟกัสการช่วยเหลือพิเศษในปัจจุบัน
// Find the node that currently has accessibility focus // Note: rootInActiveWindow can be null if the window is not available val root = rootInActiveWindow if (root != null) { val focusedNode = root.findFocus(AccessibilityNodeInfo.FOCUS_ACCESSIBILITY) // Do something with focusedNode // Always recycle nodes focusedNode?.recycle() // rootInActiveWindow doesn't need to be recycled, but obtained nodes do. }
ข้อมูลโค้ดต่อไปนี้แสดงวิธีย้ายโฟกัสการช่วยเหลือพิเศษไปยังองค์ประกอบที่เฉพาะเจาะจง
// Request that the system give focus to a given node fun focusNode(node: AccessibilityNodeInfo) { node.performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS) }
เมื่อสร้างบริการการช่วยเหลือพิเศษ ให้คำนึงถึงสถานะโฟกัสของผู้ใช้และหลีกเลี่ยงการขโมยโฟกัส เว้นแต่จะเกิดจากการดำเนินการของผู้ใช้โดยชัดแจ้ง
ทำท่าทางสัมผัส
บริการของคุณสามารถส่งท่าทางสัมผัสที่กำหนดเองไปยังหน้าจอได้ เช่น การปัด การแตะ
หรือการโต้ตอบแบบมัลติทัช โดยให้ประกาศ
android:canPerformGestures="true" ในการกำหนดค่าเพื่อให้คุณใช้
dispatchGesture() API ได้
ท่าทางสัมผัสง่ายๆ
หากต้องการใช้ท่าทางสัมผัสอย่างง่าย ให้เริ่มต้นด้วยการสร้างออบเจ็กต์ Path เพื่อแสดงถึง
การเคลื่อนไหวที่เชื่อมโยงกับท่าทางสัมผัสที่กำหนด จากนั้นให้ใส่ Path ใน GestureDescription เพื่ออธิบายเส้น สุดท้าย ให้เรียกใช้ dispatchGesture
เพื่อส่งท่าทางสัมผัส
fun swipeRight() { // Create a path for the swipe (from x=100 to x=500) val swipePath = Path() swipePath.moveTo(100f, 500f) swipePath.lineTo(500f, 500f) // Build the stroke description (0ms delay, 500ms duration) val stroke = GestureDescription.StrokeDescription(swipePath, 0, 500) // Build the gesture description val gestureBuilder = GestureDescription.Builder() gestureBuilder.addStroke(stroke) // Dispatch the gesture dispatchGesture(gestureBuilder.build(), object : AccessibilityService.GestureResultCallback() { override fun onCompleted(gestureDescription: GestureDescription?) { super.onCompleted(gestureDescription) // Gesture finished successfully } }, null) }
ท่าทางสัมผัสต่อเนื่อง
สำหรับการโต้ตอบที่ซับซ้อน (เช่น การวาดรูปตัว L หรือการลากหลายขั้นตอนที่แม่นยำ) คุณสามารถเชื่อมโยงการลากเข้าด้วยกันได้โดยใช้พารามิเตอร์ willContinue
fun performLShapedGesture() { val path1 = Path().apply { moveTo(200f, 200f) lineTo(400f, 200f) } val path2 = Path().apply { moveTo(400f, 200f) lineTo(400f, 400f) } // First stroke: willContinue = true val stroke1 = GestureDescription.StrokeDescription(path1, 0, 500, true) // Second stroke: continues immediately after stroke1 val stroke2 = stroke1.continueStroke(path2, 0, 500, false) val builder = GestureDescription.Builder() builder.addStroke(stroke1) builder.addStroke(stroke2) dispatchGesture(builder.build(), null, null) }
การจัดการเสียง
เมื่อสร้างบริการการช่วยเหลือพิเศษ (โดยเฉพาะโปรแกรมอ่านหน้าจอ) ให้ใช้STREAM_ACCESSIBILITYสตรีมเสียง ซึ่งจะช่วยให้ผู้ใช้ควบคุมระดับเสียงของบริการ แยกจากระดับเสียงสื่อของระบบได้
fun increaseAccessibilityVolume() { val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager audioManager.adjustStreamVolume( AudioManager.STREAM_ACCESSIBILITY, AudioManager.ADJUST_RAISE, 0 ) }
อย่าลืมใส่แฟล็ก FLAG_ENABLE_ACCESSIBILITY_VOLUME ในการกำหนดค่า ไม่ว่าจะเป็นใน XML หรือผ่าน setServiceInfo ที่รันไทม์
ฟีเจอร์ขั้นสูง
ท่าทางสัมผัสลายนิ้วมือ
ในอุปกรณ์ที่ใช้ Android 10 (ระดับ API 29) ขึ้นไป บริการของคุณจะบันทึกการปัดตามทิศทางบนเซ็นเซอร์ลายนิ้วมือได้ ซึ่งมีประโยชน์ในการจัดหา ตัวควบคุมการนำทางสำรอง
เพิ่มตรรกะต่อไปนี้ลงในเมธอด onServiceConnected()
// Import: android.os.Build // Import: android.accessibilityservice.FingerprintGestureController private var gestureController: FingerprintGestureController? = null override fun onServiceConnected() { // Check if the device is running Android 10 (Q) or higher if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { gestureController = fingerprintGestureController val callback = object : FingerprintGestureController.FingerprintGestureCallback() { override fun onGestureDetected(gesture: Int) { when (gesture) { FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_DOWN -> { // Handle swipe down } FingerprintGestureController.FINGERPRINT_GESTURE_SWIPE_UP -> { // Handle swipe up } } } } gestureController?.registerFingerprintGestureCallback(callback, null) } }
ปุ่มการช่วยเหลือพิเศษ
ในอุปกรณ์ที่ใช้ปุ่มนำทางของซอฟต์แวร์ ผู้ใช้จะเรียกใช้บริการของคุณได้ผ่าน ปุ่มการช่วยเหลือพิเศษในแถบนำทาง
หากต้องการใช้ฟีเจอร์นี้ ให้เพิ่มแฟล็ก FLAG_REQUEST_ACCESSIBILITY_BUTTON ลงในการกำหนดค่าบริการ
จากนั้นเพิ่มตรรกะการลงทะเบียนลงในเมธอด
onServiceConnected()
// Import: android.accessibilityservice.AccessibilityButtonController override fun onServiceConnected() { // ... existing initialization code ... val controller = accessibilityButtonController controller.registerAccessibilityButtonCallback( object : AccessibilityButtonController.AccessibilityButtonCallback() { override fun onClicked(controller: AccessibilityButtonController) { // Respond to button tap } } ) }
การอ่านออกเสียงข้อความหลายภาษา
บริการที่อ่านข้อความออกเสียงจะเปลี่ยนภาษาโดยอัตโนมัติได้หากข้อความต้นฉบับ
มีแท็ก LocaleSpan ซึ่งจะช่วยให้บริการออกเสียงเนื้อหาที่มีหลายภาษาได้อย่างถูกต้องโดยไม่ต้องสลับภาษาด้วยตนเอง
import android.text.Spannable import android.text.SpannableStringBuilder import android.text.style.LocaleSpan import java.util.Locale // Wrap text in LocaleSpan to indicate language val spannable = SpannableStringBuilder("Bonjour") spannable.setSpan( LocaleSpan(Locale.FRANCE), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE )
เมื่อบริการของคุณประมวลผล AccessibilityNodeInfo ให้ตรวจสอบพร็อพเพอร์ตี้ text
สำหรับออบเจ็กต์ LocaleSpan เพื่อกำหนดภาษา Text-to-Speech ที่ถูกต้อง
แหล่งข้อมูลเพิ่มเติม
ดูข้อมูลเพิ่มเติมได้ที่แหล่งข้อมูลต่อไปนี้
เส้นนำ
- สร้างแอปที่เข้าถึงได้
- การช่วยเหลือพิเศษใน Jetpack Compose
- คู่มือฉบับย่อ: การช่วยเหลือพิเศษใน Compose