アプリでカスタムビュー コンポーネントが必要な場合、そのビューのユーザー補助機能を強化するために、追加の作業が必要になります。カスタムビューのユーザー補助機能を強化する主な作業は次のとおりです。
- 方向コントローラのクリックを処理する
- Accessibility API のメソッドを実装する
- カスタムビュー固有の
AccessibilityEvent
オブジェクトを送信する - ビューの
AccessibilityEvent
とAccessibilityNodeInfo
を実装する
方向コントローラのクリックを処理する
ほとんどのデバイスでは、方向コントローラを使用してビューをクリックすると、現在フォーカスのあるビューに KeyEvent
と KEYCODE_DPAD_CENTER
が送信されます。KEYCODE_DPAD_CENTER
はすでに、Android のすべての標準ビューで適切に処理されます。カスタム View
コントロールを作成する場合は、このイベントの効果が、タッチスクリーン上でのビューのタップと同じになるようにしてください。
また、カスタム コントロールでは KEYCODE_ENTER
イベントを KEYCODE_DPAD_CENTER
と同じように扱う必要があります。この方法により、フルキーボードからの操作が、ユーザーにとってかなり簡単になります。
Accessibility API のメソッドを実装する
ユーザー補助イベントは、アプリ内の視覚的インターフェース コンポーネントに対するユーザーの操作に関する通知です。こうした通知を扱うのがユーザー補助サービスで、そのイベントの情報を使用して補足説明のフィードバックやプロンプトを出力します。Android 4.0(API レベル 14)以上では、ユーザー補助イベントを生成するメソッドが拡張され、Android 1.6(API レベル 4)で導入されていた AccessibilityEventSource
インターフェースよりも詳しい情報が提供されるようになりました。拡張されたユーザー補助メソッドは、View
クラスだけでなく、View.AccessibilityDelegate
クラスにも含まれます。以下のようなメソッドがあります。
sendAccessibilityEvent()
- (API レベル 4)ユーザーがビューに対してアクションを行うと、このメソッドが呼び出されます。そのイベントは、
TYPE_VIEW_CLICKED
のようなユーザー アクション タイプによって分類されます。カスタムビューを作成しない場合、通常、このメソッドを実装する必要はありません。 sendAccessibilityEventUnchecked()
- (API レベル 4)このメソッドは、ユーザー補助をデバイス上で有効にするチェックボックス(
AccessibilityManager.isEnabled()
)を、呼び出し側のコードが直接コントロールする必要があるときに使用されます。このメソッドを実装する場合、実際のシステム設定にかかわらず、ユーザー補助が有効であるかのように呼び出しを行う必要があります。通常、カスタムビューにこのメソッドを実装する必要はありません。 dispatchPopulateAccessibilityEvent()
- (API レベル 4)カスタムビューがユーザー補助イベントを生成すると、システムがこのメソッドを呼び出します。API レベル 14 でのこのメソッドのデフォルト実装では、このビューに対して
onPopulateAccessibilityEvent()
を呼び出してから、このビューの各子要素に対してdispatchPopulateAccessibilityEvent()
メソッドを呼び出します。Android 4.0(API レベル 14)より前のリビジョンでユーザー補助サービスをサポートするには、このメソッドをオーバーライドし、カスタムビューについての説明テキストをgetText()
に入力する必要があります。このテキストは、TalkBack などのユーザー補助サービスで読み上げられます。 onPopulateAccessibilityEvent()
- (API レベル 14)このメソッドはビューの
AccessibilityEvent
について、読み上げられるテキスト プロンプトを設定します。このメソッドは、そのビューの親ビューがユーザー補助イベントを生成する場合も呼び出されます。注: このメソッド内のテキスト以外の追加属性を変更すると、他のメソッドによって設定されたプロパティが上書きされる可能性があります。このメソッドを使ってユーザー補助イベントの属性を変更できますが、テキスト コンテンツだけの変更に限定し、イベントのその他のプロパティの変更には、
onInitializeAccessibilityEvent()
メソッドを使用してください。注: このイベントの実装により、出力テキストを完全にオーバーライドし、レイアウトの他の部分が各コンテンツを変更できないようにする場合は、コード内でこのメソッドのスーパー実装を呼び出さないでください。
onInitializeAccessibilityEvent()
- (API レベル 14)システムがこのメソッドを呼び出して、テキスト コンテンツ以外のビューの状態に関する追加情報を取得します。シンプルな
TextView
やButton
より複雑な操作のコントロールをカスタムビューで提供する場合、このメソッドをオーバーライドし、ビューに関する追加情報を、このメソッドを使用してイベントに設定する必要があります。追加情報とは、パスワード フィールドのタイプ、チェックボックスのタイプ、ユーザー操作やフィードバックを提供する状態などです。このメソッドをオーバーライドする場合、スーパー実装を呼び出して、スーパークラスでは設定されなかったプロパティのみを変更する必要があります。 onInitializeAccessibilityNodeInfo()
- (API レベル 14)このメソッドは、ビューの状態に関する情報をユーザー補助サービスに提供します。
View
のデフォルト実装には、ビューの標準的なプロパティが含まれています。ただし、シンプルなTextView
やButton
より複雑な操作のコントロールをカスタムビューで提供する場合は、このメソッドをオーバーライドして、ビューに関する追加情報を、このメソッドが扱うAccessibilityNodeInfo
オブジェクトに設定する必要があります。 onRequestSendAccessibilityEvent()
- (API レベル 14)ビューの子要素が
AccessibilityEvent
を生成すると、システムがこのメソッドを呼び出します。このステップにより、親ビューが追加情報を使ってユーザー補助イベントを修正できます。このメソッドを実装する必要があるのは、カスタムビューに子ビューが含まれる場合で、ユーザー補助サービスに役立つコンテキスト情報を親ビューがユーザー補助イベントに提供できる場合のみです。
カスタムビューで上記のユーザー補助メソッドをサポートするには、以下のいずれかの方法を採用する必要があります。
- アプリが Android 4.0(API レベル 14)以上をターゲットにする場合は、直接カスタムビュー クラス内で、上記のユーザー補助メソッドをオーバーライドして実装します。
- カスタムビューに Android 1.6(API レベル 4)以上との互換性を持たせる場合は、Android サポート ライブラリのリビジョン 5 以上をプロジェクトに追加します。その後、カスタムビュー クラス内で
ViewCompat.setAccessibilityDelegate()
メソッドを呼び出して、上記のユーザー補助メソッドを実装する必要があります。この方法の例については、Android サポート ライブラリ(リビジョン 5 以上)のAccessibilityDelegateSupportActivity
のサンプル(<sdk>/extras/android/support/v4/samples/Support4Demos/
内)をご覧ください。
どちらの場合も、カスタムビュー クラスに以下のユーザー補助メソッドを実装する必要があります。
dispatchPopulateAccessibilityEvent()
onPopulateAccessibilityEvent()
onInitializeAccessibilityEvent()
onInitializeAccessibilityNodeInfo()
これらのメソッドの実装について詳しくは、ユーザー補助イベントを実装する方法に関する説明をご覧ください。
ユーザー補助イベントを送信する
カスタムビューの詳細に応じて、AccessibilityEvent
オブジェクトの送信が必要になるタイミングや、対象となる、デフォルト実装では処理されないイベントが異なります。View
クラスには以下のイベントタイプのデフォルト実装が提供されています。
- API レベル 4 以上:
- API レベル 14 以上:
注: Hover イベントは、タッチガイド機能と関連付けられています。タッチイベント機能は、こうしたイベントをトリガーとして使って、ユーザー インターフェース要素の音声プロンプトを提供します。
通常は、カスタムビューのコンテンツが変わるたびに AccessibilityEvent
を送信する必要があります。たとえば、ユーザーが左矢印または右矢印を押して数値を選択できるカスタム スライダーを実装する場合、スライダーの値が変わるたびに、カスタムビューはタイプ TYPE_VIEW_TEXT_CHANGED
のイベントを発生させる必要があります。次のサンプルコードは、sendAccessibilityEvent()
メソッドを使ってこのイベントを報告する例を示します。
Kotlin
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean { return when(keyCode) { KeyEvent.KEYCODE_DPAD_LEFT -> { currentValue-- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED) true } ... } }
Java
@Override public boolean onKeyUp (int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) { currentValue--; sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED); return true; } ... }
ユーザー補助イベントを実装する
各 AccessibilityEvent
には、ビューの現在の状態を示す必須のプロパティ セットがあります。これらのプロパティには、ビューのクラス名、コンテンツの説明、チェックボックスの状態などがあります。各イベントタイプに必須のプロパティについては、AccessibilityEvent
リファレンス ドキュメントをご覧ください。
View
の実装では、こうしたプロパティにデフォルト値を指定します。これらの値の多くは、クラス名やイベントのタイムスタンプのように、自動的に提供されます。カスタムビュー コンポーネントを作成する場合は、ビューのコンテンツと特性についての情報をある程度提供する必要があります。この情報はボタンラベルのようにシンプルなものでも、ビューの状態に関してイベントに追加すべき詳細なものでもかまいません。
カスタムビューでユーザー補助サービスに情報を提供する場合の最小要件は、dispatchPopulateAccessibilityEvent()
を実装することです。システムがこのメソッドを呼び出して AccessibilityEvent
の情報をリクエストし、カスタムビューを Android 1.6(API レベル 4)以上のユーザー補助サービスと互換性のあるものにします。次のサンプルコードは、このメソッドの基本的な実装例を示します。
Kotlin
override fun dispatchPopulateAccessibilityEvent(event: AccessibilityEvent): Boolean { // Call the super implementation to populate its text to the event, which // calls onPopulateAccessibilityEvent() on API Level 14 and up. return super.dispatchPopulateAccessibilityEvent(event).let { completed -> // In case this is running on a API revision earlier that 14, check // the text content of the event and add an appropriate text // description for this custom view: if (text?.isNotEmpty() == true) { event.text.add(text) true } else { completed } } }
Java
@Override public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { // Call the super implementation to populate its text to the event, which // calls onPopulateAccessibilityEvent() on API Level 14 and up. boolean completed = super.dispatchPopulateAccessibilityEvent(event); // In case this is running on a API revision earlier that 14, check // the text content of the event and add an appropriate text // description for this custom view: CharSequence text = getText(); if (!TextUtils.isEmpty(text)) { event.getText().add(text); return true; } return completed; }
Android 4.0(API レベル 14)以上で AccessibilityEvent
の情報を入力または変更する場合は、onPopulateAccessibilityEvent()
メソッドと onInitializeAccessibilityEvent()
メソッドを使用します。onPopulateAccessibilityEvent()
メソッドは、特にイベントのテキスト コンテンツの追加または変更に使用します。TalkBack のようなユーザー補助サービスがそのテキスト コンテンツを音声プロンプトに変えます。onInitializeAccessibilityEvent()
メソッドは、ビューの選択状態など、イベントに関する追加情報を入力するために使用します。
さらに、onInitializeAccessibilityNodeInfo()
メソッドを実装します。このメソッドによって AccessibilityNodeInfo
オブジェクトにデータが入力されます。ユーザー補助サービスはこのオブジェクトを使って、ユーザー補助イベントの受信後にそのイベントを生成したビュー階層を調査し、より詳細なコンテキスト情報を取得して、適切なフィードバックをユーザーに提供します。
下記のサンプルコードでは、ViewCompat.setAccessibilityDelegate()
を使用することで上記の 3 つのメソッドをオーバーライドする方法を示します。このサンプルコードでは、API レベル 4(リビジョン 5 以上)の Android サポート ライブラリをプロジェクトに追加する必要があります。
Kotlin
ViewCompat.setAccessibilityDelegate(this, object : AccessibilityDelegateCompat() { override fun onPopulateAccessibilityEvent(host: View, event: AccessibilityEvent) { super.onPopulateAccessibilityEvent(host, event) // We call the super implementation to populate its text for the // event. Then we add our text not present in a super class. // Very often you only need to add the text for the custom view. if (text?.isNotEmpty() == true) { event.text.add(text) } } override fun onInitializeAccessibilityEvent(host: View, event: AccessibilityEvent) { super.onInitializeAccessibilityEvent(host, event); // We call the super implementation to let super classes // set appropriate event properties. Then we add the new property // (checked) which is not supported by a super class. event.isChecked = isChecked() } override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfoCompat) { super.onInitializeAccessibilityNodeInfo(host, info) // We call the super implementation to let super classes set // appropriate info properties. Then we add our properties // (checkable and checked) which are not supported by a super class. info.isCheckable = true info.isChecked = isChecked() // Quite often you only need to add the text for the custom view. if (text?.isNotEmpty() == true) { info.text = text } } })
Java
ViewCompat.setAccessibilityDelegate(new AccessibilityDelegateCompat() { @Override public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) { super.onPopulateAccessibilityEvent(host, event); // We call the super implementation to populate its text for the // event. Then we add our text not present in a super class. // Very often you only need to add the text for the custom view. CharSequence text = getText(); if (!TextUtils.isEmpty(text)) { event.getText().add(text); } } @Override public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) { super.onInitializeAccessibilityEvent(host, event); // We call the super implementation to let super classes // set appropriate event properties. Then we add the new property // (checked) which is not supported by a super class. event.setChecked(isChecked()); } @Override public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) { super.onInitializeAccessibilityNodeInfo(host, info); // We call the super implementation to let super classes set // appropriate info properties. Then we add our properties // (checkable and checked) which are not supported by a super class. info.setCheckable(true); info.setChecked(isChecked()); // Quite often you only need to add the text for the custom view. CharSequence text = getText(); if (!TextUtils.isEmpty(text)) { info.setText(text); } } }
これらのメソッドは、カスタムビュー クラス内に直接実装できます。この方法の別の例については、Android サポート ライブラリ(リビジョン 5 以上)の AccessibilityDelegateSupportActivity
のサンプル(<sdk>/extras/android/support/v4/samples/Support4Demos/
内)をご覧ください。
ユーザー補助コンテキストをカスタマイズして提供する
Android 4.0(API レベル 14)ではフレームワークが拡張され、ユーザー補助イベントを生成するユーザー インターフェース コンポーネントのビュー階層を、ユーザー補助サービスが調べられるようになりました。この拡張により、ユーザー補助サービスは今までよりはるかに豊富なコンテキスト情報を提供して、ユーザーをサポートできます。
場合によっては、ユーザー補助サービスがビュー階層から十分な情報を取得できないことがあります。一例を挙げると、カレンダーの操作のように、クリック可能な別個の領域が複数あるようなカスタム インターフェース コントロールの場合です。この場合、クリック可能なサブセクションはビュー階層を構成してはいないため、サービスは適切な情報を取得できません。

図 1. 日にちの要素を選択可能なカスタム カレンダーのビュー
図 1 の例では、カレンダー全体が 1 つのビューとして実装されているので、何もしないでおくと、ビューのコンテンツと、ビュー内でのユーザーの選択に関する十分な情報が、ユーザー補助サービスに提供されません。たとえば、ユーザーが 17 の日にちをクリックしても、ユーザー補助機能のフレームワークが受け取るのは、カレンダー全体のコントロールを説明する情報だけです。この場合、TalkBack ユーザー補助サービスは単に「カレンダー」または「4 月のカレンダー」と通知するだけで、どの日にちが選択されたかユーザーにはわかりません。
このような状況でユーザー補助サービスに十分なコンテキスト情報を提供するために、フレームワークには仮想ビュー階層を指定する方法があります。仮想ビュー階層とは、アプリのデベロッパーが補助的なビュー階層を指定して、画面上に実際に表示される情報により近い構造をユーザー補助サービスに知らせる方法です。この方法により、ユーザー補助サービスはユーザーにさらに役立つコンテキスト情報を提供できます。
そのほか、仮想ビュー階層が必要と考えられる状況としては、ユーザー インターフェースに機能と密接に関連する一連のコントロール(ビュー)があって、そのうちの 1 つのコントロールでのアクションが、他の要素の内容に影響を与える場合があります。たとえば、上矢印と下矢印の 2 つのボタンで数を選択するツールです。この場合、ユーザー補助サービスは十分な情報を取得できません。一方のコントロールのアクションによって別のコントロールのコンテンツが変更されますが、そのコントロール間の関係がユーザー補助サービスには明らかではない可能性があるからです。この状況に対処するには、関連する複数のコントロールとそれらを含むビューをグループにまとめて、このコンテナから仮想ビュー階層を指定し、コントロールが提供する情報と動作を明示します。
ビューの仮想ビュー階層を指定するには、カスタムビュー内またはビューグループ内で getAccessibilityNodeProvider()
メソッドをオーバーライドし、AccessibilityNodeProvider
の実装を返します。Android 1.6 以降と互換性のある仮想ビュー階層を実装するには、ViewCompat.getAccessibilityNodeProvider()
メソッドを含むサポート ライブラリを使用し、AccessibilityNodeProviderCompat
を使った実装を提供します。
ユーザー補助サービスへの情報提供とユーザー補助フォーカスの管理のさまざまな側面を簡略化するには、代わりに ExploreByTouchHelper
を実装することをご検討ください。AccessibilityNodeProviderCompat
を提供してビューの AccessibilityDelegateCompat
として接続することができます。例として、ExploreByTouchHelperActivity
をご覧ください。ExploreByTouchHelper
は、子ビュー SimpleMonthView
を介して CalendarView
などのフレームワーク ウィジェットでも使用されます。
カスタム タッチイベントを処理する
次の例に示すように、カスタムビュー コントロールでは、標準的でないタッチイベント動作が必要になる場合があります。
クリックベースのアクションを定義する
ウィジェットが OnClickListener
または OnLongClickListener
インターフェースを使用する場合、システムは ACTION_CLICK
および ACTION_LONG_CLICK
アクションを処理します。ただし、より高度にカスタマイズされた、OnTouchListener
インターフェースに依存するウィジェットをアプリが使用する場合は、クリックベースのユーザー補助アクション用のカスタム ハンドラを定義する必要があります。そのためには、次のコード スニペットに示すように、アクションごとに replaceAccessibilityAction()
メソッドを呼び出します。
Kotlin
// Assumes that the widget is designed to select text when tapped and select // all text when long-tapped. In its strings.xml file, this app has set // "select" to "Select" and "select_all" to "Select all", respectively. ViewCompat.replaceAccessibilityAction( WIDGET, ACTION_CLICK, context.getString(R.string.select) ) { view, commandArguments -> selectText() } ViewCompat.replaceAccessibilityAction( WIDGET, ACTION_LONG_CLICK, context.getString(R.string.select_all) ) { view, commandArguments -> selectAllText() }
Java
// Assumes that the widget is designed to select text when tapped and select // all text when long-tapped. In its strings.xml file, this app has set // "select" to "Select" and "select_all" to "Select all", respectively. ViewCompat.replaceAccessibilityAction(WIDGET, ACTION_CLICK, context.getString(R.string.select), (view, commandArguments) -> { selectText(); }); ViewCompat.replaceAccessibilityAction(WIDGET, ACTION_LONG_CLICK, context.getString(R.string.select_all), (view, commandArguments) -> { selectAllText(); });
カスタム クリック イベントを作成する
カスタム コントロールでは、onTouchEvent(MotionEvent)
リスナー メソッドを使用して ACTION_DOWN
イベントと ACTION_UP
イベントを検出し、特別なクリック イベントをトリガーする場合があります。ユーザー補助サービスとの互換性を維持するため、このカスタム クリック イベントを処理するコードは以下を行う必要があります。
- 解釈したクリック アクションに対して適切な
AccessibilityEvent
を生成します。 - ユーザー補助サービスを有効にして、タッチ スクリーンを使用できないユーザーのために、カスタム クリック アクションを実行します。
これらの要件を効率よく処理するには、performClick()
メソッドをオーバーライドするコードで、このメソッドのスーパー実装を呼び出してから、そのクリック イベントで必要なアクションを何でも実行する必要があります。カスタム クリック アクションが検出されたとき、そのコードは上記の performClick()
メソッドを呼び出す必要があります。次のコードでこのパターンの例を示します。
Kotlin
class CustomTouchView(context: Context) : View(context) { var downTouch = false override fun onTouchEvent(event: MotionEvent): Boolean { super.onTouchEvent(event) // Listening for the down and up touch events return when (event.action) { MotionEvent.ACTION_DOWN -> { downTouch = true true } MotionEvent.ACTION_UP -> if (downTouch) { downTouch = false performClick() // Call this method to handle the response, and // thereby enable accessibility services to // perform this action for a user who cannot // click the touchscreen. true } else { false } else -> false // Return false for other touch events } } override fun performClick(): Boolean { // Calls the super implementation, which generates an AccessibilityEvent // and calls the onClick() listener on the view, if any super.performClick() // Handle the action for the custom click here return true } }
Java
class CustomTouchView extends View { public CustomTouchView(Context context) { super(context); } boolean downTouch = false; @Override public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event); // Listening for the down and up touch events switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downTouch = true; return true; case MotionEvent.ACTION_UP: if (downTouch) { downTouch = false; performClick(); // Call this method to handle the response, and // thereby enable accessibility services to // perform this action for a user who cannot // click the touchscreen. return true; } } return false; // Return false for other touch events } @Override public boolean performClick() { // Calls the super implementation, which generates an AccessibilityEvent // and calls the onClick() listener on the view, if any super.performClick(); // Handle the action for the custom click here return true; } }
上記のパターンでは、カスタム クリック イベントとユーザー補助サービスとの互換性を確保するために、performClick()
メソッドを使用してユーザー補助イベントを生成し、かつ、このカスタム クリック イベントをユーザーの代わりに実行するユーザー補助サービスのエントリ ポイントを提供しています。
注: カスタム カレンダー ビューのように、カスタムビューにクリック可能な個別の領域が複数ある場合、ユーザー補助サービスとの互換性を保つために、カスタムビュー内の getAccessibilityNodeProvider()
をオーバーライドして仮想ビュー階層を実装する必要があります。