編輯:關於Android編程
Accessibility是Android從API 4開始提供的一個功能,它主要目的是幫助一些因為有視覺,聽覺,身體障礙而無法完全使用觸摸屏或鈴聲等的用戶來使用Android的。而實際上現在很多開發者都用它來實現一些其他功能了,比如說微信搶紅包,自動安裝APK,強制停止應用等。下面來簡單介紹一下它的相關使用及原理。
它最主要的接口是類AccessibilityService。AccessibilityService是Service的子類,我們可以繼承這個類並實現它的抽象方法來監視一個應用的界面元素狀態的變化,比如focus變化,一個按鈕被click等等。當有這些變化的時候,系統會將這些信息封裝在AccessibilityEvent裡面,回調AccessibilityService的onAccessibilityEvent(AccessibilityEvent)方法。我們可以實現onAccessibilityEvent來處理這些AccessibilityEvent。下面看一步一步地使用示例:
這裡使用ApiDemo當中將文字轉換為語音的例子來介紹,這段代碼在/samples//ApiDemos/src/com/example/android/apis/accessibility/TaskBackService,如何使用ApiDemo可以參考Samples。下面是簡單介紹裡面的核心代碼:
/** * This class demonstrates how an accessibility service can query * window content to improve the feedback given to the user. */ public class TaskBackService extends AccessibilityService implements OnInitListener { /** Tag for logging. */ private static final String LOG_TAG = "TaskBackService/onAccessibilityEvent"; /** Comma separator. */ private static final String SEPARATOR = ", "; /** The class name of TaskListView - for simplicity we speak only its items. */ private static final String TASK_LIST_VIEW_CLASS_NAME = "com.example.android.apis.accessibility.TaskListView"; /** Flag whether Text-To-Speech is initialized. */ private boolean mTextToSpeechInitialized; /** Handle to the Text-To-Speech engine. */ private TextToSpeech mTts; @Override public void onServiceConnected() { // Initializes the Text-To-Speech engine as soon as the service is connected. mTts = new TextToSpeech(getApplicationContext(), this); } /** * Processes an AccessibilityEvent, by traversing the View's tree and * putting together a message to speak to the user. */ @Override public void onAccessibilityEvent(AccessibilityEvent event) { if (!mTextToSpeechInitialized) { Log.e(LOG_TAG, "Text-To-Speech engine not ready. Bailing out."); return; } // This AccessibilityNodeInfo represents the view that fired the // AccessibilityEvent. The following code will use it to traverse the // view hierarchy, using this node as a starting point. // // NOTE: Every method that returns an AccessibilityNodeInfo may return null, // because the explored window is in another process and the // corresponding View might be gone by the time your request reaches the // view hierarchy. AccessibilityNodeInfo source = event.getSource(); if (source == null) { return; } // Grab the parent of the view that fired the event. AccessibilityNodeInfo rowNode = getListItemNodeInfo(source); if (rowNode == null) { return; } // Using this parent, get references to both child nodes, the label and the checkbox. AccessibilityNodeInfo labelNode = rowNode.getChild(0); if (labelNode == null) { rowNode.recycle(); return; } AccessibilityNodeInfo completeNode = rowNode.getChild(1); if (completeNode == null) { rowNode.recycle(); return; } // Determine what the task is and whether or not it's complete, based on // the text inside the label, and the state of the check-box. if (rowNode.getChildCount() < 2 || !rowNode.getChild(1).isCheckable()) { rowNode.recycle(); return; } CharSequence taskLabel = labelNode.getText(); final boolean isComplete = completeNode.isChecked(); String completeStr = null; if (isComplete) { completeStr = getString(R.string.task_complete); } else { completeStr = getString(R.string.task_not_complete); } String taskStr = getString(R.string.task_complete_template, taskLabel, completeStr); StringBuilder utterance = new StringBuilder(taskStr); // The custom ListView added extra context to the event by adding an // AccessibilityRecord to it. Extract that from the event and read it. final int records = event.getRecordCount(); for (int i = 0; i < records; i++) { AccessibilityRecord record = event.getRecord(i); CharSequence contentDescription = record.getContentDescription(); if (!TextUtils.isEmpty(contentDescription )) { utterance.append(SEPARATOR); utterance.append(contentDescription); } } // Announce the utterance. mTts.speak(utterance.toString(), TextToSpeech.QUEUE_FLUSH, null); Log.d(LOG_TAG, utterance.toString()); } private AccessibilityNodeInfo getListItemNodeInfo(AccessibilityNodeInfo source) { AccessibilityNodeInfo current = source; while (true) { AccessibilityNodeInfo parent = current.getParent(); if (parent == null) { return null; } if (TASK_LIST_VIEW_CLASS_NAME.equals(parent.getClassName())) { //找到TaskListView return current; } // NOTE: Recycle the infos. 記得回收AccessibilityNodeInfo AccessibilityNodeInfo oldCurrent = current; current = parent; oldCurrent.recycle(); } } /** * {@inheritDoc} */ @Override public void onInterrupt() { /* do nothing */ }
上面這段代碼是對TaskList的輔助,可以看一下TaskListView的界面:
首先它跟普通的Service一樣,需要在Manifest文件中聲明:
與其他Service不同的是裡面有個元數據標志了配置資源—taskbackconfig。taskbackconfig裡面的內容如下:
其中packageNames限制了監視的包名,accessibilityEventType表示該AccessibilityService想要收到的Event事件類型。accessibilityFeedbackType表示提供的feedback類型,canRetrieveWindowContent是否可以獲取窗口的內容,description表示描述信息,在設置中輔助功能裡面允許該應用進行輔助設置時,會顯示在那個頁面下面。
Accessibility是能夠獲取到控件的消息,這些信息是從哪傳遞出來的呢?
事件是跟View相關的,它從View開始的,View實現了一個叫AccessibilityEventSource的接口,AccessibilityEventSource接口包含了兩個函數:
public void sendAccessibilityEvent(int eventType); public void sendAccessibilityEventUnchecked(AccessibilityEvent event);
這兩個函數會用來發送AccessibilityEvent,比如在View收到點擊事件時,當View的Focus狀態變化時等等,View調用這兩個接口來開啟發送AccessibilityEvent。
另外ViewGroup也實現了ViewParent接口,而ViewParent有一個接口函數requestSendAccessibilityEvent,在子View中,sendAccessibilityEvent方法最終會調用getParent().requestSendAccessibilityEvent,一層一層地往上調用,最終在ViewRootImpl裡面使用AccessibilityManager的
sendAccessibilityEvent方法binder機制將AccessibilityEvent發送給AccessibilityManagerService。
AccessibilityManagerService 管理核心
AccessibilityManagerService是在SystemServer中初始化的,並且添加到ServiceManager中。AccessibilityManagerService在有包變化的時候(安裝,卸載)更新AccessibilityService綁定,如下代碼所示:
private void updateServicesLocked(UserState userState) {
if (userState.mIsAccessibilityEnabled) {
manageServicesLocked(userState);
} else {
unbindAllServicesLocked(userState);
}
}
發送到AccessibilityService
在出現新的AccessibilityService時會去綁定AccessibilityService獲取綁定的結果IAccessibilityServiceClient(實際上是AccessibilityService中onBinder返回的IAccessibilityServiceClientWrapper的Proxy),跟AccessibilityService通信相關的信息保存在AccessibilityManagerService.Service類當中。當AccessibilityManagerService收到Event後,會遍歷所有的Service,並通過IAccessibilityServiceClient將Event發送給AccessibilityService。
從AccessibilityEvent事件產生到發送到AccessibilityService,整個流程就是這樣了。整個過程如下圖所示:
這裡就簡單介紹一下流程,有機會再好好分析一下源碼。
總結
AccessibilityService給我們提供了很多方便,我們可以利用AccessibilityService做很多巧妙的事情。使用AccessibilityService主要步驟就是繼承AccessibilityServcie服務,實現onAccessibilityEvent方法,配置好相關的內容,最後在AndroidMainfest聲明相關配置。知道AccessibilityService怎麼使用,最好也了解整個AccessibilityEvent事件流程,以能夠對AccessibilityService有整體把握。
前面一篇文章http://www.jb51.net/article/103036.htm介紹了alertDialog的四種簡單使用,但是有些時候為了讓整個app的風格統一
[TOC]剛好項目中要實現這個布局效果,做完後在這裡分享出來給大家學習~效果圖:實現的功能:1、單行多行切換顯示2、單選和取消選擇源碼/** * * @author j
我們今天要講的是Activity的四種launchMode。launchMode在多個Activity跳轉的過程中扮演著重要的角色,它可以決定是否生成新的Activity
在前面的博客中,小編介紹了Android的極光推送以及如何實現登錄的一個小demo,對於xml布局頁面,擺控件這塊的內容,小編還不是很熟練,今天小編主要簡單總結一下在An