-
Notifications
You must be signed in to change notification settings - Fork 55
Accessibility node, content description, and events
Views in Android have additional AccessibilityNodeInfo objects attached to them. These nodes contain additional view accessibility information that views expose to the accessibility framework. Accessibility nodes provide content descriptions with states and handle touch exploration and events.
Some of the accessibility states are supported by default and already have special content descriptions. For example, if a view is checkable, then TalkBack says "[not] checked [content description], double tap to toggle". What's interesting, Checkable
is an interface a view must explicitly implement to be checkable, while AccessibilityNodeInfo can be checkable by default, without any extra steps. To make a node checkable you need to overwrite two methods:
@Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
info.setChecked(checked); // set new state here
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setCheckable(true);
info.setChecked(checked); // set initial state here
}
Some of accessibility nodes' features are already handled by the View class. For example, to make a node clickable it's sufficient to call view.setClickable(true)
. There's no need to overwrite these methods.
Each view that is important for the user should have a content description, that can be used by accessibility tools, like TalkBack. Setting a content description is really easy - there's an XML attribute and a method for that. Every view that has a content description by default can be accessed using touch exploration. What's more important, basic views with text content already provide pretty good content descriptions, so there's no need to spend too much time on that. Some containers also provide a description of their content - for example, RecyclerView tells the user, how many items it presents.
Calling setContentDescription(description)
on a view will cause it to refresh. TalkBack will read the new content description automatically. An example with code can be found here.
The next important thing is to send an event telling the framework that the accessibility state of the view has changed. Some of the events are already handled by the View class. For example, calling performClick()
sends AccessibilityEvent.TYPE_VIEW_CLICKED
. The same works for setContentDescription(description)
. Internally this method calls notifyViewAccessibilityStateChangedIfNeeded(AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION)
, which is an equivalent of sending an event informing about content change. There's nothing more you need to do.
If an event is not sent automatically (which is not in case of the checkable state, for example), you need to send it manually. If the state was changed, you can use notifyViewAccessibilityStateChangedIfNeeded(eventType)
. If the state was not changed, but an action was done (for example, the view was clicked), you can use either sendAccessibilityEvent(eventType)
or sendAccessibilityEventUnchecked(event)
. The former checks if the accessibility manager is enabled and the latter does not.
The last thing needed to handle accessibility in simple, custom views is performing accessibility actions. Again, the standard actions, like clicking, are handled already by calling the default action handlers (performClick()
in case of ACTION_CLICK
). To provide your own action handling, override performAccessibilityAction(..)
:
override fun performAccessibilityAction(action: Int, arguments: Bundle?): Boolean {
if (action == AccessibilityNodeInfo.ACTION_CLICK) {
// handle accessibility clicks
}
return super.performAccessibilityAction(action, arguments)
}
Accessibility code can be delegated. By extending AccessibilityDelegate
and setting it to a view you can delegate all accessibility event dispatching and handling to another class.