主要介绍如开发作符合accessibility规范的应用,和开发自己的accessibility service。
一、
开发作符合accessibility规范的应用(
Making Applications Accessible
)
主要有以下三点:
1. Labeling User Interface Elements
这点很简单,如果你都用android自带的控件(像TextView, Button等),大部分事情系统都帮你做了。你只需要在一些没有文字提示的控件(像ImageButton,ImageView,CheckBox等),使用
android:contentDescription属性,增加文字提示内容,如下代码:
android:id=”@+id/add_note_button”
android:src=”@drawable/add_note”
android:contentDescription=”@string/add_note”/>
AccessibilityService可以通过调用getContentDescription来获取contentDescription的内容。
2. Enabling Focus Navigation
这点也很简单,就是要保证你所有需要操作的控件,都可以方向键导航来换取焦点。
3. Building Accessible Custom Views
如果使用自定义accessibility控件的话,就需要自己处理accessibility 事件。了解自定义accessibility控件的方法,有助于理解accessibility框架的原理。
自定义accessibility控件主要包含以下4点:
(1)
Handle directional controller clicks
在获取焦点的View,接收到ok键或回车键的时候,需要执行点击事件的行为。
(2)Implement accessibility API methods
主要有以下四个方法(第4点会更详细介绍):
dispatchPopulateAccessibilityEvent()
onPopulateAccessibilityEvent()
onInitializeAccessibilityEvent()
onInitializeAccessibilityNodeInfo()
(3)Send AccessibilityEvent objects specific to your custom view
比如说,在你定制的控件获取焦点的时候,调用View.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)等。
(4)Populate AccessibilityEvent and AccessibilityNodeInfo for your view
构建
AccessibilityEvent和
AccessibilityNodeInfo。这一点比较重要,因为如果我们开发accessibility service的时候,需要用到Accessibility和AccessibilityNodeInfo来获取accessibility 的消息。
先来看看
AccessibilityNodeInfo的构造。
看第(2)点的的四个函数。
onInitializeAccessibilityNodeInfo()用来初始化
AccessibilityNodeInfo。它会根据View的层次填充子View的
AccessibilityNodeInfo和父View的
AccessibilityNodeInfo。比如一个LinearLayout里面放置了两个Button,在回调LinearLayout的
onInitializeAccessibilityNodeInfo()的时候,就会给LinearLayout的
AccessibilityNodeInfo填充上两个子View的
AccessibilityNodeInfo。所以,你可以通过event.getSource()获取到一个和View的层次结构类似的树形
AccessibilityNodeInfo。
再来看看
AccessibilityEvent的构造。看第(2)点第三个函数。
onInitializeAccessibilityEvent()用来初始化AccessibilityEvent的一些属性,如设置className,设置是否被选中等等。可以去看api说明,获取完整的设置属性。除了属性之外,还有一个非常重要的内容,就是accessibility的提示消息。主要通过
dispatchPopulateAccessibilityEvent()和
onPopulateAccessibilityEvent()这两个函数来获取的。看下图:
这是
AccessibilityEvent.getText()的填充过程。getText()返回的是一个字符数组。可以看到,ViewGroup调用dispatchPopulateAccessibilityEvent之后,会触发自身的onPopulateAccessibilityEvent函数。在这个函数里,可以调用event.getText().add(String)的方法来填充数据。接下来就会遍历所有的子View,调用每个子View的
dispatchPopulateAccessibilityEvent方法,然后
dispatchPopulateAccessibilityEvent触发自身的
onPopulateAccessibilityEvent函数。同样,
在这个函数里,可以调用event.getText().add(String)的方法来填充数据。这边有一点要强调一下,ViewGroup和View的AccessibilityEvent是同一个event。这样子,你就可以获取ViewGroup及其所有子View的accessibility消息了。
二、开发自己的accessibility service(
Building Accessibility Services
).
主要有以下五点:
1.
Manifest Declarations and Permissions
看下面例子:
...
...
...
android:name
=".
MyAccessibilityService
"
android:label
="@string/
accessibility_service_label
"
android:permission
="
android.permission.BIND_ACCESSIBILITY_SERVICE
">
android:name
="
android.accessibilityservice.AccessibilityService
" />
android:name="android.accessibilityservice"
android:resource="@xml/accessibility_service_config"
/>
android:name
="
android.permission.BIND_ACCESSIBILITY_SERVICE
" />
红色字体是需要添加的权限和intent-filter,绿色字体是配置文件的信息。配置文件看以下例子:
xmlns:android
="http://schemas.android.com/apk/res/android"
android:description
="@string/
accessibility_service_description
“
android:canRequestFilterKeyEvents="true"
android:packageNames="com.example.android.apis"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFlags="flagDefault"
android:accessibilityFeedbackType="feedbackSpoken"
android:notificationTimeout="100"
android:canRetrieveWindowContent="true"
android:settingsActivity="com.example.android.accessibility.ServiceSettingsActivity"
/>
其中,红色字体是注册的包名和accessibility 事件。表明只接收 com.example.android.apis这个应用的所有accessibility时间。当然,这两个属性可以在运行时改变。其他属性可以去参考api文档。
2. Registering for Accessibility Events
如上面例子,可以通过包名和事件来过滤AccessibilityEvent.
3. AccessibilityService Methods
主要有以下四个方法:
onServiceConnected():可以用来初始化操作,如初始化TTS,设置accessibilityEvent的过滤条件等。
onAccessibilityEvent():接收你注册的accessibility事件。
onInterrupt():看以下官方解释:This method is called when the system wants to interrupt the feedback your service is providing, usually in response to a user action such as moving focus to a different control. This method may be called many times over the lifecycle of your service
onUnbind()。我的理解是,系统中断你的accessibility service回馈的时候调用。例如,你用TTS语言提示一个accessibility 消息的时候,还没提示完,又来的一个accessibility event,这时候,正在播报的事件就会被中断,就会回调这个函数。但是,在我实际开发过程中,没有用到这个函数。我开发的也是用TTS提示,需要中断的话,会调用TTS的Tts.speak(text,
TextToSpeech.QUEUE_FLUSH
, null,null),TTS自己会中断之前的提示,无需用到onInterrupt()方法。
onUnbind():在accessibility service关闭的时候调用。用来清除一些系统资源等。和onSerciceConnected相对应。
4. Getting Event Details
这是在系统回调
onAccessibilityEvent()的时候获取的,主要有两个数据:
AccessibilityEvent:在
onAccessibilityEvent()的参数列表里。参照之前自定义accessibility View的章节,调用event.getText()来获取accessibility消息,还可以调用其他方法获取包名类名等消息。具体方法参照文档。
AccessibilityNodeInfo:主要用来遍历View的AccessibilityNodeInfo信息。参考之前自定义accessibility View的章节,调用AccessibilityEvent.getSource()获取当前View的
AccessibilityNodeInfo
, 通过
AccessibilityNodeInfo.getChild(index)和
AccessibilityNodeInfo.
getParent()获取子View和父View的
AccessibilityNodeInfo。
5. Taking Action for Users
accessibility service也可以执行一些操作,如让View获取焦点等,通过调用
AccessibilityNodeInfo.
performAction()来实现。也可以执行一些全局的操作,如回到launcher等,通过调用
AccessibilityService.performGlobalAction()来实现。