Принципы улучшения доступности приложений

Чтобы помочь пользователям с потребностями в специальных возможностях, платформа Android позволяет создать службу специальных возможностей, которая может предоставлять пользователям контент из приложений, а также управлять приложениями от их имени.

Android предоставляет несколько служб доступности системы, в том числе следующие:

  • TalkBack : помогает людям со слабым зрением или слепым. Он объявляет контент посредством синтезированного голоса и выполняет действия в приложении в ответ на жесты пользователя.
  • Switch Access : помогает людям с двигательными нарушениями. Он выделяет интерактивные элементы и выполняет действия в ответ на нажатие пользователем кнопки. Это позволяет управлять устройством с помощью всего одной или двух кнопок.

Чтобы помочь людям с потребностями в специальных возможностях успешно использовать ваше приложение, ваше приложение должно следовать рекомендациям, описанным на этой странице и основанным на рекомендациях, описанных в разделе «Сделайте приложения более доступными» .

Каждый из этих рекомендаций, описанных в следующих разделах, может еще больше улучшить доступность вашего приложения:

Элементы этикетки
Пользователи должны иметь возможность понимать содержание и назначение каждого интерактивного и значимого элемента пользовательского интерфейса вашего приложения.
Добавить действия по обеспечению доступности
Добавляя действия по обеспечению доступности, вы можете позволить пользователям служб доступности выполнять важные пользовательские потоки в вашем приложении.
Расширение системных виджетов
Используйте элементы представлений, входящие в состав платформы, а не создавайте собственные представления. Классы представлений и виджетов платформы уже предоставляют большую часть специальных возможностей, необходимых вашему приложению.
Используйте сигналы, отличные от цвета
Пользователи должны иметь возможность четко различать категории элементов пользовательского интерфейса. Для этого используйте узоры и положение, а также цвет, чтобы выразить эти различия.
Сделайте медиаконтент более доступным
Добавьте описания к видео- или аудиоконтенту вашего приложения, чтобы пользователям, потребляющим этот контент, не приходилось полагаться исключительно на визуальные или звуковые сигналы.

Элементы этикетки

Важно предоставить пользователям полезные и описательные метки для каждого интерактивного элемента пользовательского интерфейса в вашем приложении. Каждая этикетка должна объяснять значение и назначение конкретного элемента. Программы чтения с экрана, такие как TalkBack, могут сообщать пользователям об этих метках.

В большинстве случаев вы указываете описание элемента пользовательского интерфейса в файле ресурсов макета, содержащем этот элемент. Обычно метки добавляются с помощью атрибута contentDescription , как описано в руководстве по повышению доступности приложений . Существует несколько других методов маркировки, описанных в следующих разделах.

Редактируемые элементы

При маркировке редактируемых элементов, таких как объекты EditText , полезно отображать текст, который представляет собой пример допустимого ввода, в самом элементе, а также делать этот пример текста доступным для программ чтения с экрана. В таких ситуациях вы можете использовать атрибут android:hint , как показано в следующем фрагменте:

<!-- The hint text for en-US locale would be
     "Apartment, suite, or building". -->
<EditText
   android:id="@+id/addressLine2"
   android:hint="@string/aptSuiteBuilding" ... />

В этой ситуации для объекта View атрибут android:labelFor должен иметь идентификатор элемента EditText . Более подробную информацию см. в следующем разделе.

Пары элементов, где один описывает другой

Обычно элемент EditText имеет соответствующий объект View , который описывает, что пользователи должны ввести в элемент EditText . Вы можете указать эту связь, установив атрибут android:labelFor объекта View .

Пример маркировки таких пар элементов приведен в следующем фрагменте:

!<-- Label text for en-US locale would be "Username:" --
>T<extView
   android:id="@+id/usernameLabel" ...
   android:text="@string/username"
   android:labelFor="@+id/usernameEntry" /
>
E<ditText
   android:id="@+id/usernameEntry" ... /
>
!<-- Label text for en-US locale would be "Password:" --
>T<extView
   android:id="@+id/passwordLabel" ...
   android:text="@string/password
   android:labelFor="@+id/passwordEntry" /
>
E<ditText
   android:id="@+id/passwordEntry"
   android:inputType="textPassword" ... /
>

Элементы в коллекции

При добавлении меток к элементам коллекции каждая метка должна быть уникальной. Таким образом, службы специальных возможностей системы могут ссылаться только на один экранный элемент при объявлении метки. Эта переписка позволяет пользователям узнать, когда они циклически перемещаются по пользовательскому интерфейсу или когда они перемещают фокус на элемент, который они уже обнаружили.

В частности, включайте дополнительный текст или контекстную информацию в элементы повторно используемых макетов, например в объекты RecyclerView , чтобы каждый дочерний элемент был однозначно идентифицирован.

Для этого установите описание содержимого как часть реализации адаптера, как показано в следующем фрагменте кода:

Котлин

data class MovieRating(val title: String, val starRating: Integer)

class MyMovieRatingsAdapter(private val myData: Array<MovieRating>):
        RecyclerView.Adapter<MyMovieRatingsAdapter.MyRatingViewHolder>() {

    class MyRatingViewHolder(val ratingView: ImageView) :
            RecyclerView.ViewHolder(ratingView)

    override fun onBindViewHolder(holder: MyRatingViewHolder, position: Int) {
        val ratingData = myData[position]
        holder.ratingView.contentDescription = "Movie ${position}: " +
                "${ratingData.title}, ${ratingData.starRating} stars"
    }
}

Ява

public class MovieRating {
    private String title;
    private int starRating;
    // ...
    public String getTitle() { return title; }
    public int getStarRating() { return starRating; }
}

public class MyMovieRatingsAdapter
        extends RecyclerView.Adapter<MyAdapter.MyRatingViewHolder> {
    private MovieRating[] myData;


    public static class MyRatingViewHolder extends RecyclerView.ViewHolder {
        public ImageView ratingView;
        public MyRatingViewHolder(ImageView iv) {
            super(iv);
            ratingView = iv;
        }
    }

    @Override
    public void onBindViewHolder(MyRatingViewHolder holder, int position) {
        MovieRating ratingData = myData[position];
        holder.ratingView.setContentDescription("Movie " + position + ": " +
                ratingData.getTitle() + ", " + ratingData.getStarRating() +
                " stars")
    }
}

Группы связанного контента

Если ваше приложение отображает несколько элементов пользовательского интерфейса, образующих естественную группу, например сведения о песне или атрибуты сообщения, расположите эти элементы в контейнере, который обычно является подклассом ViewGroup . Установите для атрибута android:screenReaderFocusable объекта контейнера значение true , а для атрибута android:focusable каждого внутреннего объекта — значение false . Таким образом, службы доступности могут представлять описания содержимого внутренних элементов одно за другим в одном объявлении. Такое объединение связанных элементов помогает пользователям вспомогательных технологий более эффективно находить информацию на экране.

Следующий фрагмент содержит части контента, которые связаны друг с другом, поэтому элемент контейнера, экземпляр ConstraintLayout , имеет атрибут android:screenReaderFocusable установленный в true , а каждый из внутренних элементов TextView имеет атрибут android:focusable установленный в false :

<!-- In response to a single user interaction, accessibility services announce
     both the title and the artist of the song. -->
<ConstraintLayout
    android:id="@+id/song_data_container" ...
    android:screenReaderFocusable="true">

    <TextView
        android:id="@+id/song_title" ...
        android:focusable="false"
        android:text="@string/my_song_title" />
    <TextView
        android:id="@+id/song_artist"
        android:focusable="false"
        android:text="@string/my_songwriter" />
</ConstraintLayout>

Поскольку службы доступности объявляют описания внутренних элементов в одном предложении, важно, чтобы каждое описание было как можно короче, но при этом передавало значение элемента.

Примечание. Как правило, вам следует избегать создания описания контента для группы путем агрегирования текста ее дочерних элементов. Это делает описание группы хрупким, и когда текст дочернего элемента изменяется, описание группы может больше не соответствовать видимому тексту.

В контексте списка или сетки программа чтения с экрана может объединить текст дочерних текстовых узлов списка или элемента сетки. Лучше избегать изменения этого объявления.

Вложенные группы

Если интерфейс вашего приложения предоставляет многомерную информацию, например ежедневный список фестивальных событий, используйте атрибут android:screenReaderFocusable во внутренних контейнерах группы. Эта схема маркировки обеспечивает хороший баланс между количеством объявлений, необходимых для обнаружения содержимого экрана, и длиной каждого объявления.

В следующем фрагменте кода показан один из методов маркировки групп внутри более крупных групп:

<!-- In response to a single user interaction, accessibility services
     announce the events for a single stage only. -->
<ConstraintLayout
    android:id="@+id/festival_event_table" ... >
    <ConstraintLayout
        android:id="@+id/stage_a_event_column"
        android:screenReaderFocusable="true">

        <!-- UI elements that describe the events on Stage A. -->

    </ConstraintLayout>
    <ConstraintLayout
        android:id="@+id/stage_b_event_column"
        android:screenReaderFocusable="true">

        <!-- UI elements that describe the events on Stage B. -->

    </ConstraintLayout>
</ConstraintLayout>

Заголовки внутри текста

Некоторые приложения используют заголовки для обобщения групп текста, появляющегося на экране. Если конкретный элемент View представляет заголовок, вы можете указать его назначение для служб специальных возможностей, установив для атрибута android:accessibilityHeading элемента значение true .

Пользователи служб специальных возможностей могут выбирать между заголовками, а не между абзацами или словами. Эта гибкость улучшает возможности навигации по тексту.

Заголовки панели специальных возможностей

В Android 9 (уровень API 28) и более поздних версиях вы можете предоставлять удобные заголовки для панелей экрана. В целях доступности панель представляет собой визуально отдельную часть окна, например содержимое фрагмента. Чтобы службы специальных возможностей могли понять поведение панели как окна, дайте панелям вашего приложения описательные заголовки. Службы специальных возможностей могут затем предоставлять пользователям более подробную информацию при изменении внешнего вида или содержимого панели.

Чтобы указать заголовок панели, используйте атрибут android:accessibilityPaneTitle , как показано в следующем фрагменте:

<!-- Accessibility services receive announcements about content changes
     that are scoped to either the "shopping cart view" section (top) or
     "browse items" section (bottom) -->
<MyShoppingCartView
     android:id="@+id/shoppingCartContainer"
     android:accessibilityPaneTitle="@string/shoppingCart" ... />

<MyShoppingBrowseView
     android:id="@+id/browseItemsContainer"
     android:accessibilityPaneTitle="@string/browseProducts" ... />

Декоративные элементы

Если элемент в вашем пользовательском интерфейсе существует только для визуального пространства или внешнего вида, установите для его атрибута android:importantForAccessibility значение "no" .

Добавить действия по обеспечению доступности

Важно позволить пользователям служб специальных возможностей легко выполнять все пользовательские действия в вашем приложении. Например, если пользователь может провести пальцем по элементу в списке, это действие также может быть доступно службам специальных возможностей, чтобы у пользователей был альтернативный способ завершить тот же пользовательский поток.

Сделайте все действия доступными

Пользователю TalkBack, Voice Access или Switch Access могут потребоваться альтернативные способы выполнения определенных пользовательских потоков в приложении. Для действий, связанных с жестами, такими как перетаскивание или пролистывание, ваше приложение может отображать действия таким образом, чтобы они были доступны пользователям служб специальных возможностей.

Используя действия доступности , приложение может предоставить пользователям альтернативные способы выполнения действия.

Например, если ваше приложение позволяет пользователям проводить по элементу, вы также можете предоставить эту функциональность с помощью специального действия по обеспечению специальных возможностей, например так:

Котлин

ViewCompat.addAccessibilityAction(
    // View to add accessibility action
    itemView,
    // Label surfaced to user by an accessibility service
    getText(R.id.archive)
) { _, _ ->
    // Same method executed when swiping on itemView
    archiveItem()
    true
}

Ява

ViewCompat.addAccessibilityAction(
    // View to add accessibility action
    itemView,
    // Label surfaced to user by an accessibility service
    getText(R.id.archive),
    (view, arguments) -> {
        // Same method executed when swiping on itemView
        archiveItem();
        return true;
    }
);

With the custom accessibility action implemented, users can access the action through the actions menu.

Make available actions understandable

When a view supports actions such as touch & hold, an accessibility service such as TalkBack announces it as "Double tap and hold to long press."

This generic announcement doesn't give the user any context about what a touch & hold action does.

To make this announcement more descriptive, you can replace the accessibility actions announcement like so:

Kotlin

ViewCompat.replaceAccessibilityAction(
    // View that contains touch & hold action
    itemView,
    AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_LONG_CLICK,
    // Announcement read by TalkBack to surface this action
    getText(R.string.favorite),
    null
)

Ява

ViewCompat.replaceAccessibilityAction(
    // View that contains touch & hold action
    itemView,
    AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_LONG_CLICK,
    // Announcement read by TalkBack to surface this action
    getText(R.string.favorite),
    null
);

This results in TalkBack announcing "Double tap and hold to favorite," helping users understand the purpose of the action.

Extend system widgets

Note: When you design your app's UI, use or extend system-provided widgets that are as far down Android's class hierarchy as possible. System-provided widgets that are far down the hierarchy already have most of the accessibility capabilities your app needs. It's easier to extend these system-provided widgets than to create your own from the more generic View, ViewCompat, Canvas, and CanvasCompat classes.

If you must extend View or Canvas directly, which might be necessary for a highly customized experience or a game level, see Make custom views more accessible.

This section uses the example of implementing a special type of Switch called TriSwitch while following best practices around extending system widgets. A TriSwitch object works similarly to a Switch object, except that each instance of TriSwitch allows the user to toggle among three possible states.

Extend from far down the class hierarchy

The Switch object inherits from several framework UI classes in its hierarchy:

View
 TextView
   Button
     CompoundButton
       Switch

Лучше всего, чтобы новый класс TriSwitch наследовался непосредственно от класса Switch . Таким образом, платформа специальных возможностей Android обеспечивает большинство возможностей доступности, необходимых классу TriSwitch :

  • Действия доступности: информация для системы о том, как службы доступности могут эмулировать каждый возможный пользовательский ввод, выполняемый над объектом TriSwitch . (Унаследовано от View .)
  • События доступности: информация для служб доступности обо всех возможных способах изменения внешнего вида объекта TriSwitch при обновлении или обновлении экрана. (Унаследовано от View .)
  • Характеристики: сведения о каждом объекте TriSwitch , например, содержимое любого текста, который он отображает. (Унаследовано от TextView .)
  • Информация о состоянии: описание текущего состояния объекта TriSwitch , например «проверено» или «не отмечено». (Унаследовано от CompoundButton .)
  • Текстовое описание состояния: текстовое объяснение того, что представляет собой каждое состояние. (Унаследовано от Switch .)

Такое поведение Switch и его суперклассов практически такое же, как и для объектов TriSwitch . Следовательно, ваша реализация может быть сосредоточена на расширении количества возможных состояний с двух до трех.

Определите пользовательские события

Расширяя системный виджет, вы, вероятно, меняете аспект взаимодействия пользователей с этим виджетом. Важно определить эти изменения взаимодействия, чтобы службы специальных возможностей могли обновлять виджет вашего приложения, как если бы пользователь напрямую взаимодействовал с виджетом.

Общая рекомендация заключается в том, что для каждого обратного вызова на основе представления, который вы переопределяете, вам также необходимо переопределить соответствующее действие доступности, переопределив ViewCompat.replaceAccessibilityAction() . В тестах вашего приложения вы можете проверить поведение этих переопределенных действий, вызвав ViewCompat.performAccessibilityAction() .

Как этот принцип может работать для объектов TriSwitch

В отличие от обычного объекта Switch , касание объекта TriSwitch переключает три возможных состояния. Поэтому соответствующее действие доступности ACTION_CLICK необходимо обновить:

Котлин

class TriSwitch(context: Context) : Switch(context) {
    // 0, 1, or 2
    var currentState: Int = 0
        private set

    init {
        updateAccessibilityActions()
    }

    private fun updateAccessibilityActions() {
        ViewCompat.replaceAccessibilityAction(this, ACTION_CLICK,
            action-label) {
            view, args -> moveToNextState()
        })
    }

    private fun moveToNextState() {
        currentState = (currentState + 1) % 3
    }
}

Ява

public class TriSwitch extends Switch {
    // 0, 1, or 2
    private int currentState;

    public int getCurrentState() {
        return currentState;
    }

    public TriSwitch() {
        updateAccessibilityActions();
    }

    private void updateAccessibilityActions() {
        ViewCompat.replaceAccessibilityAction(this, ACTION_CLICK,
            action-label, (view, args) -> moveToNextState());
    }

    private void moveToNextState() {
        currentState = (currentState + 1) % 3;
    }
}

Используйте сигналы, отличные от цвета

Чтобы помочь пользователям с нарушениями цветового зрения, используйте сигналы, отличные от цвета, чтобы различать элементы пользовательского интерфейса на экранах вашего приложения. Эти методы могут включать в себя использование различных форм и размеров, текстовые или визуальные шаблоны, а также добавление звуковой или сенсорной (тактильной) обратной связи для обозначения различий между элементами.

На рисунке 1 показаны две версии действия. В одной версии используется только цвет, чтобы различать два возможных действия в рабочем процессе. Другая версия использует передовой опыт включения фигур и текста в дополнение к цвету, чтобы подчеркнуть различия между двумя вариантами:

Рисунок 1. Примеры создания элементов пользовательского интерфейса с использованием только цвета (слева) и использования цвета, форм и текста (справа).

Сделайте медиаконтент более доступным

Если вы разрабатываете приложение, включающее мультимедийный контент, например видеоклип или аудиозапись, постарайтесь помочь пользователям с различными типами потребностей в специальных возможностях понять этот материал. В частности, мы рекомендуем вам сделать следующее:

  • Включите элементы управления, которые позволяют пользователям приостанавливать или останавливать воспроизведение мультимедиа, изменять громкость и переключать субтитры (подписи).
  • Если видео содержит информацию, важную для выполнения рабочего процесса, предоставьте тот же контент в альтернативном формате, например в виде стенограммы.

Дополнительные ресурсы

Чтобы узнать больше о том, как сделать ваше приложение более доступным, посетите следующие дополнительные ресурсы:

Кодлабы

Сообщения в блоге