Chapters

Hide chapters

Android Accessibility by Tutorials

First Edition · Android 11 · Kotlin 1.4 · AS 4.1

Before You Begin

Section 0: 2 chapters
Show chapters Hide chapters

Section I: Android Accessibility by Tutorials

Section 1: 13 chapters
Show chapters Hide chapters

Section II: Appendix

Section 2: 1 chapter
Show chapters Hide chapters

9. Understandable
Written by Tori Gonda

Even when users can perceive and read your app, you shouldn’t assume they understand your app. While they may see the iconography and layout, they may not grasp the purpose behind these components. And even though a user can see or hear text, you can’t assume that person will understand what it means. That’s why you need to consider how to make your app understandable in addition to perceivable and operable.

WCAG’s guidance for this third pillar is:

3. Understandable: Information and the operation of user interface must be understandable.

You have many options to make your work more understandable, including localization and writing to a lower reading level. Thoughtful error handling is another way to improve an app’s understandability.

You’ll work through some of these options using Taco Tuesday. Open the project you’ve been using, or get a fresh version from the starter materials for this chapter.

Increasing readability

A substantial part of making your app understandable is making sure your content is readable.

Guideline 3.1 Readable: Make text content readable and understandable.

Here are some of the ways you can make an app more readable:

  • Internationalization (or i18n) and localization (or l10n).
  • Avoiding long blocks of text.
  • Pairing icons with text.
  • Using different fonts or allowing user-selected fonts.
  • Avoiding unusual words.
  • Explaining abbreviations.

This section will look at each of these examples in more detail.

Internationalizing an app

It’s easier to understand an app when it uses your primary language. Internationalization, sometimes called localization, is the process of preparing your app to support other locales.

Translation is a significant part of the process, but there’s more to it. Internationalization also comprises:

  • Supporting right-to-left (RTL) layouts.
  • Watching for local symbols such as currency.
  • Adapting for the user’s date and time formats.

Localizing your app will make it more understandable to audiences around the world. Depending on your market, it could be a lucrative pursuit to internationalize your app.

Using string resources for translations

Android allows you to put all your strings in XML resource files. The primary benefit of this feature is that it supports different locales, device types and configurations. Before you send off an app for internationalization, you need to:

  1. Put all your strings in resource files.
  2. Provide translated alternatives for these strings.

Taco Tuesday mostly uses string resources, but the bottom navigation bar doesn’t. That’s going to result in some of your text displaying in English, even if you choose another language. You need to fix this before uploading any translations.

Open bottom_navigation_menu.xml; this is the resource where you define the text for the bottom navigation.

For each item, there is a title. Replace the titles as directed:

  • Discover with @string/bottom_bar_discover
  • Favorites with @string/bottom_bar_try_it
  • Settings with @string/bottom_bar_settings

These changes inform the bottom navigation to use the string resources so they can be translated more easily. Additionally, this change improves Taco Tuesday’s consistency — which you’ll explore more later.

In fragment_discover.xml, on the view with ID discovery_card_detail_button, replace:

android:text="View"

With:

android:text="@string/shared_details"

Now, the View button on the discover card also uses string resources.

A few other places don’t use string resources, and you’ll fix these later when you learn about consistency.

You’re ready to add the translations.

Adding translations

To localize Taco Tuesday, you need to add translated strings files. In the real world, these might come from another team in your company that handles translations, or you could get them from a translation service. Fortunately, Taco Tuesday translations are provided for you.

Look for the translated strings.xml files in the materials for this chapter, in the assets folder.

Strings assets in project materials.
Strings assets in project materials.

Move these values-b+ folders under res in Taco Tuesday.

Strings files in Android Studio.
Strings files in Android Studio.

If you set the device language to Spanish, Portuguese, Croatian or Dutch, the app will use that language.

Settings screen with various translations.
Settings screen with various translations.

The recipes themselves are not translated. You might recall that WCAG has different guidelines for user-generated content. TL;DR, in most cases, you don’t need to translate it.

Note: Some languages have lines that are much longer than the others. Now you have another reason to support flexibility in the sizing of TextViews, in addition to scaleability — you covered these in Chapter 4, “Perceivable — Layout & Labeling”.

Supporting right-to-left layouts

To support RTL layouts, you need to:

  1. Use start/end attributes in your layouts instead of left/right.
  2. Enable RTL in your AndroidManifest.

The layouts in Taco Tuesday are already using start/end attributes. However, you do need to enable RTL in your AndroidManifest.

Add the following attribute to the application tag in AndroidManifest.xml:

android:supportsRtl="true"

You can try out your app using RTL without changing your language or locale. Go to your device developer options in settings and look for Force RTL layout direction.

Developer options to force RTL.
Developer options to force RTL.

Notice that most of the controls have flipped to the other side of the screen.

Taco Tuesday forced RTL.
Taco Tuesday forced RTL.

Note: You probably noticed that some text elements are still left-justified. This is because the system is detecting the English text as a left-to-right (LTR) language. If these strings were written in an RTL language, they would right-justify.

Using local formats

People format values differently depending on where they live or what language they speak.

Dates, for example, are formatted differently around the world. In the United States, 10/12/2020 means October 12th, 2020 but it looks like December 10th, 2020 to people from other countries.

Most date libraries support formatting a date based on the user’s locale. For example, you can do this using Android’s DateFormat:

val dateFormat = DateFormat.getMediumDateFormat(context)
val string = dateFormat.format(date)

The above would give you a format such as Jan 3, 2000, with changes made depending on the locale. You can also use other methods from this class, such as getLongDateFormat(), to get something that looks like Monday, January 3, 2000.

Similarly, you can use Currency.getInstance() to get the correct currency for a user’s locale.

Taco Tuesday doesn’t have any dates or currencies, so you don’t need to make any changes in this section.

Avoiding long blocks of text

Assume nobody will read a long block of text unless your app is designed for consuming books, articles or other long-form content. Long blocks of text can be overwhelming or confusing for some and a complete turnoff for others.

Choose to show shorter bits of text whenever you can. You can break blocks into smaller slices, or you can hide most of it and allow the user to expand the text to read more. You can also edit it down to its pure essence.

In Taco Tuesday, there’s too much text on the swipeable cards on the main screen. If you’re flipping through while starving for tacos, you’re probably only really seeing the title. You don’t really need much more.

Recipe card with a lot of text.
Recipe card with a lot of text.

To make this screen more pleasant, you’ll make the image bigger and show less text.

Start by updating the image size. Open fragment_discover.xml and find the view with the ID discover_recipe_image. Change the value of the app:layout_constraintDimensionRatio to 2:1:

app:layout_constraintDimensionRatio="2:1"

You’ve changed the ratio of the image, and now it takes up more of the card.

Next, add some margin to the text in this card. Add the following to the view with ID discovery_card_recipe_description:

android:layout_margin="@dimen/space_normal"

This adds some nice space between the text and the edges of the card.

Then, bump the text size of the discovery_card_recipe_description view up to 12sp:

android:textSize="12sp"

Now, you have a more readable font size — 9sp is too small to be readable! This change also fixes an accessibility lint warning.

Build and run. It already looks so much nicer!

Recipe card with less text.
Recipe card with less text.

Adding a gradient

You can make another improvement: a gradient overlay on the text. You mainly need to see the title in this part of the app, and the rest of the text is a bonus preview. A thoughtfully placed gradient will direct the eyes to the image, title and description.

Add this view below the discovery_card_recipe_description view:

<View
 android:layout_width="0dp"
 android:layout_height="0dp"
 android:background="@drawable/recipe_preview_gradient"
 app:layout_constraintBottom_toBottomOf="@id/discovery_card_recipe_description"
 app:layout_constraintEnd_toEndOf="@id/discovery_card_recipe_description"
 app:layout_constraintStart_toStartOf="@id/discovery_card_recipe_description"
 app:layout_constraintTop_toTopOf="@id/discovery_card_recipe_description" />

Build and run again. You now have a nice gradient! It looks so much more polished.

Recipe card with gradient.
Recipe card with gradient.

Pairing icons with text

Icons’ meanings are not always precise. An icon could be new to a user, culturally specific or ambiguous. Be careful when choosing icons. They might not be as universal or explicit as you’d think.

Contextualizing icons can be tricky. You can show text alongside the icon, provide a legend someplace, include context during on-boarding or add a tooltip.

In Taco Tuesday, you’ll pair text with icons. The recipe list has icons that could be interpreted in many ways.

Ambiguous icons.
Ambiguous icons.

To add some text for these icons, head over to item_try_it_recipe.xml.

Add the following to the view with the ID item_recipe_discard:

android:text="@string/shared_discard"

This adds text to the discard button.

Similarly, add this to the view with ID item_recipe_details:

android:text="@string/shared_details"

Now the view details button also has some text.

Optionally, remove this attribute from item_recipe_discard for some symmetry:

app:iconGravity="end"

Now, both of the buttons have icons on the same side. That’s it! Build and run to see your changes.

Icons with text.
Icons with text.

Now it’s more clear what those buttons do.

Using different fonts

This book has already covered most of what you need to know about fonts. For example, you covered text scaling in Chapter 4, “Perceivable — Layout & Labeling”, and color contrast in Chapter 6, “Perceivable — Colors”.

Two final notes about this topic:

  1. Make sure you pick a legible font.
  2. If you support system fonts, expect the user to change the device’s font either for preference or to improve readability. Your app should handle this change well if you’re correctly handling scaling text.

Avoiding unusual words

When creating copy for an app, be careful with lesser-known words.

Success Criterion 3.1.3 Unusual Words: A mechanism is available for identifying specific definitions of words or phrases used in an unusual or restricted way, including idioms and jargon.

Level AAA

One way to comply with this criterion is to avoid words that might be hard to understand, such as advanced words, idioms and jargon. These words can be difficult for the user to understand or relevant to one culture but not to another.

When you can’t avoid these words, you need to define them so the user gets the full meaning.

In Taco Tuesday, there are a lot of puns that could be misunderstood.

Open up strings.xml and look at the array pop_up_options. These are the intermittent messages that display in the app.

Intermittent message.
Intermittent message.

Many of these are fun, cringe-worthy lines. It’s sad to think that some people might miss the meaning. They might not understand the wordplay, or it might not translate to their native language.

What can you do in this case? You could eliminate the puns in your app, but the puns are part of the app’s personality.

There’s no need to delete the puns; you can provide another way for the user to get meaning. You can allow them to turn “off” the puns, or you can give them help text.

You’ll add some text alternatives to the app. When you’re done, the user can tap the banner to read an explanation or a plainly worded version of the message.

Start by adding this string array to the English strings.xml:

<string-array name="pop_up_alternates">
 <item>Are you enjoying the app? Rate it now in the Play Store!</item>
 <item>Victoria just tried a new recipe. What recipe are you trying this week?</item>
 <item>Jenn rated her favorite recipe 1 minute ago. Why don\'t you help others by rating yours?</item>
 <item>Ellen made tacos on Tuesday 5 weeks in a row. Beat her streak!</item>
 <item>Gabriella created her own taco recipe. Congratulate her!</item>
 <item>How are you liking this app? Let\'s talk about it. Answer this five minute survey!</item>
 <item>Let us help you find your next Taco Tuesday recipe! Swipe right on your next find.</item>
 <item>Did you find a recipe you dislike? Not your type of taco? Leave a rating on the details screen.</item>
 <item>Sorry to be all up in your grill. Do you mind taking a second to rate the app?</item>
 <item>Spectacular! You saved your first recipe. Ready to get cooking?</item>
 <item>Having issues with the app? We can talk over the phone. Contact support now.</item>
 <item>Shop for tortillas and other ingredients now so you\'re not caught without them</item>
 <item>Are you hungry? Try one of our delicious taco recipes!</item>
 <item>Have some feedback? You can tell us. We won\'t tell anyone.</item>
 <item>Try tacos on a day other than Tuesday. Tacos every day!</item>
 <item>Let\'s not waste time. You\'re our favorite customer!</item>
 <item>Take a chance on that recipe you\'re not sure about. You might end up loving it!</item>
 <item>Do you have the best taco pun? Share it with us on our forum!</item>
 <item>Taco Throw Back Thursday time! Share the best taco recipe you\'ve ever made?</item>
 <item>Ready for Taco Tuesday? How about you use one of our tried and true recipes.</item>
</string-array>

These are the messages the user will see when they tap the banner. All of the puns are removed.

Next, open MainActivity.kt. The bottom of onCreate() handles the logic for this banner.

Move the logic to pick a random index to a reusable variable:

val randomIndex = Random.nextInt(options.size - 1)
val randomMessage = options[randomIndex]

Now you can reuse this index to get the alternate text.

Right below the line binding.mainBanner.text = randomMessage, add the following code:

// 1
binding.mainBanner.setOnClickListener {
 // 2
 AlertDialog.Builder(this@MainActivity)
   .setMessage(resources
    .getStringArray(R.array.pop_up_alternates)[randomIndex])
   .setPositiveButton(R.string.shared_dismiss, null)
   .show()
}

This does two things:

  1. Sets a click listener on the banner.
  2. Displays a dialog using the alternate message for that index.

Build and run. Tap on the banner to see the dialog with alternate text.

Dialog with alternate text.
Dialog with alternate text.

Explaining abbreviations

Similar to difficult words or phrases, abbreviations can be a source of confusion.

Success Criterion 3.1.4 Abbreviations: A mechanism for identifying the expanded form or meaning of abbreviations is available.

Have you ever run across an abbreviation that you had to look up? Maybe you felt out of touch with the hip and the young, or perhaps you felt a little foolish for having to ask. You don’t want your users to feel that way!

The first time you use an abbreviation, you should spell the whole thing out and put the acronym in parentheses after it. For example, consider how RTL was presented for the first time in this chapter:

Supporting right-to-left (RTL) layouts

If you’re unable to explain inline, you can take a similar approach as with the lesser-known words and give another way to uncover the meaning.

Creating predictability

The other part of making your app understandable is making it predictable.

Guideline 3.2 Predictable: Make web pages appear and operate in predictable ways.

When you use consistent patterns, your app becomes more comfortable to use. Users shouldn’t need to guess what will happen when they perform an action.

You can make your app more predictable by:

  • Not changing context unexpectedly.
  • Using consistent language.
  • Assisting users with preventing and handling errors.

Changing context

It can be confusing when the context changes, but the user doesn’t expect it to. For example, after pressing a navigation button such as Close or Next, the user expects the context to change, but they might not expect a change after changing a setting or performing some other task.

Success Criterion 3.2.2 On Input: Changing the setting of any user interface component does not automatically cause a change of context unless the user has been advised of the behavior before using the component.

Level A

Right now, when you check Made it on a recipe, the recipe closes. Open the app and try this out. It’s quite unexpected!

Made it check box.
Made it check box.

Two options to improve it are:

  1. Let the user know they are about to change contexts.
  2. Don’t change contexts.

You’ll implement the second option because it doesn’t make sense to close the view in this case—it feels like a bug to the user.

Open RecipeDetailFragment.kt. In showEditableFields(), delete this code:

recipeDetailMadeIt.setOnCheckedChangeListener { _, _ ->
 activity?.onBackPressed()
}

This removes the OnCheckedChangeListener so that the screen doesn’t close when the user takes this action.

Build and run. Find a recipe and check the Made recipe box. Be happy the view doesn’t close!

Navigating consistently

Navigation patterns should be consistent throughout the app. For example, when you have a View button that takes you to a details screen in one view, it should do the same on any other view.

Success Criterion 3.2.3 Consistent Navigation: Navigational mechanisms that are repeated on multiple Web pages within a set of Web pages occur in the same relative order each time they are repeated, unless a change is initiated by the user.

Level AA

Taco Tuesday’s navigation is already pretty consistent. Settings always takes you to Settings. Discover leads to the same screen.

Fortunately for you, there is one thing to improve: button labels are inconsistent.

Labeling consistently

If you have two buttons that submit a form in the same way, they should have the same label, so the user knows what it does. This best practice applies to other kinds of labels too.

Success Criterion 3.2.4 Consistent Identification: Components that have the same functionality within a set of Web pages are identified consistently.

Level AA

You’ll address the specific issue where sometimes you use the Try it and Save for later buttons for the same action. You have a similar problem with Discard and No thanks. You’ll correct this by changing the labels.

Start by opening fragment_discover.xml. On the button with the ID discover_button_discard, find this line:

android:text="No thanks"

And replace it with:

android:text="@string/shared_discard"

Similarly, on the button with ID discover_button_try, find this:

android:text="Save for later"

And replace it with:

android:text="@string/shared_try_it"

Now, these buttons use the same language as they do in the rest of the app. And if you change the string resource, you’ll change all the button labels at once.

Build and run to see this change.

Updated button text.
Updated button text.

Helping with errors

Misunderstandings happen when the user doesn’t understand what’s expected of them, and when there are errors that don’t make sense. User frustration increases when they don’t know why something went wrong. The more frustrated they are, the less likely they’ll keep using your app, which is why this WCAG guideline exists:

Guideline 3.3 Input Assistance: Help users avoid and correct mistakes.

You’ll explore two different ways to clarify errors in this section:

  1. Announce errors and solutions when they occur.
  2. Make error messages descriptive and actionable.
  3. Provide descriptions for inputs.

Announcing detailed errors

When there’s an error, you want to describe it, clarify if it’s related to a specific field or task and explain if and how the user can resolve it.

Success Criterion 3.3.1 Error Identification: If an input error is automatically detected, the item that is in error is identified and the error is described to the user in text.

Level A

Taco Tuesday throws an error when you “compliment the chef” on a recipe. It’s a hard-coded error. In your own app, this could be a network error or invalid state.

Error message.
Error message.

There are already a lot of great things about this error! The message informs the user that something went unexpectedly. It’s also contextual because it displays where the error happened.

Now try using TalkBack on this screen. Did you hear anything about this error? No.

There are two things that you can improve:

  1. Announce it when it displays. As is, someone using a screen reader might not know a new error exists.
  2. Be more descriptive: specify what went wrong and the next steps.

One way to solve this would be to use a Toast or Snackbar to display the error. Both components automatically work with TalkBack, which is helpful. However, these options take the error out of context, which isn’t as helpful.

Live regions, on the other hand, keep the error message inline with the context of the action AND make sure the screen reader announces when it displays.

Open RecipeDetailFragment.kt. In showRecipeDetails(), add this code right before the click listener for recipeDetailComplimentTheChef:

recipeDetailErrorView.accessibilityLiveRegion =
  ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE

You useaccessibilityLiveRegion to determine if changes to the text or content description of this view should be announced to the user.

Note: In general, you’ll use ACCESSIBILITY_LIVE_REGION_POLITE.

To disable, use ACCESSIBILITY_LIVE_REGION_NONE.

For cases where something is urgent enough to interrupt other announcements, use ACCESSIBILITY_LIVE_REGION_ASSERTIVE. Use this option sparingly.

Now, when the view displays, it will be announced. Build and run. Test out this button again and see that it reads the error when it shows up.

Announced error message.
Announced error message.

Writing clear error messages

You should write error messages for the user. They should help the user know:

  1. What went wrong.
  2. How they can fix it.

You should explain what failed in the error message when you know the issue. In the message, you can specify the offending action or show the message in the correct context.

The error could be related to the user’s action, which they can resolve by repeating the action correctly, or some issue that they can’t directly fix, like a loading error.

You should also give the user options for solving the issue. If it’s an internal server issue, the solution could be to come back later or retry the action. If it’s a validation error, tell them which requirement they missed in the most straightforward language possible.

Your exercise for this section is to make the “compliment the chef” error message actionable. You’ll ask the user to try again in a few minutes.

In strings.xml, add this to the end of the shared_generic_error_message string:

Please try again in a few minutes.

You don’t need to change the translations for this exercise.

Build and run so that you can see an actionable instruction when the error displays.

Announced error message with instruction.
Announced error message with instruction.

Providing input descriptions

When your app has a form or single input, you should clarify what to include in this field so the user can avoid causing errors.

Success Criterion 3.3.2 Labels or Instructions: Labels or instructions are provided when content requires user input.

Level A

It’s frustrating when you’re confused about what to enter. It’s more frustrating when you get an ambiguous error and don’t understand why it happened.

You can use a description, hint, tooltip or something else. If a date must be in the future when filling out a date, let the user know ahead of time and give them a descriptive error when the input is invalid.

Imagine if the “Notes” field on a saved recipe required at least five characters to save. You’d need to inform the user of that requirement up front. That way, users don’t try to save notes that are too short and then curse your app when it fails to save them.

Key points

  • Making sure your app is understandable is your responsibility.
  • Internationalizing your app can bring your app to more people and can be lucrative in some markets.
  • Avoiding long blocks of text improves the user’s comprehension.
  • Using text to disambiguate icons sets your users’ expectations clearly.
  • Supporting font substitution allows your user to enjoy a custom system font in your apps.
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2026 Kodeco Inc.