アプリのユーザー補助機能の改善に関する原則

Android フレームワークでは、ユーザー補助を必要とするユーザーを支援するために、アプリのコンテンツをユーザーに提示し、ユーザーに代わってアプリを操作できるユーザー補助サービスを作成できます。

Android では、次のようなシステムのユーザー補助サービスを提供しています。

  • TalkBack: ロービジョンの方や目の見えない方に役立ちます。合成音声でコンテンツを読み上げ、ユーザーのジェスチャーに応じてアプリ上でアクションを実行します。
  • スイッチ アクセス: 運動障がいのあるユーザーをサポートします。インタラクティブな要素をハイライト表示し、ユーザーがボタンを押すとアクションを実行します。1 つまたは 2 つのボタンのみを使用してデバイスを制御できます。

ユーザー補助を必要とするユーザーがアプリを適切に使用できるようにするには、このページに記載されているおすすめの方法にアプリを準拠させる必要があります。これは、アプリのユーザー補助機能を強化するで説明されているガイドラインに基づいています。

以降のセクションで説明する以下の各おすすめの方法を実践することで、アプリのユーザー補助機能をさらに改善できます。

要素にラベルを付ける
アプリ内のインタラクティブで有意義な UI 要素のそれぞれについて、そのコンテンツと目的をユーザーが理解できるようにする必要があります。
ユーザー補助アクションを追加する
ユーザー補助アクションを追加することで、ユーザー補助サービスのユーザーがアプリ内で重要なユーザーフローを完了できるようになります。
システム ウィジェットを拡張する
独自のカスタムビューを作成するのではなく、フレームワークに含まれるビュー要素上に作成します。フレームワークのビュークラスとウィジェット クラスは、アプリに必要なほとんどのユーザー補助機能をすでに提供しています。
色以外の手がかりも取り入れる
ユーザーが UI 内の要素のカテゴリを明確に区別できるようにする必要があります。そのためには、色だけでなく、パターンや位置によって、こうした区別を表します。
メディア コンテンツのユーザー補助機能を強化する
アプリの動画コンテンツまたは音声コンテンツに説明を追加し、このコンテンツを消費するユーザーが、映像や音声の手がかりだけに頼らなくても済むようにします。

要素にラベルを付ける

アプリ内のインタラクティブな UI 要素ごとに、有用でわかりやすいラベルをユーザーに提供することが重要です。各ラベルは、特定の要素の意味と目的を説明する必要があります。TalkBack などのスクリーン リーダーで、これらのラベルをユーザーに読み上げることができます。

ほとんどの場合、UI 要素の説明は、その要素を含むレイアウト リソース ファイルに指定します。通常は、アプリのユーザー補助機能を強化するためのガイドで説明されているように、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 要素の ID を設定する必要があります。詳しくは次のセクションをご覧ください。

一方が他方の説明をする要素のペア

EditText 要素に、ユーザーが EditText 要素に入力する必要がある内容を記述する View オブジェクトを含めるのが一般的です。この関係を指定するには、View オブジェクトの android:labelFor 属性を設定します。

このような要素ペアのラベル付けの例を、次のスニペットに示します。


<!-- Label text for en-US locale would be "Username:" -->
<TextView
   android:id="@+id/usernameLabel" ...
   android:text="@string/username"
   android:labelFor="@+id/usernameEntry" />

<EditText
   android:id="@+id/usernameEntry" ... />

<!-- Label text for en-US locale would be "Password:" -->
<TextView
   android:id="@+id/passwordLabel" ...
   android:text="@string/password
   android:labelFor="@+id/passwordEntry" />

<EditText
   android:id="@+id/passwordEntry"
   android:inputType="textPassword" ... />

コレクション内の要素

コレクションの要素にラベルを追加する際は、各ラベルが一意である必要があります。それにより、システムのユーザー補助サービスは、ラベルを読み上げるときに画面上の要素を 1 つだけ参照できます。この対応により、ユーザーは UI 間を移動するときや、すでに検出している要素にフォーカスを移動したときに、認識できます。

特に、RecyclerView オブジェクトのように、再利用するレイアウト内の要素に追加のテキストまたはコンテキスト情報を追加して、各子要素が一意に識別されるようにします。

そのためには、次のコード スニペットに示すように、コンテンツの説明をアダプター実装の一部として設定します。

Kotlin

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"
    }
}

Java

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")
    }
}

関連コンテンツのグループ

曲の詳細やメッセージの属性など、自然なグループを構成する複数の UI 要素をアプリで表示する場合、通常 ViewGroup のサブクラスであるコンテナ内にそれらの要素を配置します。このコンテナ オブジェクトの android:screenReaderFocusable 属性を true に設定し、各内部オブジェクトの android:focusable 属性を false に設定します。このようにして、ユーザー補助サービスは内部要素のコンテンツ説明を 1 つのお知らせで順番に提示できます。関連する要素をこのように統合することで、支援技術のユーザーは画面上の情報をより効率的に見つけることができます。

次のスニペットには相互に関連するコンテンツが含まれているため、コンテナ要素(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" ... />

装飾的要素

UI 内のある要素が視覚的な間隔をあけるため、または見た目のためにのみ存在する場合、その android:importantForAccessibility 属性を "no" に設定してください。

ユーザー補助アクションを追加する

ユーザー補助サービスのユーザーが、アプリ内ですべてのユーザーフローを簡単に実行できるようにすることが重要です。たとえば、ユーザーがリスト内のアイテムをスワイプできる場合に、この操作をユーザー補助サービスにも公開することで、ユーザーは別の方法で同じユーザーフローを完了できるようになります。

すべてのアクションにアクセスできるようにする

TalkBack、Voice Access、スイッチ アクセスのユーザーが、アプリ内で特定のユーザーフローを完了するために、別の方法が必要になる場合があります。ドラッグ&ドロップやスワイプなどのジェスチャーに関連するアクションについては、ユーザー補助サービスのユーザーがアクセス可能な方法で、アプリでアクションを公開できます。

アプリはユーザー補助アクションを使用して、ユーザーが操作を完了するための代替方法を提供できます。

たとえば、アプリでユーザーがアイテムをスワイプできるようにする場合は、次のようなカスタムのユーザー補助アクションでも機能を公開できます。

Kotlin

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
}

Java

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 action’s 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
)

Java

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 オブジェクトの場合とほぼ同じです。したがって、実装では、取り得る状態の数を 2 から 3 に増やすことに重点を置くことができます。

カスタム イベントを定義する

システム ウィジェットを拡張すると、ユーザーによるそのウィジェットの操作方法について変更が生じる可能性が高まります。ユーザーがウィジェットを直接操作しているかのようにユーザー補助サービスがアプリのウィジェットを更新できるように、このような操作の変更を定義することが重要です。

一般的なガイドラインでは、オーバーライドするビューベースのコールバックごとに、ViewCompat.replaceAccessibilityAction() をオーバーライドして、対応するユーザー補助アクションを再定義する必要もあります。アプリのテストでは、ViewCompat.performAccessibilityAction() を呼び出して、このような再定義したアクションの動作を検証できます。

TriSwitch オブジェクトに対するこの原則の仕組み

通常の Switch オブジェクトとは異なり、TriSwitch オブジェクトをタップすると、3 つの状態が循環します。したがって、対応する ACTION_CLICK のユーザー補助アクションを更新する必要があります。

Kotlin

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
    }
}

Java

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;
    }
}

色以外の手がかりも取り入れる

色覚障がいのあるユーザーをサポートするには、アプリ画面内の UI 要素を区別するのに、色以外の手がかりを使用してください。たとえば、さまざまな形状やサイズを使用する、テキストや視覚的なパターンを提供する、音声フィードバックやタップフィードバック(触覚)を追加して要素の違いを示す、などの手法があります。

図 1 に、あるアクティビティの 2 つのバージョンを示します。一方のバージョンでは、ワークフロー内の可能な 2 つのアクションを、色だけで区別しています。もう一方のバージョンでは、おすすめの方法を取り入れて、色だけでなく、形とテキストによって、2 つのアクションの違いを強調しています。

図 1. 色のみを使用した UI 要素の作成(左)と、色、シェイプ、テキストを使用した UI 要素の作成(右)の例。

メディア コンテンツのユーザー補助機能を強化する

動画クリップや音声録音などのメディア コンテンツを含むアプリを開発する場合、そうしたコンテンツを把握する際のさまざまなユーザー補助のニーズに応じるサポートを試みてください。具体的には、以下を行うことをおすすめします。

  • メディアの一時停止や停止、音量の変更、字幕(字幕)の切り替えをユーザーが行えるようにするコントロールを含めます。
  • ワークフローの完了に不可欠な情報が動画で提供される場合は、文字起こしなどの別の形式で同じコンテンツを提供してください。

参考情報

アプリのユーザー補助機能を強化する方法について詳しくは、以下の参考情報をご覧ください。

Codelab

ブログ投稿