First of all, I appreciate all the work Google is putting together about accessibility. It's great to have more materials about Android Accessibility.
I'm an accessibility professional myself that have been working closely with Android applications. I'd like to share a few thoughts/problems about the subject of grouping content.
Focus navigation vs Accessibility Focus
Here's a quick definition before we continue:
-
Focus navigation: Similar to desktop keyboard navigation (tab, arrows keys, enter, esc). Keyboard or Directional Pad users. Focus is placed only on items that require user interaction (e.g. clickable views or buttons, inputs like EditText
, CheckBox
, Switch
, etc).
-
Accessibility Focus: Focus for accessibility services, primarily screen readers such as TalkBack. Focus is placed on all meaningful views of the screen, including non-focusable views such as TextView
or ImageView
, which has either text or content descriptions.
The problem
Google has been advocating a technique to group non-focusable items by using android:focusable="true"
to the parent ViewGroup
so that its contents are read all at once for screen reader users.
You can see this recommendation in one of the Google Code Labs courses.
While grouping content is good for screen reader users, it creates a problem for keyboard users.
The problem is that android:focusable
is meant for focus navigation and not accessibility focus. The consequences are:
- Views with no user interaction will now have focus via keyboard navigation. For example, focus on a
ViewGroup
that only has text views and no actions.
- These views will probably not have a focusable indicator (added as a StateListDrawable). Keyboard users won't be able to tell where the focus is at anymore. Even if we add a focusable indicator to these views, there is still no reason to have focus on TextViews for example.
Solutions
Unless if the statements above are wrong, then I recommend the following:
- Make it more evident the distinction between focus navigation and accessibility focus in Google's documentation and training material. I believe this distinction is only mentioned here in the notes.
- Suggest using android:importantForAccessibility instead of android:focusable. The only drawback is the API level since it was only introduced at API 16.
Focus behavior in a ViewGroup
Regardless of the issue mentioned above, there's an interesting thing about allowing a ViewGroup
to receive focus. Consider the following code:
<LinearLayout
android:orientation="vertical"
android:focusable="true"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:text="Heading One"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:text="Action"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:text="Heading Two"
android:background="@drawable/focusable"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
What's the focus behavior for focus navigation (Keyboard)?
- Focus moves to the
LinearLayout
.
- Focus moves to the
Button
.
This is the expected behavior since we've asked for the LinearLayout
to receive focus.
What's the accessibility focus behavior?
- Focus moves to the
LinearLayout
, announcing all its text content except for the Button
text: "Heading one, Heading Two".
- Focus moves to the
Button
, announcing the button: "Action, Button".
This creates a problem since the order here matters and the original order is different then the one being announced.
Solutions
There are two ways that developers have been solving this issue:
- Remove
android:focusable
from the container (no need to group text content if they are separated by focusable views) or...
- Add
android:focusable
to the two TextView
inside.
Removing android:focusable
from the container is not always an option and having focus on a container can actually be important. For example, a ViewPager
will announce that you are on a multi-page view, which page it is currently displaying, and allowing us to use gestures or keyboard arrows to switch pages. We still want to keep this behavior. ViewPager
is a very popular widget and it is quite common to find this problem on many Android apps.
Adding android:focusable
to the two TextView
is a solution but it makes non-focusable views now focusable to keyboard users (the issue mentioned at the beginning). For ViewPager
, developers must also provide at least an android:contentDescription
to the ViewPager
, otherwise the it won't gain focus since it has no description.
Adding android:importantForAccessibility="yes"
to the two TextView
doesn't help since they are already set to yes
by default. What dictates the accessibility focus behavior here is wether child views is focusable or not.
Luckily, we can set a view focusable only to accessibility services. To do this, we have to set to true
the setFocusable(boolean focusable) method in the AccessibilityNodeInfo using an accessibility delegate instead of the setFocusable(boolean focusable) from a View.
It would be better, though, if Android could provide something like android:accessibilityFocusable
. There could be other options like changing TalkBack behavior or fixing the most popular ViewGroup
s like ViewPager
, RecyclerView
, etc.
I'm looking for a feedback on this, see if any of my suggestions or assumptions are wrong, or even the possibility of Google to implement any of the suggestions.
Thanks!