1. 程式人生 > >Android輔助功能

Android輔助功能

1、前言

Android的輔助功能是個比較老的API(since API 4),但是該API真正的開始發展還是在API 16~19(很多常用的方法引數都是在這幾個版本逐漸加入以及完善的)版本中。輔助功能我們最常見的是在類似深度休眠,搶紅包中使用,模擬點選螢幕view。此次寫關於輔助功能的分享是因為在API 24、 API 26中,又增添了幾個類以及幾個介面。

本文首先介紹關於輔助功能的常見使用方法,然後開始介紹輔助功能新增介面以及類的使用。

2、輔助功能的使用

2.1 輔助功能常規使用方法:

本小結介紹一般情況下輔助功能的使用(基礎用法和方法釋義較多,如果需要閱覽API > 20 新增方法使用請從2.2開始閱讀),API level在20及以下的使用方式。不涉及新增的方法以及類的使用介紹。

2.1.1 使用流程:

1、Service: 配置自己的輔助功服務(註冊、配置)

2、Class: 自己的輔助功能類(對事件的攔截、處理)

2.1.2 Service配置:

1、在例項化的輔助功能類中:onServiceConnected()中初始化,如下所示。

@Override
protected void onServiceConnected() {
    super.onServiceConnected();
    AccessibilityServiceInfo info = new AccessibilityServiceInfo();
    info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
    info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
    info.notificationTimeout = 100;
    info.packageNames = new String[]{"...", "..."};
    info.flags = AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS;
    setServiceInfo(info);
}

2、使用manifest中新增meta-data的方式(該方式從API 14開始使用)

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes=""
    android:accessibilityFeedbackType=""
    android:accessibilityFlags=""
    android:canRequestEnhancedWebAccessibility=""
    android:canRequestFilterKeyEvents=""
    android:canRequestTouchExplorationMode=""
    android:canRetrieveWindowContent=""
    android:description=""
    android:notificationTimeout=""
    android:packageNames=""
    android:settingsActivity=""
    android:summary="" />

由上邊兩組程式碼可以看到,前一個配置中單個程式碼塊中,配置屬性有限,但靈活的在服務裡邊初始化,使我們可以隨時的改變需要的flag和type及一些其他引數。而後一個(Android 4.0+ / API 14+)的meta-data配置方式,可配置的屬性更多。在系統條件符合時(同時不需要更改我們輔助功能的一些引數時),還是推薦使用後一個配置方式進行輔助功能的配置。

這裡只是對所有的XML attributes進行說明。

XML attributes:

constantvalue描述
typeAllMaskffffffff所有型別的事件
typeAnnouncement4000一個應用產生一個通知事件
typeAssistReadingContext1000000輔助使用者讀取當前螢幕事件
typeContextClicked800000view中上下文點選事件
typeGestureDetectionEnd80000監測到的手勢事件完成
typeGestureDetectionStart40000開始手勢監測事件
typeNotificationStateChanged40收到notification彈出訊息事件
typeTouchExplorationGestureEnd400觸控瀏覽事件完成
typeTouchExplorationGestureStart200觸控瀏覽事件開始
typeTouchInteractionEnd200000使用者觸屏事件結束
typeTouchInteractionStart100000觸控式螢幕幕事件開始
typeViewAccessibilityFocusCleared10000無障礙焦點事件清除
typeViewAccessibilityFocused8000獲得無障礙的焦點事件
typeViewClicked1點選事件
typeViewFocused8view獲取到焦點事件
typeViewHoverEnter80一個view的懸停事件
typeViewHoverExit100一個view的懸停事件結束,懸停離開該view
typeViewLongClicked2view的長按事件
typeViewScrolled1000view的滾動事件,adapterview、scrollview
typeViewSelected4view選中,一般是具有選中屬性的view,例如adapter
typeViewTextChanged10edittext中文字發生改變的事件
typeViewTextSelectionChanged2000edittext文字選中發生改變事件
typeViewTextTraversedAtMovementGranularity20000UIanimator中在一個檢視文字中進行遍歷會產生這個事件,多個粒度遍歷文字。一般用於語音閱讀context
typeWindowContentChanged800視窗的內容發生變化,或者更具體的子樹根佈局變化事件
typeWindowStateChanged20新的彈出層導致的視窗變化(dialog、menu、popupwindow)
typeWindowsChanged400000螢幕上的視窗變化事件,需要API 21+
constantvalue描述
feedbackAllMaskffffffff取消所有的可用反饋方式
feedbackAudible4可聽見的(非語音反饋)
feedbackGeneric10通用反饋
feedbackHaptic2觸覺反饋(震動)
feedbackSpoken1語音反饋
feedbackVisual8視覺反饋
  • accessibilityFlags 輔助功能附加的標誌,多個使用 ' | '分隔
constantvalue描述
flagDefault1預設的配置
flagEnableAccessibilityVolume80這個標誌要求系統內所有的音訊通道,使用由STREAM_ACCESSIBILTY音量控制USAGE_ASSISTANCE_ACCESSIBILITY
flagIncludeNotImportantViews2表示可獲取到一些被表示為輔助功能無權獲取到的view
flagReportViewIds10使用該flag表示可獲取到view的ID
flagRequestAccessibilityButton100如果輔助功能可用,提供一個輔助功能按鈕在系統的導航欄 API 26+
flagRequestEnhancedWebAccessibility8此類擴充套件的目的是為WebView中呈現的內容提供更好的輔助功能支援。這種擴充套件的一個例子是從一個安全的來源注入JavaScript。如果至少有一個具有此標誌的輔助功能服務, 則系統將使能增強的web輔助功能。因此, 清除此標誌並不保證該裝置不會使能增強的web輔助功能, 因為可能有另一個使能的服務在使用它。
flagRequestFilterKeyEvents20能夠監聽到系統的物理按鍵
flagRequestFingerprintGestures200監聽系統的指紋手勢 API 26+
flagRequestTouchExplorationMode4系統進入觸控探索模式。出現一個滑鼠在使用者的介面
flagRetrieveInteractiveWindows40該標誌知識的輔助服務要訪問所有互動式視窗內容的系統,這個標誌沒有被設定時,服務不會收到TYPE_WINDOWS_CHANGE事件。
  • canRequestEnhancedWebAccessibility (boolean)
    輔助功能服務是否能夠請求WEB輔助增強的屬性。例如: 安裝指令碼以使應用程式內容更易於訪問。

  • canRequestFilterKeyEvents (boolean)
    輔助功能服務是否能夠請求過濾KeyEvent的屬性,是否可以請求KeyEvent事件流。,flagRequestFilterKeyEvents搭配使用

  • canRequestTouchExplorationMode (boolean)
    此屬性用於,能夠讓輔助功能服務通過手勢,來請求觸控瀏覽模式,其被觸控的項,將被朗讀出來,flagRequestTouchExplorationMode搭配使用

  • canRetrieveWindowContent (boolean)
    輔助功能服務是否能夠取回活動視窗內容的屬性。 與上邊的flagRetrieveInteractiveWindows搭配使用,無法在執行時更改此設定。

  • description
    輔助功能服務目的或行為的簡短描述。

  • notificationTimeout
    同一型別的兩個輔助功能事件傳送到服務的最短間隔(毫秒,兩個輔助功能事件之間的最小週期)

  • packageNames
    從此服務能接收到事件的軟體包名稱 (不適合所有軟體包)(多個軟體包用逗號分隔)。

  • settingsActivity
    允許使用者修改輔助功能的activity元件名稱

  • summary
    同description

2.1.4 實現自己的輔助功能類:

Public方法有:

Method描述
API 20
getRootInActiveWindow()獲取窗體中的節點資訊。 返回 AccessibilityNodeInfo
getServiceInfo()獲取本服務的配置資訊。 返回 AccessibilityServiceInfo
onAccessibilityEvent(AccessibilityEvent event)輔助功能事件(s)回撥方法
onBind(Intent intent)service的bind介面
onInterrupt()輔助功能中斷的回撥
performGlobalAction(int action)全域性的點選方法
setServiceInfo(AccessibilityServiceInfo info)設定該輔助功能服務的描述,修改輔助功能的配置。可通過搭配使用getServiceInfo()動態修改我們輔助功能的配置
API 21新增:
getWindows()返回視窗最上層的一個使用者可互動的視窗資訊節點 List<AccessibilityWindowInfo>
API 24新增:
disableSelf()關閉自己service的方法,在設定介面可以看到輔助功能狀態被關閉
getSoftKeyboardController()返回軟鍵盤控制器,可用於查詢和修改軟鍵盤顯示模式。
findFocus(int focus)找到具有指定焦點型別的檢視。搜尋在所有視窗中執行。注意:為了訪問Windows,您的服務必須通過在其元資料中設定AccessibilityService_canRetrieveWindowContent屬性來宣告檢索視窗內容的功能。有關詳細資訊,請參閱SERVICE_META_DATA。此外,服務必須選擇通過設定FLAG_RETRIEVE_INTERACTIVE_WINDOWS標誌來檢索互動式視窗。否則,搜尋將僅在活動視窗中執行。
getMagnificationController()返回放大控制器,可用於查詢和修改顯示放大的狀態。注意:為了控制放大倍數,您的服務必須通過在其元資料中設定AccessibilityService_canControlMagnification屬性來宣告該功能
dispatchGesture(GestureDescription ge, GestureResultCallback cb, Handler handler)將手勢傳送到觸控式螢幕。目前正在進行的任何手勢(無論是從使用者,本服務還是其他服務)將被取消。 手勢將被排程,就像在使用者直接在螢幕上執行的一樣,因此事件可能會受到諸如放大和觸控探索之類的功能的影響。
API 26新增:
getAccessibilityButtonController()返回系統導航區域中可訪問性按鈕的控制器。當設定FLAG_REQUEST_ACCESSIBILITY_BUTTON時,此例項可用於查詢輔助功能按鈕的狀態並註冊監聽器以進行互動和輔助功能按鈕的狀態更改。返回 AccessibilityButtonController
getFingerprintGestureController()獲取控制器的指紋手勢。此功能需要 AccessibilityServiceInfo#CAPABILITY_CAN_CAPTURE_FINGERPRINT_GESTURES。注意:在呼叫此方法之前,必須先啟動該服務。返回 FingerprintGestureController

這部分我們只針對API <= 20部分的程式碼,其中我們常用的方法有:

Method描述
getRootInActiveWindow()獲取窗體中的節點資訊 AccessibilityNodeInfo,如果需要使用該方法,我們需要在配置中申明 canRetrieveWindowContent,否則可能獲取不到窗體的節點資訊。AccessibilityNodeInfo
onAccessibilityEvent(AccessibilityEvent event)輔助功能事件(s)回撥方法,處理輔助功能內所有事件的回撥方法
performGlobalAction(int action)全域性的點選方法

在使用這些方法時,有三個需要我們瞭解的地方:

  • AccessibilityEvent 在輔助功能event回撥方法中,返回的引數。(官方API En)

  • AccessibilityNodeInfo 輔助功能中的節點物件(nodeInfo)。(官方API En)

  • performGlobalAction(action) 全域性事件傳送方法(物理按鍵的點選)

AccessibilityEvent

當用戶介面發生某些明顯的事件時,AccessibilityEvent代表的無障礙事件會被系統傳送,每一種事件型別是由該類暴露出的屬性子集表示其特徵的。在此類中為每一種事件型別定義了相應的常量。詳細的事件型別請參照AccessibilityEvent文件 Zh

Public Methods 公有方法:

Method描述
getRecord(int index)獲取給定索引下的記錄。
getRecordCount()獲取被包含在事件中的記錄數。
describeContents()描述被包含在 Parcelable 例項的編組表示中的特殊物件種類。例如,如果物件在 writeToParcel(Parcel, int)的輸出裡包括一個檔案描述符,該方法的返回值必須包括 CONTENTS_FILE_DESCRIPTOR 位。
getEventType()獲取事件型別。
setEventType(int eventType)設定事件型別。
eventTypeToString(int eventType)返回一個事件型別的字串表示。例如,TYPE_VIEW_CLICKED 的字串表示為“TYPE_VIEW_CLICKED”。
getAction()獲取觸發該事件的執行操作。
getContentChangeTypes()獲取由 TYPE_WINDOW_CONTENT_CHANGED 事件標識的改變型別的位掩碼。一個單一事件可能代表多種變化型別。
getEventTime()獲取該事件的傳送時間。
setEventTime(long eventTime)設定事件被髮送的時間。
getMovementGranularity()獲取遍歷的移動粒度。
setMovementGranularity(int granularity)設定遍歷的移動粒度。
getPackageName()獲取源的包名。
setPackageName(CharSequence packageName)設定源的包名。
initFromParcel(Parcel parcel)從 Parcel 建立一個新例項。
obtain(AccessibilityEvent event)如果可獲得,返回一個快取例項或建立一個新例項。返回例項從給定事件初始化。
obtain()如果可獲得,返回一個快取例項或例項化一個新的。
obtain(int eventType)如果可獲得,返回一個快取例項或例項化一個新的並設定它的型別屬性。
recycle()回收一個例項重複使用。
setAction(int action)設定觸發此事件的執行操作。
setContentChangeTypes(int changeTypes)設定由一個 TYPE_WINDOW_CONTENT_CHANGED 事件標識的節點樹改變的位掩碼。
toString()返回物件的字串表示。一般情況下,toString 方法返回一個“文字表示”該物件的字串。結果應該是一個簡潔但容易閱讀的資訊表示。建議所有子類重寫該方法。
writeToParcel(Parcel parcel, int flags)整組該物件到一個 Parcel。

AccessibilityNodeInfo

該類代表一個視窗內容節點和可以從源請求的操作。從 AccessibilityService的角度看,一個視窗內容被呈現為一個無障礙節點資訊樹,該樹可能與檢視層次一一對映,也可能不與檢視層次一一對映。換句話說,一個自定義檢視可靈活地將自己報告為一個無障礙節點資訊樹。一旦無障礙節點資訊被髮送給無障礙服務,該資訊將會是不可改變的,且呼叫狀態改變方法將會產生錯誤。AccessibilityNodeInfo文件 Zh

巢狀類:

常量:
使用nodeInfo.performAction(Int Action)performAction(Int Action, Bundle bundle)方法

Action釋義
ACTION_ACCESSIBILITY_FOCUS給節點新增無障礙焦點的操作。
ACTION_ARGUMENT_COLUMN_INT讓指定集合列在螢幕上可見的引數。
ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN當以一定粒度移動時,是否擴大選擇範圍或反之移除的引數。
ACTION_ARGUMENT_HTML_ELEMENT_STRING要移動到的下一個/上一個 HTML 元素的引數。
ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT當遍歷節點文字的時,使用哪種移動粒度的引數。。
ACTION_ARGUMENT_PROGRESS_VALUE指定要設定的進度值的引數。
ACTION_ARGUMENT_ROW_INT讓指定集合行在螢幕上可見的引數。
ACTION_ARGUMENT_SELECTION_END_INT指定選擇結束的引數。
ACTION_ARGUMENT_SELECTION_START_INT指定選擇起始的引數。
ACTION_CLEAR_ACCESSIBILITY_FOCUS清除節點無障礙焦點的操作。
ACTION_CLEAR_FOCUS清除節點輸入焦點的操作。
ACTION_CLEAR_SELECTION取消選擇節點的操作。
ACTION_CLICK在節點資訊上點選的操作.
ACTION_COLLAPSE摺疊一個可展開節點的操作。
ACTION_COPY將當前選擇拷貝到剪貼簿的操作。
ACTION_CUT剪貼當前選項並放置到剪貼簿的操作。
ACTION_DISMISS關閉一個可關閉節點的操作。
ACTION_EXPAND展開一個可展開節點的操作。
ACTION_FOCUS給節點新增輸入焦點的操作。
ACTION_LONG_CLICK在節點上點選長按的操作。
移動到給定型別的下一個 HTML 元素的操作。例如,移動到 BUTTON、INPUT、TABLE 等。
ACTION_PASTE貼上當前剪貼簿內容的操作。
ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY以給定移動粒度,請求去到該節點文字的上一個文字實體的操作。例如,移動到下一個字、詞等。
ACTION_PREVIOUS_HTML_ELEMENT移動到給定型別的上一個 HTML 元素的操作。例如,移動到BUTTON、INPUT、TABLE 等。
ACTION_SCROLL_BACKWARD向後滾動節點內容的操作。
ACTION_SCROLL_FORWARD向前滾動節點內容的操作
ACTION_SELECT選擇節點的操作。
ACTION_SET_TEXT設定節點文字的操作。在沒有引數的情況下執行該操作,使用 null 或者空CharSequence 將會清除文字。該操作也將會把游標放置到文字末尾
FOCUS_ACCESSIBILITY無障礙焦點。
FOCUS_INPUT輸入焦點。
MOVEMENT_GRANULARITY_CHARACTER以字元為移動粒度位,遍歷節點文字
MOVEMENT_GRANULARITY_LINE以行為移動粒度位,遍歷節點文字。
MOVEMENT_GRANULARITY_PAGE以頁為移動粒度位,遍歷節點文字。
MOVEMENT_GRANULARITY_PARAGRAPH以段為移動粒度位,遍歷節點文字。
MOVEMENT_GRANULARITY_WORD以字詞為移動粒度位,遍歷節點文字。

方法:

Public Method釋義
addAction(int action)為一個nodeInfo新增一個操作,在API 21時被棄用
addAction(AccessibilityNodeInfo.AccessibilityAction action)為一個nodeInfo新增一個操作,API 21
addChild(View root, int virtualDescendantId)新增一個虛擬子元素,作為給定根節點的子view
addChild(View child)新增一個子view節點。
canOpenPopup()獲取該節點是否可以開啟一個彈窗或對話方塊。
describeContents()描述被包含在可打包例項的封裝代表中的特殊物件的種類。
equals(Object object)標識是否有某些其他物件“等同於”該物件。
findAccessibilityNodeInfosByText(String text)使用文字找到 AccessibilityNodeInfo。
findAccessibilityNodeInfosByViewId(String viewId)使用完全合格檢視id的源名稱找到AccessibilityNodeInfo,完全合格 id 的樣式 如下“package:id/id_resource_name”。
findFocus(int focus)找到具有指定焦點型別的檢視。
focusSearch(int direction)在指定方向搜尋具有輸入焦點的最近的檢視。
getActionList()獲取可以在該節點上執行的操作。
getActions()該方法在 API 級別 21 被棄用。使用getActionList()代替。
getBoundsInParent(Rect outBounds)獲取父級座標中的節點邊界。
getBoundsInScreen(Rect outBounds)獲取螢幕座標中的節點邊界。
getChild(int index)獲取給定索引下的子元素。
getChildCount()獲取子元素的數目。
getClassName()獲取該節點來自的類。
getCollectionInfo()如果節點是個集合,獲取集合資訊。一個集合子集總是一個集合專案。
getCollectionItemInfo()如果節點是個集合專案,獲取集合專案資訊。一個集合專案總是一個集合的子集。
getContentDescription()獲取該節點的內容描述。
getExtras()獲取具有附加資料的可選 bundle。該包是閒時建立的且永不為 null。注意:為了避免衝突,推薦使用應用的包名作為關鍵字字首,如果從不同應用發出的相同關鍵詞有不同的含義,可能會導致無障礙服務困擾。
getInputType()獲取定義為 InputType 的源中的輸入型別。
getLabelFor()為無障礙目的,獲取作為標籤代表該檢視的節點資訊。 返回AccessibilityNodeInfo
getLabeledBy()為無障礙目的,獲取作為標籤代表該檢視的節點資訊。返回AccessibilityNodeInfo
getLiveRegion()獲取該節點的實時區域模式。一個實時區域是一個包含對使用者來說重要資訊的節點,且當其改變時應該告知使用者。例如,在一個登入介面,有一個呈現“密碼錯誤”通知的 TextView,該檢視應該使用ACCESSIBILITY_LIVE_REGION_POLITE模式被標記為一個實時區域。這是無障礙服務的責任,控制TYPE_WINDOW_CONTENT_CHANGED 事件標識實時區域節點和其子元素的變化。實時區域模式,或如果檢視不是個實時區域,返回ACCESSIBILITY_LIVE_REGION_NONE。
getMaxTextLength()返回該節點的最大文字長度。
getMovementGranularities()獲取遍歷該節點文字的移動粒度。
getPackageName()獲取該節點來自的包名。
getParent()獲取父級NodeInfo。
getRangeInfo()如果節點是個範圍,獲取範圍資訊AccessibilityNodeInfo.RangeInfo
getText()獲取該節點的文字。
getTextSelectionEnd()選擇文字的末尾,如果沒有選擇文字,返回游標位置,或如果不存在選擇文字和游標,返回-1。
getTextSelectionStart()選擇文字的起始,如果沒有選擇文字,返回游標位置,或如果不存在選擇文字和游標,返回-1。
getViewIdResourceName()獲取源檢視 id 的完全合格源名稱。
getWindowId()獲取該資訊來自的視窗的id。
hashCode()返回該物件的雜湊編碼值。多用於判斷node是否相同
isAccessibilityFocused()獲取該節點是否可被無障礙聚焦。
isCheckable()獲取該節點是否可選中。
isChecked()獲取該節點是否已選中。
isClickable()獲取該節點是否可點選。
isContentInvalid()獲取該節點的內容是否已失效。例如,一個日期資料格式不正確。
isDismissable()獲取該節點是否可被關閉。
isEditable()獲取該節點是否可編輯。
isEnabled()獲取該節點是否可用。
isFocusable()獲取該節點是否可獲取焦點。
isFocused()獲取該節點是否已被聚焦。
isLongClickable()獲取該節點是否可長按點選。
isMultiLine()獲取該節點是否是個多行可編輯文字。
isPassword()獲取該節點是否是個密碼。
isScrollable()獲取該節點是否可滾動。
isSelected()獲取該節點是否已被選擇。
isVisibleToUser()該節點是否對使用者可見。
obtain(View root, int virtualDescendantId)如果可獲得,返回一個快取例項,否則建立一個新的並設定源。 View:虛擬子樹的根。 int:虛擬後代的id。返回一個例項化的AccessibilityNodeInfo
obtain(AccessibilityNodeInfo info)如果可獲得,返回一個快取例項,或建立一個新的。返回的例項初始化自一個給定 root。
obtain()如果可獲得,返回一個快取例項,否則,返回一個新的。
obtain(View source)如果可獲得,返回一個快取例項,否則,返回一個新的並設定源。View:源檢視。
performAction(int action, Bundle arguments)在該節點上執行一個操作。int: 要執行的操作。Bundle:具有額外引數的包。返回一個布林值,表示是否成功執行操作。
performAction(int action)在該節點上執行一個操作。
recycle()返回一個例項重複使用,回收掉當前的例項。
refresh()重新整理檢視呈現的最新狀態資訊。返回一個布林值,表示是否重新整理成功。
removeAction(int action)該方法在 API 級別 21 被棄用。
setAccessibilityFocused(boolean focused)設定該節點可無障礙聚焦。
setBoundsInParent(Rect bounds)在父級座標中設定節點邊界。
setBoundsInScreen(Rect bounds)在螢幕座標中設定節點邊界。
setCanOpenPopup(boolean opensPopup)設定該節點可開啟一個彈窗或對話方塊。
setCheckable(boolean checkable)設定該節點可選中。
setChecked(boolean checked)設定該節點已選中。
setClassName(CharSequence className)設定該節點來自的類。
setClickable(boolean clickable)設定該節點可點選。
setCollectionInfo(AccessibilityNodeInfo.CollectionInfo collectionInfo)如果該節點是個集合,設定集合資訊。一個集合子集通常也是個集合專案。
setContentDescription(CharSequence contentDescription)設定該節點的內容描述。
setContentInvalid(boolean contentInvalid)設定節點內容無效。例如,一個日期資料格式不正確。
setContextClickable(boolean contextClickable)設定該節點文字可點選。
setDismissable(boolean dismissable)設定該節點可以被關閉。
setEditable(boolean editable)設定該節點可編輯。
setEnabled(boolean enabled)設定該節點可用。
setFocusable(boolean focusable)設定該節點可聚焦。
setFocused(boolean focused)設定該節點已聚焦。
setInputType(int inputType)設定源中的輸入型別為inputType
setLabelFor(View root, int virtualDescendantId)為無障礙目的,設定可作為標籤代表檢視的資訊。如果 virtualDescendantId 為NO_ID,根被設定為標籤。一個虛擬後代是一個虛構檢視,為無障礙目的,將自己報告為檢視層次的一部分。該操作可以使繪製複雜內容的自定義檢視,將自己報告為虛擬檢視樹,然後傳遞其邏輯結構。
setLabelFor(View labeled)為無障礙目的,設定可作為標籤代表檢視的資訊。
setLabeledBy(View label)為無障礙目的,設定可作為標籤代表檢視的資訊。
setLabeledBy(View root, int virtualDescendantId)為無障礙目的,設定可作為標籤代表檢視的資訊。如果 virtualDescendantId 為NO_ID,根被設定為標籤。一個虛擬後代是一個虛構檢視,為無障礙目的,將自己報告為檢視層次的一部分。該操作可以使繪製複雜內容的自定義檢視,將自己報告為虛擬檢視樹,然後傳遞其邏輯結構。
setLiveRegion(int mode)設定該節點的實時區域模式。int:實時區域模式,或如果檢視不是個實時區域時,為ACCESSIBILITY_LIVE_REGION_NONE。
setLongClickable(boolean longClickable)設定該節點可長按點選。
setMaxTextLength(int max)設定最大文字長度,或無限制時為-1。一般情況下,用來標識一個可編輯文字框有輸入字元數目限制。
getMovementGranularities()設定遍歷節點文字的移動粒度。
setMultiLine(boolean multiLine)設定該節點為一個多行可編輯文字。
setPackageName(CharSequence packageName)設定該節點來自的包。
setParent(View root, int virtualDescendantId)設定給定 root 虛擬後代的父元素。如果 virtualDescendantId 等於 NO_ID,該root 被設定為父級。一個虛擬後代是一個虛構檢視,為無障礙目的,將自己報告為檢視層次的一部分。該操作可以使繪製複雜內容的自定義檢視,將自己報告為虛擬檢視樹,然後傳遞其邏輯結構。
setParent(View parent)設定父元素。
setPassword(boolean password)設定該節點為密碼。
setRangeInfo(AccessibilityNodeInfo.RangeInfo rangeInfo)如果該節點是個範圍,設定範圍資訊。
setScrollable(boolean scrollable)設定該節點可滾動。
setSelected(boolean selected)設定該節點是否已選擇。
setSource(View source)設定資訊源。
setSource(View root, int virtualDescendantId)為給定 root 虛擬後代設定源。如果 virtualDescendantId 等於 NO_ID,該 root被設定為源。一個虛擬後代是一個虛構檢視,為無障礙目的,將自己報告為檢視層次的一部分。該操作可以使繪製複雜內容的自定義檢視,將自己報告為虛擬檢視樹,然後傳遞其邏輯結構。
setText(CharSequence text)設定該節點的文字。
setTextSelection(int start, int end)設定文字選擇的起始和結尾。
setViewIdResourceName(String viewIdResName)設定源檢視id的資源名稱
setVisibleToUser(boolean visibleToUser)設定該節點對使用者可見。
toString()返回一個代表物件的字串。 getClass().getName() + '@' + Integer.toHexString(hashCode())
writeToParcel(Parcel parcel, int flags)封裝該物件到一個包中。
API 21ADD
getError()API Level 21 獲取該節點的錯誤文字。
setError(CharSequence error)API Level 21 設定該節點的錯誤文字。
getWindow()API Level 21 獲取該節點所屬的視窗。AccessibilityWindowInfo
removeAction(AccessibilityNodeInfo.AccessibilityAction action)API Level 21 移除一個可以在該節點上執行的操作。如果該操作未被新增到該節點,呼叫該方法沒有任何效果。返回一個布林值表示是否成功移除操作。
removeChild(View root, int virtualDescendantId)API Level 21 移除給定根節點的一個虛擬子元素。如果子元素不是之前被新增到該節點的,呼叫該方法無效果。 返回一個布林值表示是否存在子元素。
removeChild(View child)API Level 21 移除一個子元素。如果子元素先前未被新增到該節點,呼叫該方法無效果。
API 22ADD
getTraversalAfter()API Level 22 當前節點被訪問後,獲取無障礙遍歷中的下一個節點。一個螢幕閱讀器必須在訪問該節點內容之前,訪問其他節點的內容。如果存在,返回後續節點,否則,返回 null。
getTraversalBefore()API Level 22 當前節點被訪問後,獲取無障礙遍歷中的上一個節點。螢幕閱讀器必須訪問該節點的資訊,在訪問先前節點內容之前。
setTraversalAfter(View view)API Level 22 設定無障礙遍歷中下一個被訪問的檢視。一個螢幕閱讀器在訪問該節點內容之前,必須訪問另一個節點的內容。
setTraversalAfter(View root, int virtualDescendantId)API Level 22設定無障礙遍歷中下一個被訪問的檢視。一個螢幕閱讀器在訪問該節點內容之前,必須訪問另一個節點的內容。如果 virtualDescendantId 等於 NO_ID,root被設定為前任。一個虛擬後代是一個虛構檢視,為無障礙目的,將自己報告為檢視層次的一部分。該操作可以使繪製複雜內容的自定義檢視,將自己報告為虛擬檢視樹,然後傳遞其邏輯結構。
setTraversalBefore(View view)API Level 22 設定無障礙遍歷中當前訪問節點的上一個檢視。一個螢幕閱讀器在訪問上一個節點之前,必須訪問該節點的內容。
setTraversalBefore(View root, int virtualDescendantId)API Level 22 設定無障礙遍歷中當前訪問節點的上一個檢視。一個螢幕閱讀器在訪問上一個節點之前,必須訪問該節點的內容。如果virtualDescendantId 等於 NO_ID,root被設定為後任。一個虛擬後代是一個虛構檢視,為無障礙目的,將自己報告為檢視層次的一部分。該操作可以使繪製複雜內容的自定義檢視,將自己報告為虛擬檢視樹,然後傳遞其邏輯結構。
API 23ADD
isContextClickable()API Level 23 獲取該節點是否上下文可點選。
API 24ADD
setImportantForAccessibility(boolean important)API Level 24 設定該節點對無障礙很重要。
setDrawingOrder(int drawingOrderInParent)API Level 24 設定該節點相關檢視的繪製順序。
getDrawingOrder()API Level 24。獲取該節點中檢視的相對繪製順序。繪製順序只決定於節點的父級,所以該索引只與其兄弟姐妹相關。在一些情景下,繪製順序是基本同步的,所以兩個兄弟姐妹的返回值相同是可能的。返回值會被跳過也是可能的。
isImportantForAccessibility()API Level 24 返回該節點是否來源於一個對無障礙相當重要的檢視。

performGlobalAction

Action釋義
GLOBAL_ACTION_BACK相當於點選物理按鍵返回
GLOBAL_ACTION_HOME相當於點選物理按鍵Home鍵
GLOBAL_ACTION_NOTIFICATIONS相當於下滑開啟通知
GLOBAL_ACTION_RECENTS相當於點選物理按鍵最近任務鍵
GLOBAL_ACTION_QUICK_SETTINGS開啟快速設定
GLOBAL_ACTION_POWER_DIALOG開啟長按電源鍵的彈框

2.1.5 自定義AccessibilityService類的常規使用

public class MyAccessibilityService extends AccessibilityService {

    @Override
    protected void onServiceConnected() {
        super.onServiceConnected();
    }

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        // 1.AccessibilityEvent中一些常用的使用方法
        // 這裡我們獲取到該輔助功能的事件型別
        // 事件型別請參照 2.1.3中AccessibilityEventTypes表
        int eventType = event.getEventType();
        // 輸出事件的字串type
        String typeStr = event.eventTypeToString(eventType);
        // 根據事件型別來分發我們的需要的操作,這裡以視窗變化為例
        if(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED == eventType){
            // 判斷我們的輔助功能是否在約定好的應用介面執行,以設定介面為例
            if("com.android.settings".equals(event.getPackageName()){
                // doSomeThing
            }
        } else if(AccessibilityEvent.TYPE_GESTURE_DETECTION_START == eventType) {
            // 在監測到手勢的時候
        } else {
            // 在完成操作時,可以關閉自己的服務,下次使用再次開啟。
            // API > = 24
            disableSelf();
        }

        // 2.通過event來遍歷我們的nodeInfo
        if (VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
            // 這裡使用getResource()方法其實是從AccessibilityEvent繼承的
            // AccessibilityRecord中抽取AccessibilityNodeInfo
            // 實際呼叫的是AccessibilityRecord中的方法,返回的是AccessibilityNodeInfo mSourceNode
            AccessibilityNodeInfo info = event.getSource();
        else
            info = getRootInActiveWindow();

        // 3.遍歷info中的子節點
        if (info.getChildCount() != 0){
            // 通過一個迴圈將info的子節點遍歷
            for (int i = 0; i < info.getChildCount(); i++) {
                // 獲取子節點中某個特定的node,這裡通過以下方法通過ID查詢
                List<AccessibilityNodeInfo> list = info.findAccessibilityNodeInfosByViewId("com.android" +".settings:id/xxxx");
                // 通過text查詢
                List<AccessibilityNodeInfo> list = info.findAccessibilityNodeInfosByText("xxxx");
                // 列印nodeinfo的資訊
                Log.e("InfoType: " + info.getClassName());
                Log.e("InfoText: " + info.getText());
                Log.e("InfoPkgName: " + info.getPackageName());
                Log.e("InfoViewId: " + info.getViewIdResourceName());
            }
        }

        // 4.為節點新增操作
        // 1) 首先獲取到我們的節點
        AccessibilityNodeInfo info = event.getSource();
        // 2) 通過查詢指定的ID、text來查詢一個系列的節點,返回一個list,需要判斷list.size()是否為空
        List<AccessibilityNodeInfo> list = info.findAccessibilityNodeInfosByViewId("pkgName." + "id");
        AccessibilityNodeInfo info = list.get(0);
        // 3) 為節點新增操作,點選事件(事件可參照AccessibilityNodeInfo表)
        info.performAction(AccessibilityNodeInfo.ACTION_CLICK);
        // 4) 新增node可用的action,給node新增一個可清除焦點的操作
        // 通過addAction(AccessibilityNodeInfo.AccessibilityAction action)
        info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_CLEAR_FOCUS);
        // 獲取該node上可用的action屬性list,可以用來檢視該node的屬性
        List<AccessibilityNodeInfo.AccessibilityAction> listAction = info.getActionList();

        // 5.獲取該節點上子節點個數
        int childCount = info.getChildCount();

        // 6.操作完成後,回收例項,返回一個下次可用的例項
        info.recycle();

        // 需要注意的是,在node.performAction之後,呼叫本地廣播的話,之後的globeAction不會起作用,例:
        sureStopNode.performAction(AccessibilityNodeInfo.ACTION_CLICK);
        sureStopNode.recycle();
        SystemMessage.getInstance().send(SystemMessage.ACTION_POWER_BOOSTER_NEXT);
        performGlobalAction();// 這句話是不起作用的

        // 7.輔助功能的一些適用場景:
        // 1)部分應用中獲取簡訊驗證碼(通過開啟輔助功能的方式獲取)
        //  - 監聽通知欄的訊息(typeNotificationStateChanged)
        //  - 彈出通知欄的時候獲取該通知的節點資訊(event.getSource())
        //  - 遍歷root節點,取得顯示資訊的文字Node(info.findAccessibilityNodeInfosByViewId("pkgName." + "id"))
        //  - 通過NodeInfo.getText()方法獲取到相應的文字資訊,並取出驗證碼
        //  - 取消掉通知欄彈出框
        //  - 獲取到要輸入的EditText獲取直接在本應用給需要填寫驗證碼的區域設定文字。

        // 2)部分應用中恢復APP的初始設定(清除APP的資料)
        //  - 跳轉到應用詳情,通過findAccessibilityNodeInfosByViewId查詢節點
        //  - 通過節點info.performAction(AccessibilityNodeInfo.ACTION_CLICK)點選清除
        //  - 彈出確定對話方塊,同上方式找到確定節點,點選後返回

        // 3)監測應用是否在前臺,APP的啟動(通過windowstatechange事件獲取到當前的event的pkgName)
        //  - 通過接收typeWindowStateChanged事件,獲取event的pkgName確定哪個應用啟動或者在最上層顯示(懸浮窗不適用)

        // 4)自動安裝與解除安裝軟體
        //  - 同3,尋找相應的節點,點選事件

        // 5)自動化UI測試

        // 6)最常見的搶紅包

        // 7)通過輔助功能開啟一些許可權(不需要使用者手動點選開啟了)
        //  - 在使用者確定需要開啟許可權時,自動跳轉,尋找相應的開關按鈕
        //  - 需要注意的是,部分開關(switch button, checkbox)可能沒有ID,需要通過info.getClassName()來判斷,屬於那種型別的view。
    }

    @Override
    public void onInterrupt() {
    }

}

需要重寫的一些方法:

  • onServiceConnected() 該方法在初始化輔助功能服務時呼叫,可以在這時做相應的初始化工作。
  • onAccessibilityEvent() 在獲取到指定的監聽事件時,通過回撥這個方法來進行對應的操作
  • onInterrupt() 系統想要中斷輔助功能時會呼叫該方法

當然service中的onUnbind()方法也同樣適用,在onUnbind()中釋放一些資源也是可以的。

Note: 這裡需要了解的是輔助功能最重要的就是手機機型的適配。由於各個廠商的系統不一樣,導致了我們在獲取一些節點時,需要的節點ID不同(通過ID獲取節點時一種比較高效的方式)。而我們一般通過Device Monitor中的Hierarchy View獲取節點資訊。同時在此方式下,很多手機沒法獲取到節點資訊,我們就需要使用新增flagReportViewIds進行手動遍歷介面節點的方式獲取nodeInfo。

以上是輔助功能常規使用以及使用場景的介紹。下面為輔助功能的註冊。

<service
    android:name=".我們自己繼承自AccessibilityService的類"
        android:label="@string/app_name"
        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/第二個輔助功能配置方式的檔案,存放在xml下"/>
</service>

2.2 API > 20 新增方法,介面(預覽版)使用探究:

2.2.1 AccessibilityService新增方法一覽:

API 21:

Method描述
getWindows()返回視窗最上層的一個使用者可互動的視窗資訊節點 List<AccessibilityWindowInfo>

獲取螢幕上的視窗。
返回一個有目標的使用者可以與之進行互動的視窗,而不是所有的視窗。

為了訪問Windows,服務必須通過在其元資料中設定AccessibilityService_canRetrieveWindowContent屬性來宣告檢索視窗內容的功能。此外,服務必須選擇通過設定FLAG_RETRIEVE_INTERACTIVE_WINDOWS標誌來檢索互動式視窗。

使用此方法來獲取我們螢幕上最上層的一個視窗根節點。

API 24:

Method描述
getSoftKeyboardController()返回軟鍵盤控制器,可用於查詢和修改軟鍵盤顯示模式。

返回AccessibilityService.SoftKeyboardController其中可以使用新增addOnShowModeChangedListener()的方式來進行對鍵盤的顯示隱藏狀態監聽,通過getShowMode()獲取到軟鍵盤的顯示隱藏狀態,setShowMode(int showMode)設定軟鍵盤的顯示隱藏狀態。

Method描述
disableSelf()關閉自己service的方法,在設定介面可以看到輔助功能狀態被關閉

通過在服務中呼叫該方法可以將自己的服務停止,同時在輔助功能開啟介面上的開關也會隨之關閉。

Method描述
dispatchGesture(GestureDescription gesture, AccessibilityService.GestureResultCallback callback, Handler handler)將手勢傳送到觸控式螢幕。

可以通過設定GestureDescription將手勢的軌跡顯示在觸控式螢幕上,同時可以通過GestureResultCallback回撥獲得事件的完成情況。

Method描述
findFocus(int focus)找到具有指定焦點型別的檢視。

這裡的焦點型別有FOCUS_INPUTFOCUS_ACCESSIBILITY分別代表了輸入焦點和無障礙重點。
FOCUS_ACCESSIBILITY這個焦點,是需要使用setAccessibilityFocused(boolean isFocus)方法進行設定過得焦點。

需要注意的是:如果要使用該方法的話,需要先設定AccessibilityService_canRetrieveWindowContent同時新增一個flag:FLAG_RETRIEVE_INTERACTIVE_WINDOWS

Method描述
getMagnificationController()返回放大控制器,可用於查詢和修改顯示放大的狀態。

返回:AccessibilityService.MagnificationController。為了控制放大倍數,輔助功能服務必須通過在其元資料中設定AccessibilityService_canControlMagnification屬性來宣告該功能。

API 26:

Method描述
getAccessibilityButtonController()系統導航區域內的輔助功能按鈕控制器

當設定FLAG_REQUEST_ACCESSIBILITY_BUTTON時,此類可用於查詢輔助功能按鈕的狀態並註冊回撥以進行互動,並對可訪問性按鈕進行狀態更改。

注意:此類和FLAG_REQUEST_ACCESSIBILITY_BUTTON不應該被用作通過AccessibilityService為使用者提供功能的唯一手段。 一些裝置實現可以選擇不提供軟體呈現的系統導航區域,使得該功能永久不可用。

注意:在支援輔助功能按鈕的裝置實現中,它可能始終不可用,例如前臺應用程式使用SYSTEM_UI_FLAG_HIDE_NAVIGATION時。 使用者還可以選擇將該按鈕分配給另一個可訪問性服務或功能。 在每種情況下,將呼叫註冊的AccessibilityButtonController.AccessibilityButtonCallback的onAvailabilityChanged(AccessibilityButtonController,boolean)方法來提供對可訪問性按鈕對註冊服務的可用性的更改的通知。

注意底部導航欄的小人:


image

下面是一個簡單的用例:

public class MyAccessibilityService extends AccessibilityService {

    @Override
    protected void onServiceConnected() {
        super.onServiceConnected();
        AccessibilityServiceInfo info = new AccessibilityServiceInfo();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            info.flags = AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;
        } else {
            info.flags = AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS;
        }
        info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
        info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
        info.notificationTimeout = 100;
        setServiceInfo(info);
    }

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            AccessibilityButtonController mAccessibilityButtonController = getAccessibilityButtonController();
            mAccessibilityButtonController.registerAccessibilityButtonCallback(new AccessibilityButtonController.AccessibilityButtonCallback() {
                @Override
                public void onClicked(AccessibilityButtonController controller) {
                    super.onClicked(controller);
                    // 底部導航欄中輔助功能按鈕點選事件回撥
                    Toast.makeText(MyAccessibilityService.this, "on Click", Toast.LENGTH_SHORT).show();
                }

                @Override
                public void onAvailabilityChanged(AccessibilityButtonController controller, boolean available) {
                    super.onAvailabilityChanged(controller, available);
                    // 輔助功能可用性改變的回撥。返回了輔助功能底部按鈕是否可用的布林值,和按鈕控制器
                    // available = true 表示該按鈕對本服務可用
                    // available = false 是由於裝置顯示了按鈕,或按鈕被分配到另一個服務或其他原因。
                }
            });
        }
    }

    @Override
    public void onInterrupt() {
    }
}

在開啟輔助功能之後,如果是O系統的話,在底部導航欄會出現一個輔助功能的小人。同時這個小人可以接收到點選事件,和輔助功能可用狀態變化。

Method描述
getFingerprintGestureController()返回指紋手勢控制器

只要裝置具有能夠檢測手勢的感測器,AccessibilityService可以捕獲在裝置的指紋感測器上執行的手勢。
使用時需要宣告CAPABILITY_CAN_CAPTURE_FINGERPRINT_GESTURES同時需要許可權USE_FINGERPRINT,由於沒有真機除錯該方法,待Google釋出正式版API後繼續研究

支援的四個事件:

常量描述
FINGERPRINT_GESTURE_SWIPE_DOWN指紋手勢下滑。
FINGERPRINT_GESTURE_SWIPE_LEFT指紋手勢左滑。
FINGERPRINT_GESTURE_SWIPE_RIGHT指紋手勢右滑。
FINGERPRINT_GESTURE_SWIPE_DOWN指紋手勢上滑。

公有方法

方法描述
isGestureDetectionAvailable()判斷手勢監測是否可用。
registerFingerprintGestureCallback(FingerprintGestureController.FingerprintGestureCallback callback, Handler handler)通過該方法註冊一個指紋手勢的回撥。
unregisterFingerprintGestureCallback(FingerprintGestureController.FingerprintGestureCallback callback)取消註冊。
registerFingerprintGestureCallback(FingerprintGestureController.FingerprintGestureCallback, Handler)取消註冊。
方法描述
onGestureDetected(int gesture)這裡獲取到相應的手勢。
onGestureDetectionAvailabilityChanged(boolean available)這裡返回了手勢監測是否可用。

2.2.1 AccessibilityNodeInfo新增方法一覽:

API 21

Method描述
getError()獲取該節點的錯誤文字。
setError(CharSequence error)設定該節點的錯誤文字。

以上兩個方法需要搭配使用。

Method描述
getWindow()獲取該節點所屬的視窗(根節點)

通過該方法,我們可以使用一個子節點來重新獲取到我們的根節點。在需要多次操作介面元素的情況下(同時沒有產生windowstatechange事件),我們可以不通過監聽事件來進行下一個節點的操作。

Method描述
addAction(AccessibilityNodeInfo.AccessibilityAction action)為一個nodeInfo新增一個操作,API 21
removeAction(AccessibilityNodeInfo.AccessibilityAction action)移除一個可以在該節點上執行的操作。
removeChild(View root, int virtualDescendantId)移除給定根節點的一個虛擬子元素。
removeChild(View child)移除一個子元素。

第一個addAction(AccessibilityNodeInfo.AccessibilityAction action)方法是API 21時替代API 20中的addAction(int action)方法。
提供了一個在輔助功能外操作節點的方法,該方法不能在輔助功能服務中呼叫,不然會報錯java.lang.IllegalStateException: Cannot perform this action on a sealed instance.

後三個方法使用的前提都是需要事先在節點中添加了操作,或者檢視,不然都不會起作用。要說明的是removeAction方法也需要在服務外呼叫,不然也要報錯

API 22

Method描述
getTraversalAfter()當前節點被訪問後,獲取無障礙遍歷中的下一個節點。
getTraversalBefore()當前節點被訪問後,獲取無障礙遍歷中的上一個節點。
setTraversalAfter(View view)設定無障礙遍歷中下一個被訪問的檢視。一個螢幕閱讀器在訪問該節點內容之前,必須訪問另一個節點的內容。如果 virtualDescendantId 等於 NO_ID,root被設定為前任。
setTraversalAfter(View root, int virtualDescendantId)設定無障礙遍歷中下一個被訪問的檢視。
setTraversalBefore(View view)設定無障礙遍歷中當前訪問節點的上一個檢視。

通過在view的xml屬性中新增android:accessibilityTraversalAfter="+id"方式來進行輔助功能遍歷時的順序後,通過本方法獲取下一個節點。

通過在view的xml屬性中新增android:accessibilityTraversalBefore="+id"方式來進行輔助功能遍歷時的順序,同上。

通過View.setAccessibilityTraversalBefore(+id)