ผู้ใช้ Android จำนวนมากมีสถานการณ์ต่างๆ มากมายที่ทำให้พวกเขาโต้ตอบกับโทรศัพท์ด้วยวิธีที่ต่างกัน สำหรับผู้ใช้ที่ไม่สามารถใช้สมาร์ทโฟน Android ได้เนื่องจากการมองเห็น การได้ยิน หรือเหตุผลทางกายภาพอื่นๆ Android มีฟังก์ชันและบริการการเข้าถึงเพื่อช่วยให้ผู้ใช้เหล่านี้ใช้งานอุปกรณ์ได้ง่ายขึ้น รวมถึงการอ่านออกเสียงข้อความ การตอบสนองต่อการสัมผัส การใช้ท่าทาง แทร็กบอล และการดำเนินการจัดการ นักพัฒนาสามารถสร้างบริการการเข้าถึงของตนเอง ซึ่งสามารถปรับปรุงการใช้งานแอปพลิเคชัน เช่น เสียงเตือน การตอบรับทางกายภาพ และโหมดการทำงานเสริมอื่น ๆ
การใช้งานเฉพาะของมันคือการทำงานในเบื้องหลังผ่านบริการ AccessibilityService และรับการเรียกกลับสำหรับเหตุการณ์ที่ระบุผ่าน AccessibilityEvent เหตุการณ์ดังกล่าวแสดงถึงการเปลี่ยนแปลงสถานะบางอย่างโดยผู้ใช้ในอินเทอร์เฟซ เช่น การเปลี่ยนโฟกัส การคลิกปุ่ม เป็นต้น บริการดังกล่าวอาจเป็นทางเลือกที่สามารถขอเนื้อหาของหน้าต่างที่ใช้งานอยู่ได้ พูดง่ายๆ ก็คือ AccessibilityService เป็นบริการตรวจสอบพื้นหลัง เมื่อเนื้อหาที่คุณตรวจสอบเปลี่ยนแปลง วิธีการโทรกลับของบริการพื้นหลังจะถูกเรียก
เขียนคลาสบริการของคุณเองและแทนที่เมธอด onServiceConnected(), เมธอด onAccessibilityEvent() และเมธอด onInterrupt()
public class QHBAccessibilityService extends AccessibilityService {
/**
* 当启动服务的时候就会被调用
*/
@ Override
protected void onServiceConnected () {
super . onServiceConnected ();
}
/**
* 监听窗口变化的回调
*/
@ Override
public void onAccessibilityEvent ( AccessibilityEvent event ) {
int eventType = event . getEventType ();
//根据事件回调类型进行处理
}
/**
* 中断服务的回调
*/
@ Override
public void onInterrupt () {
}
}
ต่อไปนี้เป็นคำแนะนำเกี่ยวกับวิธีการที่ใช้กันทั่วไปใน AccessibilityService
เนื่องจากเป็นบริการเบื้องหลัง เราจึงต้องกำหนดค่าข้อมูลบริการในรายการ
< service
android : name = " .AccessibilityService.QHBAccessibilityService "
android : enabled = " true "
android : exported = " true "
android : label = " @string/label "
android : permission = " android.permission.BIND_ACCESSIBILITY_SERVICE " >
< intent-filter >
< action android : name = " android.accessibilityservice.AccessibilityService " />
</ intent-filter >
</ service >
เราต้องให้ความสนใจ: ข้อผิดพลาดในการกำหนดค่าข้อมูลใด ๆ จะทำให้บริการไม่ตอบสนอง
การกำหนดค่าพารามิเตอร์บริการหมายถึง: การกำหนดค่าให้ยอมรับเหตุการณ์ประเภทที่ระบุ ฟังแพ็คเกจที่ระบุ ดึงเนื้อหาหน้าต่าง รับเวลาของประเภทเหตุการณ์ ฯลฯ มีสองวิธีในการกำหนดค่าพารามิเตอร์บริการ:
เพิ่มแท็กข้อมูลเมตาลงในรายการต้นฉบับเพื่อระบุไฟล์ xml
< service
android : name = " .AccessibilityService.QHBAccessibilityService "
android : enabled = " true "
android : exported = " true "
android : label = " @string/label "
android : permission = " android.permission.BIND_ACCESSIBILITY_SERVICE " >
< intent-filter >
< action android : name = " android.accessibilityservice.AccessibilityService " />
</ intent-filter >
< meta-data
android : name = " android.accessibilityservice "
android : resource = " @xml/accessibility_service_config " />
</ service >
ถัดไปคือการกำหนดค่าของไฟล์ accessibility_service_config
<? xml version = " 1.0 " encoding = " utf-8 " ?>
< accessibility-service xmlns : android = " http://schemas.android.com/apk/res/android "
android : accessibilityEventTypes = " typeNotificationStateChanged|typeWindowStateChanged|typeWindowContentChanged|typeWindowsChanged "
android : accessibilityFeedbackType = " feedbackGeneric "
android : accessibilityFlags = " flagDefault "
android : canRetrieveWindowContent = " true "
android : description = " @string/description "
android : notificationTimeout = " 100 "
android : packageNames = " com.tencent.mm " />
ต่อไปนี้เป็นคำแนะนำเกี่ยวกับพารามิเตอร์ xml
accessibilityEventTypes: ระบุว่าการเปลี่ยนแปลงใดในอินเทอร์เฟซที่บริการสนใจ นั่นคือ เหตุการณ์ใดที่ได้รับการแจ้งเตือน เช่น การเปิดหน้าต่าง การเลื่อน การเปลี่ยนโฟกัส การกดแบบยาว เป็นต้น ค่าเฉพาะสามารถพบได้ในคลาส AccessibilityEvent ตัวอย่างเช่น typeAllMask หมายถึงการยอมรับการแจ้งเตือนเหตุการณ์ทั้งหมด
accessibilityFeedbackType: ระบุวิธีการตอบรับ เช่น การเล่นเสียงหรือการสั่น
canRetrieveWindowContent: ระบุว่าบริการสามารถเข้าถึงเนื้อหาในหน้าต่างที่ใช้งานอยู่หรือไม่ นั่นคือถ้าคุณต้องการรับเนื้อหาแบบฟอร์มในบริการ คุณต้องตั้งค่าให้เป็นจริง
คำอธิบาย: คำอธิบายคุณลักษณะการเข้าถึง ดังแสดงในรูปด้านล่าง
alertTimeout: ช่วงเวลาสำหรับการยอมรับเหตุการณ์ โดยปกติจะตั้งค่าเป็น 100
packageNames: ระบุว่าบริการใช้แพ็คเกจใดในการตรวจสอบเหตุการณ์ ที่นี่เราใช้ชื่อแพ็คเกจของ WeChat เป็นตัวอย่าง
กำหนดค่าข้อมูล AccessibilityServiceInfo สำหรับ AccessibilityService ของเราผ่านโค้ด ที่นี่เราสามารถแยกข้อมูลออกเป็นวิธีการตั้งค่าได้
private void settingAccessibilityInfo () {
String [] packageNames = { "com.tencent.mm" };
AccessibilityServiceInfo mAccessibilityServiceInfo = new AccessibilityServiceInfo ();
// 响应事件的类型,这里是全部的响应事件(长按,单击,滑动等)
mAccessibilityServiceInfo . eventTypes = AccessibilityEvent . TYPES_ALL_MASK ;
// 反馈给用户的类型,这里是语音提示
mAccessibilityServiceInfo . feedbackType = AccessibilityServiceInfo . FEEDBACK_SPOKEN ;
// 过滤的包名
mAccessibilityServiceInfo . packageNames = packageNames ;
setServiceInfo ( mAccessibilityServiceInfo );
}
คลาส AccessibilityServiceInfo เกี่ยวข้องที่นี่ คลาส AccessibilityServiceInfo ใช้เพื่อกำหนดค่าข้อมูล AccessibilityService คลาสนี้ประกอบด้วยฟิลด์คงที่จำนวนมากสำหรับการกำหนดค่าและแอตทริบิวต์ xml คลาส AccessibilityEventTypes, canRequestFilterKeyEvents, packageNames เป็นต้น
ที่นี่เราจำเป็นต้องเปิดฟังก์ชันนี้ด้วยตนเองในฟังก์ชันการช่วยสำหรับการเข้าถึง ไม่เช่นนั้นเราจะไม่สามารถดำเนินการต่อได้ คุณสามารถเปิดรายการฟังก์ชันการช่วยสำหรับการเข้าถึงของระบบผ่านรหัสต่อไปนี้
Intent intent = new Intent ( Settings . ACTION_ACCESSIBILITY_SETTINGS );
startActivity ( intent );
เนื่องจากเราตรวจสอบแถบการแจ้งเตือนและอินเทอร์เฟซของเหตุการณ์ เมื่อแถบการแจ้งเตือนหรืออินเทอร์เฟซของชื่อแพ็คเกจที่ระบุเปลี่ยนแปลง กิจกรรมของเราจะถูกเรียกกลับผ่าน onAccessibilityEvent จากนั้นเหตุการณ์จะถูกประมวลผล
@ Override
public void onAccessibilityEvent ( AccessibilityEvent event ) {
int eventType = event . getEventType ();
//根据事件回调类型进行处理
switch ( eventType ) {
//当通知栏发生改变时
case AccessibilityEvent . TYPE_NOTIFICATION_STATE_CHANGED :
break ;
//当窗口的状态发生改变时
case AccessibilityEvent . TYPE_WINDOW_STATE_CHANGED :
break ;
}
}
เมื่อเราได้รับการแจ้งเตือนบน WeChat ข้อความพุชจะมาถึงในแถบสถานะ ในเวลานี้ จะถูกตรวจสอบโดย TYPE_NOTIFICATION_STATE_CHANGED และเนื้อหาภายในจะถูกดำเนินการ เมื่อเราสลับอินเทอร์เฟซ WeChat หรือใช้ WeChat คราวนี้มันจะถูกดำเนินการ ได้รับการตรวจสอบโดย TYPE_WINDOW_STATE_CHANGED ดำเนินการเนื้อหาภายใน
วิธีการเข้าถึงเหตุการณ์
หลังจากได้รับการเปลี่ยนแปลงหน้าต่างอินเทอร์เฟซแล้ว จำเป็นต้องได้รับโหนดควบคุมในขณะนี้ โหนดของหน้าต่างทั้งหมดโดยพื้นฐานแล้วเป็นโครงสร้างแบบต้นไม้ ข้อมูลโหนดจะดำเนินการผ่านสิ่งต่อไปนี้
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow ();
//通过文本找到对应的节点集合
List < AccessibilityNodeInfo > list = nodeInfo . findAccessibilityNodeInfosByText ( text );
//通过控件ID找到对应的节点集合,如com.tencent.mm:id/gd
List < AccessibilityNodeInfo > list = nodeInfo . findAccessibilityNodeInfosByViewId ( clickId );
หลังจากที่เราได้รับข้อมูลโหนดแล้ว เราก็สามารถทำการคลิกจำลอง การกดแบบยาว และการดำเนินการอื่นๆ บนโหนดควบคุมได้ คลาส AccessibilityNodeInfo จัดเตรียมเมธอด PerformanceAction() เพื่อให้เราสามารถดำเนินการจำลองได้ สำหรับการดำเนินการเฉพาะ โปรดดูที่เอกสารประกอบอย่างเป็นทางการ การดำเนินการที่ใช้กันทั่วไปแสดงอยู่ที่นี่
//模拟点击
accessibilityNodeInfo . performAction ( AccessibilityNodeInfo . ACTION_CLICK );
//模拟长按
accessibilityNodeInfo . performAction ( AccessibilityNodeInfo . ACTION_LONG_CLICK );
//模拟获取焦点
accessibilityNodeInfo . performAction ( AccessibilityNodeInfo . ACTION_FOCUS );
//模拟粘贴
accessibilityNodeInfo . performAction ( AccessibilityNodeInfo . ACTION_PASTE );
เมื่อโทรศัพท์ของเราเชื่อมต่อกับสาย USB ให้เลือกอุปกรณ์ใน Android Device Monitor และเปิดเครื่องมือ Dump View Hierarchy สำหรับ UI Automator ซึ่งเราสามารถรับข้อมูลการควบคุมได้
ถาม:
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
บล็อกเกอร์ประสบปัญหานี้หรือไม่ ฉันพบปัญหาเช่นเดียวกับคุณ ฉันคิดว่ามันอยู่ใน "พารามิเตอร์บริการการกำหนดค่า" ฉันเพิ่งเริ่มกำหนดค่าในโค้ดและมันก็คืนค่าเป็นโมฆะด้วย ฉันรู้สึกว่าฉันไม่ได้เขียน android:canRetrieveWindowContent="true "แต่ไม่พบ ฉันพบการตั้งค่าที่สอดคล้องกับ android:canRetrieveWindowContent="true" ดังนั้นฉันจึงเปลี่ยนให้กำหนดค่าเป็น xml และมันก็ใช้ได้
ถาม: คุณได้รับชื่อคลาสเหล่านี้ com.tencent.mm.ui.LauncherUI ได้อย่างไร ตอบ: String className = event.getClassName().toString(); ใช้ Log เพื่อพิมพ์ออกมา และเปิด WeChat เพื่อดูข้อมูล Log
ข้อความต้นฉบับ: บล็อกของ Hensen_