ユーザー補助機能を必要とするユーザーをサポートするため、Android フレームワークでは、アプリのコンテンツをユーザーに提示したり、ユーザーに代わってアプリを操作したりできるユーザー補助サービスを作成できます。
Android では、次のようなシステムのユーザー補助サービスを提供しています。
- TalkBack: 視力の弱いユーザーや目の不自由なユーザーをサポートします。合成音声でコンテンツを知らせ、ユーザーの操作に応じてアプリでアクションを実行します。
- スイッチ アクセス: 運動障がいのあるユーザーをサポートします。インタラクティブな要素をハイライト表示し、ユーザーのボタン押下に応じてアクションを実行します。1 つまたは 2 つのボタンのみを使用してデバイスを制御できます。
ユーザー補助機能を必要とするユーザーがアプリを快適に利用できるように、このページで説明されているおすすめの方法に沿ったアプリを作成する必要があります。おすすめの方法は、アプリのユーザー補助機能を強化するで説明したガイドラインに基づいています。
要素にラベルを付ける
アプリ内の操作対象の 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 に設定します。そうすることで、ユーザー補助サービスが内部に含まれる要素のコンテンツの説明を、続けて一度に読み上げて紹介することができます。関連要素をこうして統合すると、支援技術のユーザーが画面上にある情報を効率よく発見できるようになります。
次のスニペットには互いに関連するコンテンツが含まれているので、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; } }
参考情報
アプリのユーザー補助機能を強化する方法について詳しくは、以下の参考情報をご覧ください。