「这是我参与11月更文挑战的第13天,活动详情查看:2021最后一次更文挑战」
收集信息
无障碍服务还提供了标准方法来收集和表示用户提供的信息的关键单元,如事件详细信息、文字和数字。
获取事件详细信息
Android 系统可通过AccessibilityEvent 对象向无障碍服务提供有关界面互动的信息。对于 Android 4.0 之前的版本,无障碍事件中的可用信息虽然可以提供有关用户所选界面控件的大量详细信息,但提供的上下文信息比较有限。在很多情况下,这种缺失的上下文信息可能对于理解所选控件的含义至关重要。
例如,对于日历或每日计划,上下文就至关重要。如果用户在周一至周五的日期列表中选择了下午 4:00 的时段,而无障碍服务读出“下午 4 点”,但未说明哪个工作日、一个月中的哪一天或哪个月份,则生成的反馈会令人感到困惑。在这种情况下,界面控件的上下文对于想要安排会议的用户来说至关重要。
Android 4.0 通过基于视图层次结构编写无障碍事件,极大地增加了无障碍服务可以获取的有关界面互动的信息量。视图层次结构是指包含该组件的一系列界面组件(其父级)和该组件可能包含的界面元素(其子级)。这样一来,Android 系统就可以提供有关无障碍事件的更多详细信息,从而使无障碍服务能够为用户提供更加实用的反馈。
无障碍服务可通过系统传递给服务的 onAccessibilityEvent() 回调方法的AccessibilityEvent 获取有关界面事件的信息。此对象提供了有关事件的详细信息,包括操作对象的类型、其描述性文字以及其他详细信息。从 Android 4.0 开始(之前的版本通过支持库中的AccessibilityEventCompat对象也能支持),您可以使用以下调用获取有关事件的其他信息:
AccessibilityEvent.getRecordCount() 和 getRecord(int) - 通过这些方法,您可以检索一系列 AccessibilityRecord 对象,这些对象对于系统传递给您的AccessibilityEvent 有帮助。此级别的详细信息为触发无障碍服务的事件提供了更多上下文。
AccessibilityEvent.getSource()- 此方法会返回一个 AccessibilityNodeInfo对象。此对象可让您请求生成了无障碍事件的组件的视图布局层次结构(父级和子级)。此功能允许无障碍服务调查事件的完整上下文,包括任何内含视图或子视图的内容和状态。
重要提示:从 AccessibilityEvent 调查视图层次结构可能会向无障碍服务公开私密用户信息。为此,服务必须通过在无障碍服务配置 XML 文件中添加 canRetrieveWindowContent 属性并将其设为 true 来请求该级别的访问权限。如果您未在服务配置 XML 文件中添加此设置,则对getSource()的调用将会失败。
注意:在 Android 4.1(API 级别 16)及更高版本中,getSource() 方法以及 AccessibilityNodeInfo.getChild()和 getParent() 只会返回被认为对无障碍功能而言重要的视图对象(绘制内容或响应用户操作的视图)。如果服务需要所有视图,则可以通过将服务的 AccessibilityServiceInfo实例的 [lags成员设为 FLAG_INCLUDE_NOT_IMPORTANT_VIEWS 来请求这些视图。
获取窗口更改详细信息
Android 9(API 级别 28)及更高版本允许应用在同时重新绘制多个窗口时跟踪窗口更新。发生TYPE_WINDOWS_CHANGED事件时,应使用 getWindowChanges()API 来确定窗口是如何更改的。在多窗口更新期间,每个窗口都会生成一系列自己的事件。getSource()方法会返回与每个事件关联的窗口的根视图。
如果某个应用为其 View对象定义了无障碍窗格标题,那么当该应用的界面更新时,服务就能分辨出来。发生 TYPE_WINDOW_STATE_CHANGED事件时,应使用 getContentChangeTypes()返回的类型来确定窗口是如何更改的。例如,当窗格有了新标题或者窗格消失时,框架可以检测到。
收集无障碍功能节点详细信息
此步骤是可选的,但非常有用。Android 平台可让 AccessibilityService查询视图层次结构、收集有关生成事件的界面组件及其父级和子级的信息。为此,请确保在 XML 配置中设置以下代码行:
android:canRetrieveWindowContent="true"
完成该操作后,使用 getSource()获取AccessibilityNodeInfo对象。只有在生成事件的窗口仍为活动窗口时,此调用才会返回对象。如果不是活动窗口,则会返回 null,并执行相应的操作。下面的示例显示了一个代码段,演示了在接收到事件时执行以下操作:
立即抓取生成事件的视图的父级
在该视图中,查找标签和复选框作为子视图
如果找到这些对象,则创建一个字符串来向用户报告,指明标签以及它是否被选中。
如果在遍历视图层次结构的过程中,在任意时间返回了 null 值,则该方法会静默放弃查找。
// Alternative onAccessibilityEvent, that uses AccessibilityNodeInfo\
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
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.checked);
} else {
completeStr = getString(R.string.not_checked);
}
String reportStr = taskLabel + completeStr;
speakToUser(reportStr);
}
现在,您已经拥有了一种可正常运行的完整无障碍服务。通过添加 Android 的文字转语音引擎或使用Vibrator来提供触感反馈。