許多Android用戶因為各種情況導致他們要以不同的方式與手機互動。對於那些由於視力、聽力或其它身體原因導致不能方便使用Android智慧型手機的用戶,Android提供了Accessibility功能和服務幫助這些用戶更加簡單地操作設備,包括文字轉語音、觸覺反饋、手勢操作、軌跡球和手柄操作。開發者可以建立自己的Accessibility服務,這可以加強應用程式的可用性,例如聲音提示,實體回饋,和其他可選的操作模式。
它的具體實作是透過AccessibilityService服務運行在背景中,透過AccessibilityEvent接收指定事件的回呼。這樣的事件表示使用者在介面中的一些狀態轉換,例如:焦點改變了,一個按鈕被點擊,等等。這樣的服務可以選擇請求活動視窗的內容的能力。簡單的說AccessibilityService就是一個後台監控服務,當你監控的內容改變時,就會呼叫後台服務的回呼方法
寫自己的Service類,重寫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中常用的方法的介紹
既然是後台服務,那就需要我們在manifests中設定該服務訊息
< 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 >
我們必須注意:任何一個訊息配置錯誤,都會使該服務無反應
設定服務參數是指:設定用來接受指定類型的事件,監聽指定package,擷取視窗內容,取得事件類型的時間等等。其配置服務參數有兩種方法:
在原先的manifests中增加meta-data標籤指定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:表示該服務能否存取活動視窗中的內容。也就是如果你希望在服務中取得窗體內容,則需要設定其值為true
description:對此無障礙功能的描述,具體體現在下圖
notificationTimeout:接受事件的時間間隔,通常將其設為100即可
packageNames:表示對該服務是用來監聽哪個包的產生的事件,這裡以微信的包名為例
透過程式碼為我們的AccessibilityService配置AccessibilityServiceInfo訊息,這裡我們可以抽取成一個方法進行設置
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 );
由於我們監聽了事件的通知欄和介面等訊息,當我們指定packageNames的通知欄或介面發生變化時,會透過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 ;
}
}
當我們微信收到通知時,狀態列會有一條推播訊息到達,這個時候就會被TYPE_NOTIFICATION_STATE_CHANGED監聽,執行裡面的內容,當我們切換微信介面時,或是使用微信時,這個時候就會被TYPE_WINDOW_STATE_CHANGED監聽,執行裡面的內容
AccessibilityEvent的方法
取得了介面視窗變更後,這個時候就要取得控制項的節點。整個視窗的節點本質是個樹狀結構,透過以下操作節點訊息
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow ();
//通过文本找到对应的节点集合
List < AccessibilityNodeInfo > list = nodeInfo . findAccessibilityNodeInfosByText ( text );
//通过控件ID找到对应的节点集合,如com.tencent.mm:id/gd
List < AccessibilityNodeInfo > list = nodeInfo . findAccessibilityNodeInfosByViewId ( clickId );
當我們取得了節點資訊之後,對控制項節點進行模擬點擊、長按等操作,AccessibilityNodeInfo類別提供了performAction()方法讓我們執行模擬操作,具體操作可看官方文件介紹,這裡列舉常用的操作
//模拟点击
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 for UI Automator工具,透過它可以獲取控制信息
Q:
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
總是報空指針,問博主遇到這個問題嗎? A:我也遇到你這樣的問題了,我覺得是「配置服務參數」那裡的,我剛開始用在程式碼裡面配置,也是回傳null,我覺得我沒有寫這個android:canRetrieveWindowContent="true",但沒有找到android:canRetrieveWindowContent="true"對應的設置,所以我改成在xml裡面配置了,就可以了。
Q: 請問這些類別名稱com.tencent.mm.ui.LauncherUI是如何取得的? A: String className = event.getClassName().toString();使用Log列印出來,開啟微信看Log訊息
原文:Hensen_的博客