Accessibility & Android & Staggered Grid Layout

Yehonatan Lavi
AndroidPub
Published in
5 min readMar 25, 2019

--

Accessibility allows people with disabilities to use the app you develop, while accessibility in the app works regularly, the user experience (UX) is better for those with disabilities.

According to the World Bank, 15% of the world’s population is disabled and dependent on a smartphone like any other person. With accessibility and tools such as TalkBack and Switch Access, they can communicate, learn and work.

In the Android operating system, in most cases, accessibility works as expected and doesn’t require large additions and changes. Although, there are cases, such as using Staggered Grid Layout, which doesn’t work as expected.

In this article I will bring some helpful functions and examples of how to use them to make your application more accessible and of course, as it appears in the title, accessibility solution for Staggered Grid Layout.

When I developed my app I’ve used Staggered Grid Layout to better present users with information and I expected the accessibility part to work as well as it works on Recycler View and Grid View. Unfortunately, it didn’t work well. Instead of the focus advancing in order, the focus jumped at some point to another item and then went back and jumped again and so on as can be seen in the GIF.

The actual order is: 1,3,2,5,6,7

See the code before the solution https://github.com/YoniBagi/StaggeredGridForAccessibility/tree/master

So I started to research a bit more about Staggered Grid Layout and when searching for sources, most of the guides retrieved the Pinterest app as an example. Unfortunately, when I enabled the accessibility on Pinterest they didn’t implement the Staggered Grid Layout part. The accessibility doesn’t work in order and even skips on some of the items. As can be seen in the GIF, meaning people with disabilities can’t use the app properly.

The accessibility works not in order and even on some of the items it skips

Eventually, I had to independently implement the accessibility algorithm in Staggered Grid Layout. In this article I would like to present my solution, but before that there are a number of accessibility functions which can help us.

Reading text

When we want some view to read a particular text we will use:

view.announceForAccessibility(benefitVariant.getSum() + “ benefits were selected”);

For example, I would like to read the following:

benefitVariant.getSum() + “ benefits were selected”

As a default, what happens is that only when the view gets focus, then it reads the same view. But what about clicking?

Using the above function, we can implement onClick() that will read the desired text.

Request accessibility focus for view

Sometimes the focus does not reach a particular view, so, by using the following function we can ask for focus on a specific view.

mCardView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);

Manage Focus View group

If we want to perform certain actions on different events we can implement the function of a specific view.

getViewPager().setAccessibilityDelegate(new View.AccessibilityDelegate());

And of course override the functions of the class “AccessibilityDelegate” to perform operations in various accessibility events.

RecylcerView has a special class to manage “RecyclerViewAccessibilityDelegate”

For example:

I would like that on any change of focus in the child, the recylcerView will scroll to the next item.

So we use the function “setAccessibilityDelegateCompat”.

recycler1.setAccessibilityDelegateCompat(new RVAccessibilityDelegate(mDataBinding.recycler1));

The class RVAccessibilityDelegate is just a class that extend the class RecyclerViewAccessibilityDelegate and overrides the method onRequestSendAccessibilityEvent.

Thus, every item that receives focus calls the above function and scrolls to the next position.

The solution of Accessibility in Staggered Grid Layout

The project is built with a single screen — one activity which has RecyclerView and the Layout Manager is set to StaggeredGridLayoutManager.

In addition, the project has an adapter class.

So let’s start with the XML, in the activity layout there is a simple RecyclerView.

In Main Activity layout have just RecyclerView

In Main Activity class have initialize of RecyclerView and Adapter.

The adapter’s layout, that is, of each item in the recyclerView is built from CardView, where there is ImageView and TextView:

And now to the main part, the Adapter class.

A general comment about the article: There is no reference to software architecture, and not to design patterns because it is not a scope of the article.

The first part has a data definition.

Photo list:

Text list:

View holder class:

And additional functions of recycler adapter:

At this moment if we run the application and enable the accessibility the transition between the items will not be in order.

The solution is theoretically to build the hierarchical accessibility, that is, define for every view, after which view it comes using setTraversalAfter function.

And now to the practical part:

Step 1

We hold HashMap’s int and view, in our case the view is CardView.

HashMap<Integer, CardView> mCardViewHashMap = new HashMap<>();

In this data structure we will maintain the position of a specific view and the view itself.

Step 2

In the onBindViewHolder function, each time the Recycler performs onBind, we will insert the position and the view as follows:

mCardViewHashMap.put(position, holder.mCardView);

Step 3

Use the function of the static class ViewCompat

What happens is that we define for each View (the first parameter of setAccessibilityDelegate) accessibility delegate.

In onInitializeAccessibilityNodeInfo function we get two parameters:

@param host The View hosting the delegate.* @param info The instance to initialize.

The first type of View is actually the View that hosts the Delegate, in this case our CardView.

The second parameter is the instance we want to initialize.

So, in the function we initialize the instance and define for each view after which view it’s coming in the accessibility flow:

info.setTraversalAfter(mCardViewHashMap.get(position — 1));

And of course we will have to wrap this part with condition if the HashMap is greater than 1 because we approach position minus one.

In the same way you could specify info.setTraversalBefore and adjust the logic.

The complete solution in Github

In conclusion, even if you didn’t need to solve the Staggered problem, I believe you learned a lot of useful things. We’ve got basic tools to work with accessibility, set up correctly and of course make our application accessible to people with disabilities.

--

--