アプリでカスタムビュー コンポーネントが必要な場合、そのビューのユーザー補助機能を強化する必要があります。カスタムビューのユーザー補助機能は、このページに記載しているように、次の手順で強化できます。
- 方向コントローラのクリックを処理する
- Accessibility API のメソッドを実装する
- カスタムビュー固有の
AccessibilityEvent
オブジェクトを送信する - ビューの
AccessibilityEvent
とAccessibilityNodeInfo
を実装する
方向コントローラのクリックを処理する
ほとんどのデバイスでは、方向コントローラを使用してビューをクリックすると、現在フォーカスのあるビューに KeyEvent
と KEYCODE_DPAD_CENTER
が送信されます。KEYCODE_DPAD_CENTER
は、Android のすべての標準ビューで適切に処理されます。カスタム View
コントロールを作成する場合は、このイベントの効果が、タッチスクリーン上でのビューのタップと同じになるようにしてください。
カスタム コントロールでは、KEYCODE_ENTER
イベントを KEYCODE_DPAD_CENTER
と同じように扱う必要があります。この方法により、フルキーボードからの操作が、ユーザーにとってかなり簡単になります。
Accessibility API のメソッドを実装する
ユーザー補助イベントは、アプリの視覚的インターフェース コンポーネントに対するユーザーの操作に関する通知です。こうした通知を扱うのがユーザー補助サービスで、そのイベントの情報を使用して補足説明のフィードバックやプロンプトを出力します。ユーザー補助メソッドは、View
クラスだけでなく、View.AccessibilityDelegate
クラスにも含まれます。以下のようなメソッドがあります。
dispatchPopulateAccessibilityEvent()
onPopulateAccessibilityEvent()
を呼び出してから、このビューの各子要素に対して dispatchPopulateAccessibilityEvent()
メソッドを呼び出します。onInitializeAccessibilityEvent()
TextView
や Button
より複雑な操作のコントロールをカスタムビューで提供する場合、このメソッドをオーバーライドし、ビューに関する追加情報を、このメソッドを使用してイベントに設定する必要があります。追加情報とは、パスワード フィールドのタイプ、チェックボックスのタイプ、ユーザー操作やフィードバックを提供する状態などです。このメソッドをオーバーライドする場合、スーパー実装を呼び出して、スーパークラスでは設定されていないプロパティのみを変更します。onInitializeAccessibilityNodeInfo()
View
のデフォルト実装には、ビューの標準的なプロパティが含まれています。ただし、シンプルな TextView
や Button
より複雑な操作のコントロールをカスタムビューで提供する場合は、このメソッドをオーバーライドして、ビューに関する追加情報を、このメソッドが扱う AccessibilityNodeInfo
オブジェクトに設定する必要があります。onPopulateAccessibilityEvent()
AccessibilityEvent
について、読み上げられるテキスト プロンプトを設定します。このメソッドは、そのビューの親ビューがユーザー補助イベントを生成する場合も呼び出されます。
onRequestSendAccessibilityEvent()
AccessibilityEvent
を生成すると、システムがこのメソッドを呼び出します。このステップにより、親ビューが追加情報を使ってユーザー補助イベントを修正できます。このメソッドを実装する必要があるのは、カスタムビューに子ビューが含まれる場合で、ユーザー補助サービスに役立つコンテキスト情報を親ビューがユーザー補助イベントに提供できる場合のみです。sendAccessibilityEvent()
- ユーザーがビューに対してアクションを行うと、このメソッドが呼び出されます。そのイベントは、
TYPE_VIEW_CLICKED
のようなユーザー アクション タイプによって分類されます。通常は、カスタムビューのコンテンツが変わるたびにAccessibilityEvent
を送信する必要があります。 sendAccessibilityEventUnchecked()
- このメソッドは、ユーザー補助をデバイス上で有効にするチェックボックス(
AccessibilityManager.isEnabled()
)を、呼び出し側のコードが直接コントロールする必要があるときに使用されます。このメソッドを実装する場合、実際のシステム設定にかかわらず、ユーザー補助が有効であるかのように呼び出します。通常、カスタムビューにこのメソッドを実装する必要はありません。 dispatchPopulateAccessibilityEvent()
onInitializeAccessibilityEvent()
onInitializeAccessibilityNodeInfo()
onPopulateAccessibilityEvent()
TYPE_VIEW_CLICKED
TYPE_VIEW_FOCUSED
TYPE_VIEW_HOVER_ENTER
TYPE_VIEW_HOVER_EXIT
TYPE_VIEW_LONG_CLICKED
TYPE_VIEW_SCROLLED
- 解釈したクリック アクションに対して適切な
AccessibilityEvent
を生成します。 - ユーザー補助サービスを有効にして、タッチ スクリーンを使用できないユーザーのために、カスタム クリック アクションを実行します。
ユーザー補助をサポートするには、直接カスタムビュー クラス内で、上記のユーザー補助メソッドをオーバーライドして実装します。
カスタムビュー クラスには、少なくとも次のユーザー補助メソッドを実装してください。
これらのメソッドの実装について詳しくは、ユーザー補助イベントを実装する方法に関する説明をご覧ください。
ユーザー補助イベントを送信する
カスタムビューの詳細に応じて、AccessibilityEvent
オブジェクトの送信が必要になるタイミングや、対象となる、デフォルト実装では処理されないイベントが異なります。View
クラスには以下のイベントタイプのデフォルト実装が提供されています。
通常は、カスタムビューのコンテンツが変わるたびに 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
の実装では、こうした必須プロパティにデフォルト値を指定します。これらの値の多くは、クラス名やイベントのタイムスタンプのように、自動的に提供されます。カスタムビュー コンポーネントを作成する場合は、ビューのコンテンツと特性についての情報を提供する必要があります。この情報はボタンラベルのようにシンプルなものでも、ビューの状態に関してイベントに追加する詳細なものでもかまいません。
AccessibilityEvent
の情報を入力または変更する場合は、onPopulateAccessibilityEvent()
メソッドと onInitializeAccessibilityEvent()
メソッドを使用します。onPopulateAccessibilityEvent()
メソッドは、特にイベントのテキスト コンテンツの追加または変更に使用します。TalkBack のようなユーザー補助サービスがそのテキスト コンテンツを音声プロンプトに変えます。onInitializeAccessibilityEvent()
メソッドは、ビューの選択状態など、イベントに関する追加情報を入力するために使用します。
さらに、onInitializeAccessibilityNodeInfo()
メソッドを実装します。このメソッドによって AccessibilityNodeInfo
オブジェクトにデータが入力されます。ユーザー補助サービスはこのオブジェクトを使って、ユーザー補助イベントの受信後にそのイベントを生成したビュー階層を調査し、適切なフィードバックをユーザーに提供します。
下記のサンプルコードでは、ビューで上記の 3 つのメソッドをオーバーライドする方法を示します。
Kotlin
override fun onPopulateAccessibilityEvent(event: AccessibilityEvent?) { super.onPopulateAccessibilityEvent(event) // Call the super implementation to populate its text for the // event. Then, add text not present in a super class. // You typically only need to add the text for the custom view. if (text?.isNotEmpty() == true) { event?.text?.add(text) } } override fun onInitializeAccessibilityEvent(event: AccessibilityEvent?) { super.onInitializeAccessibilityEvent(event) // Call the super implementation to let super classes // set appropriate event properties. Then, add the new checked // property that is not supported by a super class. event?.isChecked = isChecked() } override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo?) { super.onInitializeAccessibilityNodeInfo(info) // Call the super implementation to let super classes set // appropriate info properties. Then, add the checkable and checked // properties that are not supported by a super class. info?.isCheckable = true info?.isChecked = isChecked() // You typically only need to add the text for the custom view. if (text?.isNotEmpty() == true) { info?.text = text } }
Java
@Override public void onPopulateAccessibilityEvent(AccessibilityEvent event) { super.onPopulateAccessibilityEvent(event); // Call the super implementation to populate its text for the // event. Then, add the text not present in a super class. // You typically 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(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); // Call the super implementation to let super classes // set appropriate event properties. Then, add the new checked // property that is not supported by a super class. event.setChecked(isChecked()); } @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); // Call the super implementation to let super classes set // appropriate info properties. Then, add the checkable and checked // properties that are not supported by a super class. info.setCheckable(true); info.setChecked(isChecked()); // You typically only need to add the text for the custom view. CharSequence text = getText(); if (!TextUtils.isEmpty(text)) { info.setText(text); } }
これらのメソッドは、カスタムビュー クラス内に直接実装できます。
ユーザー補助コンテキストをカスタマイズして提供する
ユーザー補助サービスは、ユーザー補助イベントを生成するユーザー インターフェース コンポーネントのビュー階層を調べられます。これにより、ユーザー補助サービスは豊富なコンテキスト情報を提供してユーザーをサポートできます。
場合によっては、ユーザー補助サービスがビュー階層から十分な情報を取得できないことがあります。一例を挙げると、カレンダーの操作のように、クリック可能な別個の領域が複数あるようなカスタム インターフェース コントロールの場合です。この場合、クリック可能なサブセクションはビュー階層を構成してはいないため、サービスは適切な情報を取得できません。
図 1 の例では、カレンダー全体が 1 つのビューとして実装されているので、デベロッパーが追加の情報を提供しない限り、ビューのコンテンツと、ビュー内でのユーザーの選択に関する十分な情報が、ユーザー補助サービスに提供されません。たとえば、ユーザーが 17 の日にちをクリックしても、ユーザー補助機能のフレームワークが受け取るのは、カレンダー全体のコントロールを説明する情報だけです。この場合、TalkBack ユーザー補助サービスは単に「カレンダー」または「4 月のカレンダー」と通知するだけで、どの日にちが選択されたかユーザーにはわかりません。
このような状況でユーザー補助サービスに十分なコンテキスト情報を提供するために、フレームワークには仮想ビュー階層を指定する方法があります。仮想ビュー階層とは、アプリのデベロッパーが補助的なビュー階層を指定して、画面上に実際に表示される情報により近い構造をユーザー補助サービスに知らせる方法です。この方法により、ユーザー補助サービスはユーザーにさらに役立つコンテキスト情報を提供できます。
そのほか、仮想ビュー階層が必要と考えられる状況としては、ユーザー インターフェースに機能と密接に関連する一連の View
コントロールがあって、そのうちの 1 つのコントロールでのアクションが、他の要素の内容に影響を与える場合があります。たとえば、上矢印と下矢印の 2 つのボタンで数を選択するツールです。この場合、ユーザー補助サービスは十分な情報を取得できません。一方のコントロールのアクションによって別のコントロールのコンテンツが変更されますが、そのコントロール間の関係がユーザー補助サービスには明らかではない可能性があるからです。
この状況に対処するには、関連する複数のコントロールとそれらを含むビューをグループにまとめて、このコンテナから仮想ビュー階層を指定し、コントロールが提供する情報と動作を明示します。
ビューの仮想ビュー階層を指定するには、カスタムビュー内またはビューグループ内で getAccessibilityNodeProvider()
メソッドをオーバーライドし、AccessibilityNodeProvider
の実装を返します。仮想ビュー階層を実装するには、ViewCompat.getAccessibilityNodeProvider()
メソッドを含むサポート ライブラリを使用し、AccessibilityNodeProviderCompat
を使った実装を提供します。
ユーザー補助サービスへの情報提供とユーザー補助フォーカスの管理を簡略化するには、代わりに ExploreByTouchHelper
を実装します。AccessibilityNodeProviderCompat
を提供し、setAccessibilityDelegate
を呼び出すことでビューの AccessibilityDelegateCompat
として接続できます。例として、ExploreByTouchHelperActivity
をご覧ください。ExploreByTouchHelper
は、子ビュー SimpleMonthView
を介して CalendarView
などのフレームワーク ウィジェットでも使用されます。
カスタム タッチイベントを処理する
次の例に示すように、カスタムビュー コントロールでは、標準的でないタッチイベント動作が必要になる場合があります。
クリックベースのアクションを定義する
ウィジェットが OnClickListener
または OnLongClickListener
インターフェースを使用する場合、システムは ACTION_CLICK
および ACTION_LONG_CLICK
アクションを処理します。OnTouchListener
インターフェースに依存する高度にカスタマイズされたウィジェットをアプリが使用する場合は、クリックベースのユーザー補助アクション用のカスタム ハンドラを定義します。そのためには、次のコード スニペットに示すように、アクションごとに replaceAccessibilityAction()
メソッドを呼び出します。
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { ... // Assumes that the widget is designed to select text when tapped, and selects // all text when tapped and held. In its strings.xml file, this app sets // "select" to "Select" and "select_all" to "Select all". ViewCompat.replaceAccessibilityAction( binding.textSelectWidget, ACTION_CLICK, getString(R.string.select) ) { view, commandArguments -> selectText() } ViewCompat.replaceAccessibilityAction( binding.textSelectWidget, ACTION_LONG_CLICK, getString(R.string.select_all) ) { view, commandArguments -> selectAllText() } }
Java
@Override protected void onCreate(Bundle savedInstanceState) { ... // Assumes that the widget is designed to select text when tapped, and select // all text when tapped and held. In its strings.xml file, this app sets // "select" to "Select" and "select_all" to "Select all". ViewCompat.replaceAccessibilityAction( binding.textSelectWidget, ACTION_CLICK, getString(R.string.select), (view, commandArguments) -> selectText()); ViewCompat.replaceAccessibilityAction( binding.textSelectWidget, ACTION_LONG_CLICK, getString(R.string.select_all), (view, commandArguments) -> selectAllText()); }
カスタム クリック イベントを作成する
カスタム コントロールでは、onTouchEvent(MotionEvent)
リスナー メソッドを使用して ACTION_DOWN
イベントと ACTION_UP
イベントを検出し、特別なクリック イベントをトリガーする場合があります。ユーザー補助サービスとの互換性を維持するため、このカスタム クリック イベントを処理するコードは以下を行う必要があります。
これらの要件を効率よく処理するには、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 // enable accessibility services to // perform this action for a user who can't // tap 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 // enable accessibility services to // perform this action for a user who can't // tap 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()
メソッドを使用してユーザー補助イベントを生成し、かつ、このカスタム クリック イベントをユーザーの代わりに実行するユーザー補助サービスのエントリ ポイントを提供しています。