Android には、ユーザーによるアプリの操作からイベントをインターセプトする方法が複数あります。 ユーザー インターフェース内のイベントをインターセプトする場合は、ユーザーが操作する特定の View オブジェクトからイベントをキャプチャするアプローチが考えられます。View クラスは、そのための手段を提供します。
レイアウトを構成するために使用する各種の View クラスには、UI イベントの処理に役立つパブリック コールバック メソッドがいくつか含まれています。これらのメソッドは、対応するアクションがオブジェクトで発生したときに、Android フレームワークによって呼び出されます。たとえば、ビュー(ボタンなど)がタップされると、そのオブジェクトで onTouchEvent() メソッドが呼び出されます。これをインターセプトするには、クラスを拡張してメソッドをオーバーライドする必要があります。ただし、このようなイベントを処理するために、その都度 View オブジェクトを拡張するのは実際的ではありません。そのために、View クラスには、もっと簡単に定義できるコールバックを持つネストされたインターフェースのコレクションも含まれています。これらのインターフェースはイベント リスナーと呼ばれ、ユーザーによる UI の操作をキャプチャする手段として使用されます。
一般的にはイベント リスナーを使用してユーザー操作をリッスンしますが、カスタム コンポーネントを作成するために View クラスを拡張する場合もあります。たとえば、Button クラスを拡張して、凝った機能を提供することもできます。その場合は、クラスのイベント ハンドラを使用して、クラスのデフォルトのイベント動作を定義できます。
イベント リスナー
イベント リスナーは、単一のコールバック メソッドを含む、View クラスのインターフェースです。以下のメソッドは、イベント リスナーが登録されているビューがユーザーによる UI アイテムの操作によってトリガーされたときに、Android フレームワークによって呼び出されます。
イベント リスナー インターフェースには、以下のコールバック メソッドが含まれています。
- onClick()
- View.OnClickListenerのメソッド。 ユーザーがアイテムをタップしたとき(タッチモードの場合)、またはナビゲーション キーかトラックボールでアイテムにフォーカスを合わせて適切な「Enter」キーかトラックボールを押したときに、呼び出されます。
- onLongClick()
- View.OnLongClickListenerのメソッド。 ユーザーがアイテムを長押ししたとき(タッチモードの場合)、またはナビゲーション キーかトラックボールでアイテムにフォーカスを合わせて適切な「Enter」キーかトラックボールを長押し(1 秒間)したときに呼び出されます。
- onFocusChange()
- View.OnFocusChangeListenerのメソッド。 ユーザーがナビゲーション キーかトラックボールを使用して、アイテムに移動したときまたはアイテムから移動したときに、呼び出されます。
- onKey()
- View.OnKeyListenerのメソッド。 ユーザーがアイテムにフォーカスを合わせてデバイスのハードウェア キーを押したときまたは離したときに、呼び出されます。
- onTouch()
- View.OnTouchListenerのメソッド。 画面(アイテムの境界内)で、タッチイベントとして判断されるアクション(押す、離す、移動するなどの操作)をユーザーが行ったときに呼び出されます。
- onCreateContextMenu()
- View.OnCreateContextMenuListenerのメソッド。コンテキスト メニューが(「長押しクリック」を続けた結果として)ビルドされたときに呼び出されます。コンテキスト メニューの詳細については、メニューのデベロッパー ガイドをご覧ください。
これらのメソッドは、それぞれのインターフェースに含まれる唯一のメソッドです。これらのメソッドのいずれかを定義してイベントを処理するには、ネストされたインターフェースをアクティビティに実装するか、匿名クラスとして定義します。
次に、実装のインスタンスをそれぞれの View.set...Listener() メソッドに渡します(たとえば、setOnClickListener()OnClickListener の実装を渡します)。
次の例は、ボタンの on-click リスナーを登録する方法を示しています。
Kotlin
protected void onCreate(savedValues: Bundle) { ... val button: Button = findViewById(R.id.corky) // Register the onClick listener with the implementation above button.setOnClickListener { view -> // do something when the button is clicked } ... }
Java
// Create an anonymous implementation of OnClickListener private OnClickListener corkyListener = new OnClickListener() { public void onClick(View v) { // do something when the button is clicked } }; protected void onCreate(Bundle savedValues) { ... // Capture our button from layout Button button = (Button)findViewById(R.id.corky); // Register the onClick listener with the implementation above button.setOnClickListener(corkyListener); ... }
OnClickListener をアクティビティの一部として実装する方法も有用です。 この方法では、追加のクラスの読み込みとオブジェクトの割り当てを回避できます。次に例を示します。
Kotlin
class ExampleActivity : Activity(), OnClickListener { protected fun onCreate(savedValues: Bundle) { val button: Button = findViewById(R.id.corky) button.setOnClickListener(this) } // Implement the OnClickListener callback fun onClick(v: View) { // do something when the button is clicked } }
Java
public class ExampleActivity extends Activity implements OnClickListener { protected void onCreate(Bundle savedValues) { ... Button button = (Button)findViewById(R.id.corky); button.setOnClickListener(this); } // Implement the OnClickListener callback public void onClick(View v) { // do something when the button is clicked } ... }
上記の例の onClick() コールバックには戻り値がありませんが、イベント リスナー メソッドによってはブール値を返す必要があることにご注意ください。ブール値を返す理由は、イベントによって異なります。いくつかのメソッドについて、その理由を示します。
- onLongClick()
- onKey()
- onTouch()
ハードウェア キーイベントは、常に、現在フォーカスされているビューに送信されます。ビュー階層の一番上から下に向かって、適切な宛先に到達するまでディスパッチされます。ビュー(またはビューの子)が現在フォーカスされている場合は、dispatchKeyEvent()onKeyDown()onKeyUp()
また、アプリのテキスト入力について検討する際は、多くのデバイスにはソフトウェア入力メソッドしかないことにご注意ください。ソフトウェア入力メソッドではキーの使用は必須ではなく、音声入力や手書き入力を利用できるものもあります。入力メソッドがキーボードに似たインターフェースを提供する場合でも、それによって onKeyDown()IME_ACTION_DONE などのアクションを使用してアプリが期待する反応を入力メソッドに伝え、それによって入力メソッドが UI に意味のある変更を加えられるようにしてください。ソフトウェア入力メソッドの動作を推測し、その推測を信じて書式設定済みのテキストをアプリに提供することは避けてください。
注: Android は、最初にイベント ハンドラを呼び出し、次にクラス定義から適切なデフォルト ハンドラを呼び出します。そのため、これらのイベント リスナーから true が返されると、イベントは他のイベント リスナーに伝播されなくなり、ビュー内のデフォルトのイベント ハンドラへのコールバックもブロックされます。したがって、true を返す場合は、本当にイベントを終了してよいかを確認してください。
イベント ハンドラ
ビューからカスタム コンポーネントを作成する場合は、デフォルトのイベント ハンドラとして使用するいくつかのコールバック メソッドを定義できます。カスタムビュー コンポーネントに関するドキュメントでは、イベント処理に使用される次のような一般的なコールバックについて確認できます。
- onKeyDown(int, KeyEvent)
- onKeyUp(int, KeyEvent)
- onTrackballEvent(MotionEvent)
- onTouchEvent(MotionEvent)
- onFocusChanged(boolean, int, Rect)
これ以外に、View クラスに含まれていないがイベントの処理方法に直接影響するメソッドがいくつかあり、それらについても考慮する必要があります。レイアウト内の複雑なイベントを管理する場合は、以下のメソッドの使用を検討してください。
- Activity.dispatchTouchEvent(MotionEvent)- Activityがそれらをすべてインターセプトできるようにします。
- ViewGroup.onInterceptTouchEvent(MotionEvent)- ViewGroupがそのイベントを監視できるようにします。
- ViewParent.requestDisallowInterceptTouchEvent(boolean)- onInterceptTouchEvent(MotionEvent)
タッチモード
ユーザーが方向キーまたはトラックボールを使用してユーザー インターフェースを操作する場合、入力を受け入れるアイテムがどれかをユーザーが把握できるように、操作可能なアイテム(ボタンなど)にフォーカスを与える必要があります。ただし、デバイスがタッチ機能を備えており、ユーザーがインターフェースにタッチして操作を開始した場合、アイテムをハイライト表示したり特定のビューにフォーカスを与えたりする必要はなくなります。「タッチモード」という操作モードが存在するのは、このためです。
タッチ対応デバイスでは、ユーザーが画面にタッチすると、デバイスはタッチモードになります。それ以降は、isFocusableInTouchMode() が true のビュー(テキスト編集ウィジェットなど)だけがフォーカス可能になります。
タッチ可能な他のビュー(ボタンなど)は、タッチされてもフォーカスを取得しません。押されたときに on-click リスナーを起動するだけです。
ユーザーが方向キーを押すか、トラックボールでスクロールすると、デバイスはタッチモードから抜け、ビューがフォーカスを取得します。これにより、ユーザーは画面にタッチせずにユーザー インターフェースの操作を再開できます。
タッチモードの状態は、システム全体(すべてのウィンドウとアクティビティ)で維持されます。
現在の状態をクエリするには、isInTouchMode() を呼び出して、デバイスが現在タッチモードかどうかを確認します。
フォーカスの処理
フレームワークは、ユーザー入力に応じて、所定のフォーカス移動を処理します。
そのようなフォーカス移動には、ビューが削除されたり非表示になったり、新しいビューが利用可能になったりしたことによるフォーカスの変更が含まれます。ビューは、isFocusable()setFocusable()isFocusableInTouchMode()setFocusableInTouchMode()
Android 9(API レベル 28)以降を搭載したデバイスでは、アクティビティは初期フォーカスを割り当てません。割り当てたい場合は、明示的に初期フォーカスをリクエストする必要があります。
フォーカス移動は、指定された方向の最も近くにあるアイテムを見つけるアルゴリズムに基づいて行われます。しかし、デフォルトのアルゴリズムがデベロッパーの意図する動作と一致しないことがまれにあります。そのような場合は、レイアウト ファイル内の XML 属性 nextFocusDown、nextFocusLeft、nextFocusRight、nextFocusUp を明示的にオーバーライドできます。これらの属性のいずれかを、フォーカスの「移動元」のビューに追加します。フォーカスの「移動先」のビューの ID を属性の値として定義します。次に例を示します。
<LinearLayout android:orientation="vertical" ... > <Button android:id="@+id/top" android:nextFocusUp="@+id/bottom" ... /> <Button android:id="@+id/bottom" android:nextFocusDown="@+id/top" ... /> </LinearLayout>
本来、この垂直方向のレイアウトでは、1 番目のボタンから上に移動しようとしても、2 番目のボタンから下に移動しようしても、フォーカスはどこにも移動しません。しかし、上記の例では、一番上のボタンによって一番下のボタンが nextFocusUp として定義されています(逆もまた同様)。これにより、ナビゲーション フォーカスは一番上から一番下、一番下から一番上に循環します。
UI 内でビューをフォーカス可能として宣言するには(元々フォーカス可能でなかった場合)、レイアウト宣言で android:focusable XML 属性をビューに追加します。
値を true に設定します。また、android:focusableInTouchMode を使用して、タッチモード中にビューをフォーカス可能として宣言することもできます。
特定のビューがフォーカスを取得することをリクエストするには、requestFocus()
フォーカス イベントをリッスンする(それによってビューがフォーカスを取得または喪失したときに通知を受け取る)には、イベント リスナー セクションの説明に従って、onFocusChange()
 
  