Wear で手首の動きによる操作を使用する

タッチ スクリーンが不便な場合でも、手首の動きによって、アプリを片手で素早く操作できます。

たとえば、ユーザーは片手に飲み物のコップを持ったまま、もう片方の手で通知をスクロールできます。手首の動きを使用するその他のユースケースとしては、次のようなものがあります。

  • ジョギング アプリで、歩数、経過時間、現在のペースの各表示画面を移動する
  • 旅行アプリで、フライトと搭乗口の情報をスクロールする
  • ニュースアプリで、記事をスクロールする

スマートウォッチ デバイスで手首の動きによる操作を確認するには、[設定] > [高度な機能] > [ジェスチャー] > [手首の操作: ON] を選択し、手首の動きによる操作が有効になっていることを確認します。次に、[チュートリアルを起動] を選択して、スマートウォッチでジェスチャーのチュートリアルを完了します。

注: 手首を振る動作は、システムレベルで「戻る」または「元に戻す」を表すジェスチャーであり、アプリでカスタマイズすることはできません。

このガイドで説明しているように、手首の動きによる操作は以下の方法で使用できます。

次の表に示すように、手首の動きによる操作はそれぞれ KeyEvent クラスの int 定数にマッピングされています。

操作 KeyEvent 説明
手首を外側にすばやく振る KEYCODE_NAVIGATE_NEXT このキーコードが次のアイテムに進みます。
手首を内側にすばやく振る KEYCODE_NAVIGATE_PREVIOUS このキーコードが前のアイテムに戻ります。

曲線レイアウトを使用して手首の動きによる操作をサポートする

WearableRecyclerView クラスはリスト用の曲線レイアウトを提供し、手首の動きによる操作を自動的にサポートします。このクラスには、ビューがフォーカスを保持しているときに手首の動きによる操作によって実行される事前定義済みのアクションが含まれています。WearableRecyclerView クラスの使用方法については、Wear OS でリストを作成するをご覧ください。また、このガイドのおすすめの方法もご覧ください。

注: WearableRecyclerView クラスは、ウェアラブル サポート ライブラリの非推奨になった類似クラスの後継クラスです。

WearableRecyclerView を使用する場合でも、KeyEvent クラスの定数を使用できます。WearableRecyclerView をサブクラス化して onKeyDown() コールバックを再実装することで、事前定義済みのアクションをオーバーライドできます。この動作を完全に無効化するには、setEnableGestureNavigation(false) を使用します。詳しくは、キーボード アクションの処理をご覧ください。

キーイベントを直接使用する

WearableRecyclerView の外部でキーイベントを使用すると、ジェスチャー イベントに対する新しいアクションをトリガーできます。重要な点として、これらのジェスチャー イベントは、デバイスがアクティブ モードのときに認識され、すべてのキーイベントと同様に配信されます。

KeyEvent.Callback を実装した、ユーザー操作に関連するクラス(ViewActivity など)は、他のキーイベントと同様に、手首の動きによる操作に関連するキーイベントをリッスンできます。Android フレームワークは、フォーカスを保持している View または Activity をキーイベントで呼び出します。ジェスチャーについては、ジェスチャーが行われると onKeyDown() メソッドのコールバックが呼び出されます。

たとえば、アプリは KeyEvent.Callback を実装した View または Activity で事前定義済みのアクションを次のようにしてオーバーライドできます。

Kotlin

class GesturesActivity : Activity() {

    /* KeyEvent.Callback */
    override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
        return when (keyCode) {
            KeyEvent.KEYCODE_NAVIGATE_NEXT ->
                // Do something that advances a user View to the next item in an ordered list.
                moveToNextItem()
            KeyEvent.KEYCODE_NAVIGATE_PREVIOUS ->
                // Do something that advances a user View to the previous item in an ordered list.
                moveToPreviousItem()
            else -> {
                // If you did not handle it, let it be handled by the next possible element as determined
                // by the Activity.
                super.onKeyDown(keyCode, event)
            }
        }
    }

    /** Shows the next item in the custom list.  */
    private fun moveToNextItem(): Boolean {
        ...
        // Return true if handled successfully, otherwise return false.
        return false
    }

    /** Shows the previous item in the custom list.  */
    private fun moveToPreviousItem(): Boolean {
        ...
        // Return true if handled successfully, otherwise return false.
        return false
    }
}

Java

public final class GesturesActivity extends Activity {

 @Override /* KeyEvent.Callback */
 public boolean onKeyDown(int keyCode, KeyEvent event) {
  switch (keyCode) {
   case KeyEvent.KEYCODE_NAVIGATE_NEXT:
    // Do something that advances a user View to the next item in an ordered list.
    return moveToNextItem();
   case KeyEvent.KEYCODE_NAVIGATE_PREVIOUS:
    // Do something that advances a user View to the previous item in an ordered list.
    return moveToPreviousItem();
  }
  // If you did not handle it, let it be handled by the next possible element as determined by the Activity.
  return super.onKeyDown(keyCode, event);
 }

 /** Shows the next item in the custom list. */
 private boolean moveToNextItem() {
  boolean handled = false;
  ...
  // Return true if handled successfully, otherwise return false.
  return handled;
 }

 /** Shows the previous item in the custom list. */
 private boolean moveToPreviousItem() {
  boolean handled = false;
  ...
  // Return true if handled successfully, otherwise return false.
  return handled;
 }
}

おすすめの方法

  • ViewActivity へのキーイベントの配信については、KeyEvent KeyEvent.Callback のページをご確認ください。
  • 方向アフォーダンスの一貫性を保ちます。次に進むには「手首を外側にすばやく振る」を使用し、前に戻るには「手首を内側にすばやく振る」を使用します。
  • 手首の動きによる操作に対応するタップ操作を使用できるようにします。
  • 視覚的なフィードバックを提供します。
  • 直感に反する機能をシステムの残りの部分に実装するためにキーコードを使用しないでください。たとえば、アクションをキャンセルするか、手首をすばやく振る動作で左右の軸を移動するために、KEYCODE_NAVIGATE_NEXT を使用しないでください。
  • ユーザー インターフェースに含まれていない要素(画面の表示領域外のビューや一部が覆われているビューなど)でキーイベントをインターセプトしないでください。これは、すべてのキーイベントについて同じです。
  • 手首を繰り返しすばやく振るジェスチャーを独自の新しい操作として解釈し直さないでください。システムの「手首を振る」ジェスチャーと競合する可能性があります。
  • ビューがジェスチャーのキーイベントを受信するには、フォーカスを保持していることが必要です。 View.setFocusable() をご覧ください。

    ジェスチャーはキーイベントとして扱われるため、「タップモード」からの遷移をトリガーします。これにより、予期しない動作が発生する可能性があります。ユーザーがタップとジェスチャーを併用する可能性があるため、 View::setFocusableInTouchmode() メソッドが必要になる場合があります。場合によっては、「タップモード」との間でモードの遷移が行われた後でフォーカスが変更された際に、目的のビューがフォーカスを取得できるように、setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS) を使用しなければならないこともあります。

  • requestFocus()clearFocus() は慎重に使用してください。
    • requestFocus() を呼び出すときは、ビューが適切にフォーカスを保持していることを確認します。ビューが画面の表示領域外にある場合や別のビューで覆われている場合は、手首の動きによる操作によってコールバックがトリガーされると、予期しない事態が発生することがあります。
    • clearFocus() メソッドは、別の適切なビューを見つけるためにフォーカスの検索を開始します。ビュー階層によっては、この検索でかなりの量の計算が必要になる場合があります。最終的に、フォーカスを受け取るとは予想しなかったビューにフォーカスが割り当てられる場合もあります。
  • キーイベントはまず、ビュー階層内のフォーカスを保持するビューに配信されます。フォーカスを保持するビューがイベントを処理しない場合(つまり false を返す場合)、親ビューがフォーカスを受け取ることができ、なおかつ KeyListener を持っていたとしても、イベントは親ビューには配信されません。代わりに、フォーカスを保持するビュー階層を持つ現在のアクティビティに配信されます。

    したがって、より上位のレベルですべてのイベントをキャッチしてから、関連するコードに渡す必要があります。または、アクティビティをサブクラス化して、 dispatchKeyEvent(KeyEvent event) メソッドをオーバーライドします。これにより、必要に応じてキーをインターセプトするか、より下位のレイヤでキーが処理されなかったときにキーを処理します。