アプリを開発する

以下の資料はアプリ開発者向けです。

アプリでロータリーをサポートするには、次のことを行う必要があります。

  1. FocusParkingViewをそれぞれのアクティビティ レイアウトに配置します。
  2. ビューがフォーカス可能である (またはフォーカスできない) ことを確認します。
  3. FocusAreaを使用して、 FocusParkingViewを除くすべてのフォーカス可能なビューを囲みます。

ロータリー対応アプリを開発するための環境をセットアップした後の、これらの各タスクについて以下で詳しく説明します。

ロータリーコントローラーをセットアップする

ロータリー対応アプリの開発を始める前に、ロータリー コントローラーまたはスタンドインのいずれかが必要です。以下に説明するオプションがあります。

エミュレータ

source build/envsetup.sh && lunch car_x86_64-userdebug
m -j
emulator -wipe-data -no-snapshot -writable-system

aosp_car_x86_64-userdebugを使用することもできます。

エミュレートされたロータリー コントローラーにアクセスするには:

  1. ツールバーの下部にある 3 つの点をタップします。

    エミュレートされたロータリー コントローラーにアクセス
    図 1.エミュレートされたロータリー コントローラーへのアクセス
  2. 拡張コントロール ウィンドウで[車のロータリー]を選択します。

    車のロータリーを選択してください
    図 2.車のロータリーを選択します

USBキーボード

  • Android Automotive OS (AAOS) を実行しているデバイスに USB キーボードを接続すると、場合によっては、オンスクリーン キーボードが表示されなくなることがあります。
  • userdebugまたはengビルドを使用します。
  • キーイベントフィルタリングを有効にする:
    adb shell settings put secure android.car.ROTARY_KEY_EVENT_FILTER 1
    
  • 各アクションに対応するキーを見つけるには、以下の表を参照してください。
    ロータリーアクション
    Q反時計回りに回転
    E時計回りに回転します
    左にナッジする
    D右にナッジ
    W上に軽く押し上げる
    Sナッジダウン
    Fまたはカンマセンターボタン
    R または Esc戻るボタン

ADBコマンド

car_serviceコマンドを使用して、ロータリー入力イベントを挿入できます。これらのコマンドは、Android Automotive OS (AAOS) を実行するデバイスまたはエミュレータ上で実行できます。

car_service コマンドロータリー入力
adb shell cmd car_service inject-rotary反時計回りに回転
adb shell cmd car_service inject-rotary -c true時計回りに回転します
adb shell cmd car_service inject-rotary -dt 100 50反時計回りに複数回回転します (100 ミリ秒前と 50 ミリ秒前)
adb shell cmd car_service inject-key 282左にナッジする
adb shell cmd car_service inject-key 283右にナッジ
adb shell cmd car_service inject-key 280上に軽く押し上げる
adb shell cmd car_service inject-key 281ナッジダウン
adb shell cmd car_service inject-key 23中央ボタンのクリック
adb shell input keyevent inject-key 4 「戻る」ボタンのクリック

OEMロータリーコントローラー

ロータリー コントローラー ハードウェアが稼働している場合、これが最も現実的なオプションです。これは、高速回転をテストする場合に特に役立ちます。

フォーカス駐車場ビュー

FocusParkingView Car UI ライブラリ (car-ui-library)の透明なビューです。 RotaryServiceこれを使用して、ロータリー コントローラーのナビゲーションをサポートします。 FocusParkingViewレイアウト内の最初のフォーカス可能なビューである必要があります。すべてのFocusAreaの外側に配置する必要があります。各ウィンドウには 1 つのFocusParkingView必要です。 FocusParkingViewを含む car-ui-library 基本レイアウトをすでに使用している場合は、別のFocusParkingViewを追加する必要はありません。以下にRotaryPlaygroundFocusParkingViewの例を示します。

<FrameLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent">
   <com.android.car.ui.FocusParkingView
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>
   <FrameLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent"/>
</FrameLayout>

FocusParkingViewが必要な理由は次のとおりです。

  1. Android では、別のウィンドウにフォーカスが設定されている場合、フォーカスが自動的にクリアされません。前のウィンドウのフォーカスをクリアしようとすると、Android はそのウィンドウのビューに再度フォーカスを置き、その結果、2 つのウィンドウが同時にフォーカスされます。各ウィンドウにFocusParkingViewを追加すると、この問題を解決できます。このビューは透明であり、デフォルトのフォーカス ハイライトが無効になっているため、フォーカスされているかどうかに関係なく、ユーザーには表示されません。フォーカスを取得できるため、 RotaryServiceフォーカスをその上にパーキングして、フォーカスのハイライトを削除できます。
  2. 現在のウィンドウにFocusArea 1 つだけある場合、 FocusArea内でコントローラーを回転すると、 RotaryServiceによってフォーカスが右側のビューから左側のビューに (またはその逆に) 移動します。このビューを各ウィンドウに追加すると、問題を解決できます。 RotaryServiceフォーカス ターゲットがFocusParkingViewであると判断すると、ラップアラウンドが発生しようとしていると判断でき、その時点でフォーカスを移動しないことでラップアラウンドを回避します。
  3. 回転コントロールがアプリを起動すると、Android は最初のフォーカス可能なビュー (常にFocusParkingViewにフォーカスします。 FocusParkingView焦点を合わせるのに最適なビューを決定し、フォーカスを適用します。

フォーカス可能なビュー

RotaryService携帯電話に物理キーボードと方向パッドがあった時代に遡る、Android フレームワークの既存のビュー フォーカスの概念に基づいて構築されています。既存のandroid:nextFocusForward属性はロータリー用に再利用されますが ( FocusArea のカスタマイズ を参照)、 android:nextFocusLeftandroid:nextFocusRightandroid:nextFocusUp 、およびandroid:nextFocusDownは再利用されません。

RotaryServiceフォーカス可能なビューのみに焦点を当てます。 Buttonなどの一部のビューは通常、フォーカス可能です。 TextViewViewGroupなどの他のものは、通常はそうではありません。クリック可能なビューは自動的にフォーカス可能であり、ビューにクリック リスナーがある場合は自動的にクリック可能です。この自動ロジックの結果、望ましいフォーカス機能が得られる場合は、ビューのフォーカス機能を明示的に設定する必要はありません。自動ロジックによって望ましいフォーカス可能性が得られない場合は、 android:focusable属性をtrueまたはfalseに設定するか、 View.setFocusable(boolean)を使用してプログラムでビューのフォーカス可能性を設定します。 RotaryServiceそれに焦点を当てるには、ビューが次の要件を満たさなければなりません。

  • フォーカス可能
  • 有効
  • 見える
  • 幅と高さにゼロ以外の値を指定する

ビューがこれらの要件をすべて満たしていない場合 (たとえば、フォーカス可能だが無効なボタン)、ユーザーは回転コントロールを使用してビューにフォーカスすることはできません。無効なビューに焦点を当てたい場合は、 android:state_enabledではなくカスタム状態を使用して、Android がビューを無効とみなす必要があることを示さずにビューの表示方法を制御することを検討してください。アプリは、タップ時にビューが無効になる理由をユーザーに通知できます。次のセクションでは、これを行う方法について説明します。

カスタム状態

カスタム状態を追加するには:

  1. カスタム属性をビューに追加します。たとえば、 state_rotary_enabledカスタム状態をCustomViewビュー クラスに追加するには、
    <declare-styleable name="CustomView">
        <attr name="state_rotary_enabled" format="boolean" />
    </declare-styleable>
    
    を使用します。
  2. この状態を追跡するには、アクセサー メソッドとともにインスタンス変数をビューに追加します:
    private boolean mRotaryEnabled;
    public boolean getRotaryEnabled() { return mRotaryEnabled; }
    public void setRotaryEnabled(boolean rotaryEnabled) {
        mRotaryEnabled = rotaryEnabled;
    }
    
  3. ビューの作成時に属性の値を読み取るには:
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView);
    mRotaryEnabled = a.getBoolean(R.styleable.CustomView_state_rotary_enabled);
    
  4. ビュー クラスで、 onCreateDrawableState()メソッドをオーバーライドし、必要に応じてカスタム状態を追加します。例:
    @Override
    protected int[] onCreateDrawableState(int extraSpace) {
        if (mRotaryEnabled) extraSpace++;
        int[] drawableState = super.onCreateDrawableState(extraSpace);
        if (mRotaryEnabled) {
            mergeDrawableStates(drawableState, { R.attr.state_rotary_enabled });
        }
        return drawableState;
    }
    
  5. ビューのクリック ハンドラーの実行をその状態に応じて変更します。たとえば、クリック ハンドラーは何も行わないか、 mRotaryEnabled falseの場合にトーストをポップアップ表示する場合があります。
  6. ボタンを無効にするには、ビューの背景のドローアブルで、 android:state_enabled代わりにapp:state_rotary_enabled使用します。まだ持っていない場合は、
    xmlns:app="http://schemas.android.com/apk/res-auto"
    
    を追加する必要があります。
  7. いずれかのレイアウトでビューが無効になっている場合は、 android:enabled="false"app:state_rotary_enabled="false"に置き換えて、上記のようにapp名前空間を追加します。
  8. ビューがプログラムによって無効になっている場合は、 setEnabled()の呼び出しをsetRotaryEnabled()の呼び出しに置き換えます。

注目されるところ

FocusAreasを使用してフォーカス可能なビューをブロックに分割し、ナビゲーションを容易にし、他のアプリとの一貫性を保ちます。たとえば、アプリにツールバーがある場合、ツールバーはアプリの残りの部分とは別のFocusAreaにある必要があります。タブ バーやその他のナビゲーション要素も、アプリの残りの部分から分離する必要があります。大きなリストには通常、独自のFocusAreaが必要です。そうでない場合、ユーザーはリスト全体を順番に参照して一部のビューにアクセスする必要があります。

FocusArea car-ui-library のLinearLayoutのサブクラスです。この機能が有効になっている場合、 FocusArea 、その子孫の 1 つにフォーカスがあるときにハイライトを描画します。詳細については、 「フォーカス ハイライトのカスタマイズ」を参照してください。

レイアウト ファイルにナビゲーション ブロックを作成するときに、そのブロックのコンテナとしてLinearLayoutを使用する場合は、代わりにFocusAreaを使用してください。それ以外の場合は、ブロックをFocusAreaでラップします。

FocusArea別のFocusArea内にネストしないでください。そうすると、未定義のナビゲーション動作が発生します。すべてのフォーカス可能なビューがFocusArea内にネストされていることを確認します。

RotaryPlaygroundFocusAreaの例を以下に示します。

<com.android.car.ui.FocusArea
       android:layout_margin="16dp"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:orientation="vertical">
       <EditText
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:singleLine="true">
       </EditText>
   </com.android.car.ui.FocusArea>

FocusArea次のように機能します。

  1. 回転およびナッジアクションを処理するとき、 RotaryServiceビュー階層内でFocusAreaのインスタンスを検索します。
  2. 回転イベントを受信すると、 RotaryService 、同じFocusArea内でフォーカスを取得できる別の View にフォーカスを移動します。
  3. ナッジ イベントを受信すると、 RotaryServiceフォーカスを別の (通常は隣接する) FocusAreaにフォーカスできる別のビューに移動します。

レイアウトにFocusAreasを含めない場合、ルート ビューは暗黙的なフォーカス エリアとして扱われます。ユーザーはアプリ内を移動するためにナッジすることはできません。代わりに、すべてのフォーカス可能なビューを回転するため、ダイアログには適切な場合があります。

フォーカスエリアのカスタマイズ

2 つの標準の View 属性を使用して、ロータリー ナビゲーションをカスタマイズできます。

  • android:nextFocusForwardアプリ開発者はフォーカス領域の回転順序を指定できます。これは、キーボード ナビゲーションのタブ順序を制御するために使用される属性と同じです。ループを作成するためにこの属性を使用しないでください。代わりに、 app:wrapAround (以下を参照) を使用してループを作成します。
  • android:focusedByDefault使用すると、アプリ開発者はウィンドウ内のデフォルトのフォーカス ビューを指定できます。この属性とapp:defaultFocus (以下を参照) を同じFocusArea内で使用しないでください。

FocusAreaロータリー ナビゲーションをカスタマイズするためのいくつかの属性も定義します。これらの属性を使用して暗黙的なフォーカス領域をカスタマイズすることはできません。

  1. ( Android 11 QPR3、Android 11 Car、Android 12 )
    app:defaultFocus使用すると、ユーザーがこのFocusAreaに移動したときにフォーカスされる必要がある、フォーカス可能な子孫ビューの ID を指定できます。
  2. ( Android 11 QPR3、Android 11 Car、Android 12 )
    app:defaultFocusOverridesHistory trueに設定すると、このFocusArea内の別のビューがフォーカスされていたことを示す履歴がある場合でも、上で指定したビューにフォーカスを置くことができます。
  3. (アンドロイド12 )
    app:nudgeLeftShortcutapp:nudgeRightShortcutapp:nudgeUpShortcut 、およびapp:nudgeDownShortcutを使用して、ユーザーが特定の方向にナッジしたときにフォーカスされるフォーカス可能な子孫ビューの ID を指定します。詳細については、以下のナッジ ショートカットのコンテンツを参照してください。

    ( Android 11 QPR3、Android 11 Car、Android 12 では非推奨) app:nudgeShortcutapp:nudgeShortcutDirection 1 つのナッジ ショートカットのみをサポートしていました。

  4. ( Android 11 QPR3、Android 11 Car、Android 12 )
    このFocusArea内で回転をラップアラウンドできるようにするには、 app:wrapAround trueに設定します。これは、ビューが円形または楕円形に配置される場合に最も一般的に使用されます。
  5. ( Android 11 QPR3、Android 11 Car、Android 12 )
    このFocusAreaでハイライトのパディングを調整するには、 app:highlightPaddingStartapp:highlightPaddingEndapp:highlightPaddingTopapp:highlightPaddingBottomapp:highlightPaddingHorizontal 、およびapp:highlightPaddingVerticalを使用します。
  6. ( Android 11 QPR3、Android 11 Car、Android 12 )
    このFocusAreaの知覚される境界を調整してナッジ ターゲットを見つけるには、 app:startBoundOffsetapp:endBoundOffsetapp:topBoundOffsetapp:bottomBoundOffsetapp:horizontalBoundOffset 、およびapp:verticalBoundOffsetを使用します。
  7. ( Android 11 QPR3、Android 11 Car、Android 12 )
    指定された方向に隣接するFocusArea (または複数のエリア) の ID を明示的に指定するには、 app:nudgeLeftapp:nudgeRightapp:nudgeUp 、およびapp:nudgeDownを使用します。デフォルトで使用される幾何学的検索で目的のターゲットが見つからない場合にこれを使用します。

通常、ナッジはフォーカスエリア間を移動します。ただし、ナッジ ショートカットの場合、ナッジによって最初にFocusArea内に移動する場合があるため、ユーザーは次のFocusAreaに移動するために 2 回ナッジする必要がある場合があります。ナッジ ショートカットは、次の例のように、 FocusArea長いリストとそれに続くFloating Action Button が含まれる場合に便利です。

ナッジショートカット
図 3.ナッジ ショートカット

ナッジ ショートカットがなければ、ユーザーはリスト全体を回転して FAB に到達する必要があります。

フォーカスハイライトのカスタマイズ

上で述べたように、 RotaryService 、Android フレームワークの既存のビュー フォーカスの概念に基づいて構築されています。ユーザーが回転したり微調整したりすると、 RotaryServiceフォーカスを移動し、あるビューに焦点を当て、別のビューの焦点を外します。 Android では、ビューがフォーカスされているとき、ビューが次のようになります。

  • 独自のフォーカス ハイライトを指定すると、Android はビューのフォーカス ハイライトを描画します。
  • フォーカス ハイライトを指定せず、デフォルトのフォーカス ハイライトが無効になっていない場合、Android はビューのデフォルトのフォーカス ハイライトを描画します。

タッチ操作用に設計されたアプリでは、通常、適切なフォーカス ハイライトが指定されていません。

デフォルトのフォーカス ハイライトは Android フレームワークによって提供されており、OEM によってオーバーライドできます。アプリ開発者は、使用しているテーマがTheme.DeviceDefaultから派生している場合にそれを受け取ります。

一貫したユーザー エクスペリエンスを実現するには、可能な限りデフォルトのフォーカス ハイライトを使用してください。カスタム形状 (円形や錠剤の形など) のフォーカス ハイライトが必要な場合、またはTheme.DeviceDefaultから派生していないテーマを使用している場合は、 car-ui-library リソースを使用して独自のフォーカス ハイライトを指定します。それぞれの景色。

ビューのカスタム フォーカス ハイライトを指定するには、ビューの背景または前景のドローアブルを、ビューがフォーカスされているときに異なるドローアブルに変更します。通常は背景を変更します。次のドローアブルを正方形のビューの背景として使用すると、丸いフォーカス ハイライトが生成されます。

<selector xmlns:android="http://schemas.android.com/apk/res/android">
   <item android:state_focused="true" android:state_pressed="true">
      <shape android:shape="oval">
         <solid android:color="@color/car_ui_rotary_focus_pressed_fill_color"/>
         <stroke
            android:width="@dimen/car_ui_rotary_focus_pressed_stroke_width"
            android:color="@color/car_ui_rotary_focus_pressed_stroke_color"/>
      </shape>
   </item>
   <item android:state_focused="true">
      <shape android:shape="oval">
         <solid android:color="@color/car_ui_rotary_focus_fill_color"/>
         <stroke
            android:width="@dimen/car_ui_rotary_focus_stroke_width"
            android:color="@color/car_ui_rotary_focus_stroke_color"/>
      </shape>
   </item>
   <item>
      <ripple...>
         ...
      </ripple>
   </item>
</selector>

( Android 11 QPR3、Android 11 Car、Android 12 ) 上記のサンプル内の太字のリソース参照は、car-ui-library によって定義されたリソースを示します。 OEM は、指定したデフォルトのフォーカス ハイライトと一致するようにこれらをオーバーライドします。これにより、ユーザーがカスタム フォーカス ハイライトのあるビューとデフォルトのフォーカス ハイライトのあるビューの間を移動するときに、フォーカス ハイライトの色、ストローク幅などが変更されないことが保証されます。最後の項目はタッチに使用するリップルです。太字のリソースに使用されるデフォルト値は次のようになります。

太字のリソースのデフォルト値
図 4.太字のリソースのデフォルト値

さらに、以下の例のように、ユーザーの注意を引くためにボタンに単色の背景色が指定されている場合、カスタム フォーカス ハイライトが呼び出されます。これにより、フォーカスのハイライトが見えにくくなることがあります。この状況では、二次色を使用してカスタム フォーカス ハイライトを指定します。

単色の背景色
  • ( Android 11 QPR3、Android 11 Car、Android 12 )
    car_ui_rotary_focus_fill_secondary_color
    car_ui_rotary_focus_stroke_secondary_color
  • (アンドロイド12 )
    car_ui_rotary_focus_pressed_fill_secondary_color
    car_ui_rotary_focus_pressed_stroke_secondary_color

例えば:

圧迫されるのではなく集中する集中、圧迫
圧迫されるのではなく集中する集中、圧迫

回転スクロール

アプリがRecyclerViewを使用する場合は、代わりにCarUiRecyclerViewを使用する必要があります (SHOULD)。これにより、OEM のカスタマイズがすべてのCarUiRecyclerViewに適用されるため、UI が他の UI と一貫性を持つことが保証されます。

リスト内の要素がすべてフォーカス可能であれば、他に何もする必要はありません。ロータリー ナビゲーションにより、リスト内の要素間でフォーカスが移動し、リストがスクロールして、新しくフォーカスされた要素が表示されます。

( Android 11 QPR3、Android 11 Car、Android 12 )
フォーカス可能な要素とフォーカスできない要素が混在している場合、またはすべての要素がフォーカスできない場合は、回転スクロールを有効にすることができます。これにより、ユーザーは回転コントローラーを使用して、フォーカスできない項目をスキップすることなくリストを徐々にスクロールできます。回転スクロールを有効にするには、 app:rotaryScrollEnabled属性をtrueに設定します。

( Android 11 QPR3、Android 11 Car、Android 12 )
CarUiUtilssetRotaryScrollEnabled()メソッドを使用して、 av CarUiRecyclerViewを含む任意のスクロール可能なビューで回転スクロールを有効にすることができます。その場合は、次のことを行う必要があります。

  • スクロール可能なビューをフォーカス可能にして、そのフォーカス可能な子孫ビューが表示されていないときにフォーカスできるようにします。
  • setDefaultFocusHighlightEnabled(false)を呼び出して、スクロール可能なビューのデフォルトのフォーカス ハイライトを無効にし、スクロール可能なビューがフォーカスされていないようにします。
  • setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS)を呼び出して、スクロール可能なビューがその子孫の前にフォーカスされていることを確認します。
  • SOURCE_ROTARY_ENCODERAXIS_VSCROLLまたはAXIS_HSCROLLを使用して MotionEvents をリッスンし、スクロールする距離と方向 (標識による) を示します。

CarUiRecyclerViewで回転スクロールが有効になっており、ユーザーがフォーカス可能なビューが存在しない領域まで回転すると、スクロールバーがフォーカスされていることを示すかのように、スクロールバーが灰色から青色に変わります。必要に応じて、同様の効果を実装できます。

MotionEvent は、ソースを除き、マウスのスクロール ホイールによって生成されるイベントと同じです。

直接操作モード

通常、ナッジと回転はユーザー インターフェイス内を移動し、中央ボタンを押すとアクションが実行されますが、常にそうとは限りません。たとえば、ユーザーがアラームの音量を調整したい場合、回転コントローラを使用して音量スライダに移動し、中央ボタンを押し、コントローラを回転してアラームの音量を調整し、次に戻るボタンを押してナビゲーションに戻ります。 。これは直接操作 (DM)モードと呼ばれます。このモードでは、ロータリー コントローラーは、ナビゲーションではなくビューを直接操作するために使用されます。

2 つの方法のいずれかで DM を実装します。回転を処理するだけでよく、操作したいビューがACTION_SCROLL_FORWARDおよびACTION_SCROLL_BACKWARD AccessibilityEventに適切に応答する場合は、単純なメカニズムを使用してください。それ以外の場合は、高度なメカニズムを使用してください。

この単純なメカニズムがシステム ウィンドウの唯一のオプションです。アプリはどちらのメカニズムも使用できます。

シンプルな仕組み

( Android 11 QPR3、Android 11 Car、Android 12 )
アプリはDirectManipulationHelper.setSupportsRotateDirectly(View view, boolean enable)を呼び出す必要があります。 RotaryService 、ユーザーが DM モードであることを認識し、ビューがフォーカスされているときにユーザーがセンター ボタンを押すと DM モードに入ります。 DM モードの場合、回転はACTION_SCROLL_FORWARDまたはACTION_SCROLL_BACKWARDを実行し、ユーザーが [戻る] ボタンを押すと DM モードを終了します。シンプルなメカニズムにより、DM モードの開始時と終了時にビューの選択状態が切り替わります。

ユーザーが DM モードであることを視覚的に示すには、選択時にビューが異なるように表示します。たとえば、 android:state_selectedtrueの場合に背景を変更します。

先進の機構

アプリは、 RotaryService DM モードに入るときと、DM モードから出るときを決定します。一貫したユーザー エクスペリエンスを実現するには、DM ビューにフォーカスした状態で中央ボタンを押すと DM モードに入り、戻るボタンを押すと DM モードが終了する必要があります。センター ボタンやナッジが使用されていない場合は、DM モードを終了する別の方法として使用できます。マップなどのアプリの場合、DM を表すボタンを使用して DM モードに入ることができます。

高度な DM モードをサポートするには、次のビューを使用します。

  1. ( Android 11 QPR3、Android 11 Car、Android 12 ) DM モードに入るにはKEYCODE_DPAD_CENTERイベントをリッスンし、DM モードを終了するにはKEYCODE_BACKイベントをリッスンし、それぞれの場合にDirectManipulationHelper.enableDirectManipulationMode()を呼び出しなければなりません。これらのイベントをリッスンするには、次のいずれかを実行します。
    • OnKeyListenerを登録します。
    • または、
    • ビューを拡張し、そのdispatchKeyEvent()メソッドをオーバーライドします。
  2. ビューがナッジを処理する必要がある場合は、ナッジ イベント ( KEYCODE_DPAD_UPKEYCODE_DPAD_DOWNKEYCODE_DPAD_LEFT 、またはKEYCODE_DPAD_RIGHT ) をリッスンする必要があります。
  3. ビューが回転を処理したい場合は、 MotionEventをリッスンし、 AXIS_SCROLLで回転カウントを取得する必要があります。これを行うにはいくつかの方法があります。
    1. OnGenericMotionListenerを登録します。
    2. ビューを拡張し、そのdispatchTouchEvent()メソッドをオーバーライドします。
  4. DM モードでスタックすることを避けるために、ビューが属するフラグメントまたはアクティビティが対話型でない場合は、DM モードを終了しなければなりません。
  5. ビューが DM モードであることを示す視覚的な合図を提供すべきです。

DM モードを使用してマップをパンおよびズームするカスタム ビューのサンプルを以下に示します。

/** Whether this view is in DM mode. */
private boolean mInDirectManipulationMode;

/** Initializes the view. Called by the constructors. */ private void init() { setOnKeyListener((view, keyCode, keyEvent) -> { boolean isActionUp = keyEvent.getAction() == KeyEvent.ACTION_UP; switch (keyCode) { // Always consume KEYCODE_DPAD_CENTER and KEYCODE_BACK events. case KeyEvent.KEYCODE_DPAD_CENTER: if (!mInDirectManipulationMode && isActionUp) { mInDirectManipulationMode = true; DirectManipulationHelper.enableDirectManipulationMode(this, true); setSelected(true); // visually indicate DM mode } return true; case KeyEvent.KEYCODE_BACK: if (mInDirectManipulationMode && isActionUp) { mInDirectManipulationMode = false; DirectManipulationHelper.enableDirectManipulationMode(this, false); setSelected(false); } return true; // Consume controller nudge events only when in DM mode. // When in DM mode, nudges pan the map. case KeyEvent.KEYCODE_DPAD_UP: if (!mInDirectManipulationMode) return false; if (isActionUp) pan(0f, -10f); return true; case KeyEvent.KEYCODE_DPAD_DOWN: if (!mInDirectManipulationMode) return false; if (isActionUp) pan(0f, 10f); return true; case KeyEvent.KEYCODE_DPAD_LEFT: if (!mInDirectManipulationMode) return false; if (isActionUp) pan(-10f, 0f); return true; case KeyEvent.KEYCODE_DPAD_RIGHT: if (!mInDirectManipulationMode) return false; if (isActionUp) pan(10f, 0f); return true; // Don't consume other key events. default: return false; } });
// When in DM mode, rotation zooms the map. setOnGenericMotionListener(((view, motionEvent) -> { if (!mInDirectManipulationMode) return false; float scroll = motionEvent.getAxisValue(MotionEvent.AXIS_SCROLL); zoom(10 * scroll); return true; })); }
@Override public void onPause() { if (mInDirectManipulationMode) { // To ensure that the user doesn't get stuck in DM mode, disable DM mode // when the fragment is not interactive (e.g., a dialog shows up). mInDirectManipulationMode = false; DirectManipulationHelper.enableDirectManipulationMode(this, false); } super.onPause(); }

その他の例は、 RotaryPlaygroundプロジェクトにあります。

アクティビティビュー

ActivityView を使用する場合:

  • ActivityViewフォーカス可能であってはなりません。
  • ( Android 11 QPR3、Android 11 Car、Android 11 では非推奨)
    ActivityViewのコンテンツには、最初のフォーカス可能なビューとしてFocusParkingView含まれていなければならず、そのapp:shouldRestoreFocus属性はfalseでなければなりません。
  • ActivityViewのコンテンツにはandroid:focusByDefaultビューがあってはなりません。

ユーザーにとって、ActivityView は、フォーカス領域が ActivityView にまたがることができないことを除き、ナビゲーションに影響を与えるべきではありません。つまり、 ActivityViewの内部外部にコンテンツを含む単一のフォーカス領域を設定することはできません。 ActivityViewに FocusAreas を追加しない場合、 ActivityViewのビュー階層のルートは暗黙的なフォーカス領域とみなされます。

長押しすると動作するボタン

ほとんどのボタンは、クリックされると何らかのアクションを引き起こします。一部のボタンは長押しすると動作します。たとえば、早送りボタンと巻き戻しボタンは通常、押し続けると動作します。このようなボタンを回転式にサポートするには、次のようにKEYCODE_DPAD_CENTER KeyEventsをリッスンします。

mButton.setOnKeyListener((v, keyCode, event) ->
{
    if (keyCode != KEYCODE_DPAD_CENTER) {
        return false;
    }
    if (event.getAction() == ACTION_DOWN) {
        mButton.setPressed(true);
        mHandler.post(mRunnable);
    } else {
        mButton.setPressed(false);
        mHandler.removeCallbacks(mRunnable);
    }
    return true;
});

ここでは、 mRunnableアクション (巻き戻しなど) を実行し、遅延後に実行されるようにスケジュールを設定します。

タッチモード

ユーザーは、ロータリー コントローラーを使用するか、画面にタッチするという 2 つの方法で、ロータリー コントローラーを使用して車内のヘッド ユニットを操作できます。回転コントローラを使用すると、フォーカス可能なビューの 1 つが強調表示されます。画面をタッチしてもフォーカスハイライトは表示されません。ユーザーはいつでもこれらの入力モードを切り替えることができます。

  • 回転→タッチ。ユーザーが画面に触れると、フォーカスのハイライトが消えます。
  • → 回転をタッチします。ユーザーが中央ボタンをナッジ、回転、または押すと、フォーカスのハイライトが表示されます。

「戻る」ボタンと「ホーム」ボタンは入力モードには影響しません。

ロータリーは、Android のタッチ モードの既存の概念に便乗しています。 View.isInTouchMode()を使用すると、ユーザーが使用している入力モードを判断できます。 OnTouchModeChangeListenerを使用して変更をリッスンできます。これを使用して現在の入力モードに合わせてユーザー インターフェイスをカスタマイズできますが、大幅な変更は混乱を招く可能性があるため避けてください。

トラブルシューティング

タッチ用に設計されたアプリでは、フォーカス可能なビューが入れ子になっているのが一般的です。たとえば、 ImageButtonの周囲にFrameLayoutがあり、どちらもフォーカス可能であるとします。これはタッチには害はありませんが、ユーザーは次のインタラクティブ ビューに移動するためにコントローラーを 2 回回転する必要があるため、ロータリーのユーザー エクスペリエンスが低下する可能性があります。優れたユーザー エクスペリエンスを実現するために、Google では、外側のビューまたは内側のビューのどちらか一方をフォーカス可能にし、両方をフォーカス可能にすることは推奨しません。

ロータリー コントローラーを押したときにボタンまたはスイッチがフォーカスを失った場合は、次のいずれかの状況が該当する可能性があります。

  • ボタンが押されたため、ボタンまたはスイッチが(短時間または無期限に)無効になっています。いずれの場合も、これに対処するには次の 2 つの方法があります。
    • android:enabled状態をtrueのままにし、 「カスタム状態」で説明されているように、カスタム状態を使用してボタンまたはスイッチをグレー表示します。
    • コンテナを使用してボタンまたはスイッチを囲み、ボタンまたはスイッチの代わりにコンテナをフォーカス可能にします。 (クリック リスナーはコンテナ上に存在する必要があります。)
  • ボタンまたはスイッチが交換されています。たとえば、ボタンが押されたとき、またはスイッチが切り替えられたときに実行されるアクションによって、使用可能なアクションが更新され、新しいボタンが既存のボタンに置​​き換わる場合があります。これに対処するには 2 つの方法があります。
    • 新しいボタンやスイッチを作成する代わりに、既存のボタンやスイッチのアイコンやテキストを設定します。
    • 上記と同様に、ボタンまたはスイッチの周囲にフォーカス可能なコンテナを追加します。

ロータリー遊び場

RotaryPlaygroundロータリーのリファレンス アプリです。これを使用して、ロータリー機能をアプリに統合する方法を学習します。 RotaryPlaygroundエミュレーター ビルドと、Android Automotive OS (AAOS) を実行するデバイスのビルドに含まれています。

  • RotaryPlaygroundリポジトリ: packages/apps/Car/tests/RotaryPlayground/
  • バージョン: Android 11 QPR3、Android 11 Car、Android 12

RotaryPlaygroundアプリの左側に次のタブが表示されます。

  • カード。フォーカスできない要素やテキスト入力をスキップして、フォーカス領域の周りを移動するテストを行います。
  • 直接操作。シンプルおよび高度な直接操作モードをサポートするテスト ウィジェット。このタブは、特にアプリ ウィンドウ内で直接操作するためのものです。
  • システム UI の操作。単純な直接操作モードのみがサポートされているシステム ウィンドウで、直接操作をサポートするウィジェットをテストします。
  • グリッド。スクロールを伴う Z パターン回転ナビゲーションをテストします。
  • 通知。ヘッズアップ通知の内外へのナッジをテストします。
  • スクロール。フォーカス可能なコンテンツとフォーカスできないコンテンツを組み合わせてスクロールをテストします。
  • Webビュー。 WebView内のリンク間の移動をテストします。
  • カスタムのFocusArea FocusAreaカスタマイズをテストします。
    • 包み込む。
    • android:focusedByDefaultおよびapp:defaultFocus
    • 明示的なナッジターゲット。
    • ショートカットをナッジします。
    • フォーカス可能なビューのないFocusArea