以下の資料はアプリ開発者向けです。
アプリでロータリーをサポートするには、次のことを行う必要があります。
-
FocusParkingView
をそれぞれのアクティビティ レイアウトに配置します。 - ビューがフォーカス可能である (またはフォーカスできない) ことを確認します。
-
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
を使用することもできます。
エミュレートされたロータリー コントローラーにアクセスするには:
- ツールバーの下部にある 3 つの点をタップします。
- 拡張コントロール ウィンドウで[車のロータリー]を選択します。
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
を追加する必要はありません。以下にRotaryPlayground
のFocusParkingView
の例を示します。
<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
が必要な理由は次のとおりです。
- Android では、別のウィンドウにフォーカスが設定されている場合、フォーカスが自動的にクリアされません。前のウィンドウのフォーカスをクリアしようとすると、Android はそのウィンドウのビューに再度フォーカスを置き、その結果、2 つのウィンドウが同時にフォーカスされます。各ウィンドウに
FocusParkingView
を追加すると、この問題を解決できます。このビューは透明であり、デフォルトのフォーカス ハイライトが無効になっているため、フォーカスされているかどうかに関係なく、ユーザーには表示されません。フォーカスを取得できるため、RotaryService
フォーカスをその上にパーキングして、フォーカスのハイライトを削除できます。 - 現在のウィンドウに
FocusArea
1 つだけある場合、FocusArea
内でコントローラーを回転すると、RotaryService
によってフォーカスが右側のビューから左側のビューに (またはその逆に) 移動します。このビューを各ウィンドウに追加すると、問題を解決できます。RotaryService
フォーカス ターゲットがFocusParkingView
であると判断すると、ラップアラウンドが発生しようとしていると判断でき、その時点でフォーカスを移動しないことでラップアラウンドを回避します。 - 回転コントロールがアプリを起動すると、Android は最初のフォーカス可能なビュー (常に
FocusParkingView
にフォーカスします。FocusParkingView
焦点を合わせるのに最適なビューを決定し、フォーカスを適用します。
フォーカス可能なビュー
RotaryService
携帯電話に物理キーボードと方向パッドがあった時代に遡る、Android フレームワークの既存のビュー フォーカスの概念に基づいて構築されています。既存のandroid:nextFocusForward
属性はロータリー用に再利用されますが ( FocusArea のカスタマイズ を参照)、 android:nextFocusLeft
、 android:nextFocusRight
、 android:nextFocusUp
、およびandroid:nextFocusDown
は再利用されません。
RotaryService
フォーカス可能なビューのみに焦点を当てます。 Button
などの一部のビューは通常、フォーカス可能です。 TextView
やViewGroup
などの他のものは、通常はそうではありません。クリック可能なビューは自動的にフォーカス可能であり、ビューにクリック リスナーがある場合は自動的にクリック可能です。この自動ロジックの結果、望ましいフォーカス機能が得られる場合は、ビューのフォーカス機能を明示的に設定する必要はありません。自動ロジックによって望ましいフォーカス可能性が得られない場合は、 android:focusable
属性をtrue
またはfalse
に設定するか、 View.setFocusable(boolean)
を使用してプログラムでビューのフォーカス可能性を設定します。 RotaryService
それに焦点を当てるには、ビューが次の要件を満たさなければなりません。
- フォーカス可能
- 有効
- 見える
- 幅と高さにゼロ以外の値を指定する
ビューがこれらの要件をすべて満たしていない場合 (たとえば、フォーカス可能だが無効なボタン)、ユーザーは回転コントロールを使用してビューにフォーカスすることはできません。無効なビューに焦点を当てたい場合は、 android:state_enabled
ではなくカスタム状態を使用して、Android がビューを無効とみなす必要があることを示さずにビューの表示方法を制御することを検討してください。アプリは、タップ時にビューが無効になる理由をユーザーに通知できます。次のセクションでは、これを行う方法について説明します。
カスタム状態
カスタム状態を追加するには:
- カスタム属性をビューに追加します。たとえば、
state_rotary_enabled
カスタム状態をCustomView
ビュー クラスに追加するには、<declare-styleable name="CustomView"> <attr name="state_rotary_enabled" format="boolean" /> </declare-styleable>
を使用します。 - この状態を追跡するには、アクセサー メソッドとともにインスタンス変数をビューに追加します:
private boolean mRotaryEnabled; public boolean getRotaryEnabled() { return mRotaryEnabled; } public void setRotaryEnabled(boolean rotaryEnabled) { mRotaryEnabled = rotaryEnabled; }
- ビューの作成時に属性の値を読み取るには:
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView); mRotaryEnabled = a.getBoolean(R.styleable.CustomView_state_rotary_enabled);
- ビュー クラスで、
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; }
- ビューのクリック ハンドラーの実行をその状態に応じて変更します。たとえば、クリック ハンドラーは何も行わないか、
mRotaryEnabled
false
の場合にトーストをポップアップ表示する場合があります。 - ボタンを無効にするには、ビューの背景のドローアブルで、
android:state_enabled
代わりにapp:state_rotary_enabled
使用します。まだ持っていない場合は、xmlns:app="http://schemas.android.com/apk/res-auto"
を追加する必要があります。 - いずれかのレイアウトでビューが無効になっている場合は、
android:enabled="false"
をapp:state_rotary_enabled="false"
に置き換えて、上記のようにapp
名前空間を追加します。 - ビューがプログラムによって無効になっている場合は、
setEnabled()
の呼び出しをsetRotaryEnabled()
の呼び出しに置き換えます。
注目されるところ
FocusAreas
を使用してフォーカス可能なビューをブロックに分割し、ナビゲーションを容易にし、他のアプリとの一貫性を保ちます。たとえば、アプリにツールバーがある場合、ツールバーはアプリの残りの部分とは別のFocusArea
にある必要があります。タブ バーやその他のナビゲーション要素も、アプリの残りの部分から分離する必要があります。大きなリストには通常、独自のFocusArea
が必要です。そうでない場合、ユーザーはリスト全体を順番に参照して一部のビューにアクセスする必要があります。
FocusArea
car-ui-library のLinearLayout
のサブクラスです。この機能が有効になっている場合、 FocusArea
、その子孫の 1 つにフォーカスがあるときにハイライトを描画します。詳細については、 「フォーカス ハイライトのカスタマイズ」を参照してください。
レイアウト ファイルにナビゲーション ブロックを作成するときに、そのブロックのコンテナとしてLinearLayout
を使用する場合は、代わりにFocusArea
を使用してください。それ以外の場合は、ブロックをFocusArea
でラップします。
FocusArea
別のFocusArea
内にネストしないでください。そうすると、未定義のナビゲーション動作が発生します。すべてのフォーカス可能なビューがFocusArea
内にネストされていることを確認します。
RotaryPlayground
のFocusArea
の例を以下に示します。
<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
次のように機能します。
- 回転およびナッジアクションを処理するとき、
RotaryService
ビュー階層内でFocusArea
のインスタンスを検索します。 - 回転イベントを受信すると、
RotaryService
、同じFocusArea
内でフォーカスを取得できる別の View にフォーカスを移動します。 - ナッジ イベントを受信すると、
RotaryService
フォーカスを別の (通常は隣接する)FocusArea
にフォーカスできる別のビューに移動します。
レイアウトにFocusAreas
を含めない場合、ルート ビューは暗黙的なフォーカス エリアとして扱われます。ユーザーはアプリ内を移動するためにナッジすることはできません。代わりに、すべてのフォーカス可能なビューを回転するため、ダイアログには適切な場合があります。
フォーカスエリアのカスタマイズ
2 つの標準の View 属性を使用して、ロータリー ナビゲーションをカスタマイズできます。
-
android:nextFocusForward
アプリ開発者はフォーカス領域の回転順序を指定できます。これは、キーボード ナビゲーションのタブ順序を制御するために使用される属性と同じです。ループを作成するためにこの属性を使用しないでください。代わりに、app:wrapAround
(以下を参照) を使用してループを作成します。 -
android:focusedByDefault
使用すると、アプリ開発者はウィンドウ内のデフォルトのフォーカス ビューを指定できます。この属性とapp:defaultFocus
(以下を参照) を同じFocusArea
内で使用しないでください。
FocusArea
ロータリー ナビゲーションをカスタマイズするためのいくつかの属性も定義します。これらの属性を使用して暗黙的なフォーカス領域をカスタマイズすることはできません。
- ( Android 11 QPR3、Android 11 Car、Android 12 )
app:defaultFocus
使用すると、ユーザーがこのFocusArea
に移動したときにフォーカスされる必要がある、フォーカス可能な子孫ビューの ID を指定できます。 - ( Android 11 QPR3、Android 11 Car、Android 12 )
app:defaultFocusOverridesHistory
true
に設定すると、このFocusArea
内の別のビューがフォーカスされていたことを示す履歴がある場合でも、上で指定したビューにフォーカスを置くことができます。 - (アンドロイド12 )
app:nudgeLeftShortcut
、app:nudgeRightShortcut
、app:nudgeUpShortcut
、およびapp:nudgeDownShortcut
を使用して、ユーザーが特定の方向にナッジしたときにフォーカスされるフォーカス可能な子孫ビューの ID を指定します。詳細については、以下のナッジ ショートカットのコンテンツを参照してください。( Android 11 QPR3、Android 11 Car、Android 12 では非推奨)
app:nudgeShortcut
とapp:nudgeShortcutDirection
1 つのナッジ ショートカットのみをサポートしていました。 - ( Android 11 QPR3、Android 11 Car、Android 12 )
このFocusArea
内で回転をラップアラウンドできるようにするには、app:wrapAround
true
に設定します。これは、ビューが円形または楕円形に配置される場合に最も一般的に使用されます。 - ( Android 11 QPR3、Android 11 Car、Android 12 )
このFocusArea
でハイライトのパディングを調整するには、app:highlightPaddingStart
、app:highlightPaddingEnd
、app:highlightPaddingTop
、app:highlightPaddingBottom
、app:highlightPaddingHorizontal
、およびapp:highlightPaddingVertical
を使用します。 - ( Android 11 QPR3、Android 11 Car、Android 12 )
このFocusArea
の知覚される境界を調整してナッジ ターゲットを見つけるには、app:startBoundOffset
、app:endBoundOffset
、app:topBoundOffset
、app:bottomBoundOffset
、app:horizontalBoundOffset
、およびapp:verticalBoundOffset
を使用します。 - ( Android 11 QPR3、Android 11 Car、Android 12 )
指定された方向に隣接するFocusArea
(または複数のエリア) の ID を明示的に指定するには、app:nudgeLeft
、app:nudgeRight
、app:nudgeUp
、およびapp:nudgeDown
を使用します。デフォルトで使用される幾何学的検索で目的のターゲットが見つからない場合にこれを使用します。
通常、ナッジはフォーカスエリア間を移動します。ただし、ナッジ ショートカットの場合、ナッジによって最初にFocusArea
内に移動する場合があるため、ユーザーは次のFocusArea
に移動するために 2 回ナッジする必要がある場合があります。ナッジ ショートカットは、次の例のように、 FocusArea
長いリストとそれに続くFloating Action Button が含まれる場合に便利です。
ナッジ ショートカットがなければ、ユーザーはリスト全体を回転して 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 は、指定したデフォルトのフォーカス ハイライトと一致するようにこれらをオーバーライドします。これにより、ユーザーがカスタム フォーカス ハイライトのあるビューとデフォルトのフォーカス ハイライトのあるビューの間を移動するときに、フォーカス ハイライトの色、ストローク幅などが変更されないことが保証されます。最後の項目はタッチに使用するリップルです。太字のリソースに使用されるデフォルト値は次のようになります。
さらに、以下の例のように、ユーザーの注意を引くためにボタンに単色の背景色が指定されている場合、カスタム フォーカス ハイライトが呼び出されます。これにより、フォーカスのハイライトが見えにくくなることがあります。この状況では、二次色を使用してカスタム フォーカス ハイライトを指定します。
- ( 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 )
CarUiUtils
のsetRotaryScrollEnabled()
メソッドを使用して、 av CarUiRecyclerView
を含む任意のスクロール可能なビューで回転スクロールを有効にすることができます。その場合は、次のことを行う必要があります。
- スクロール可能なビューをフォーカス可能にして、そのフォーカス可能な子孫ビューが表示されていないときにフォーカスできるようにします。
-
setDefaultFocusHighlightEnabled(false)
を呼び出して、スクロール可能なビューのデフォルトのフォーカス ハイライトを無効にし、スクロール可能なビューがフォーカスされていないようにします。 -
setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS)
を呼び出して、スクロール可能なビューがその子孫の前にフォーカスされていることを確認します。 -
SOURCE_ROTARY_ENCODER
とAXIS_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_selected
がtrue
の場合に背景を変更します。
先進の機構
アプリは、 RotaryService
DM モードに入るときと、DM モードから出るときを決定します。一貫したユーザー エクスペリエンスを実現するには、DM ビューにフォーカスした状態で中央ボタンを押すと DM モードに入り、戻るボタンを押すと DM モードが終了する必要があります。センター ボタンやナッジが使用されていない場合は、DM モードを終了する別の方法として使用できます。マップなどのアプリの場合、DM を表すボタンを使用して DM モードに入ることができます。
高度な DM モードをサポートするには、次のビューを使用します。
- ( Android 11 QPR3、Android 11 Car、Android 12 ) DM モードに入るには
KEYCODE_DPAD_CENTER
イベントをリッスンし、DM モードを終了するにはKEYCODE_BACK
イベントをリッスンし、それぞれの場合にDirectManipulationHelper.enableDirectManipulationMode()
を呼び出しなければなりません。これらのイベントをリッスンするには、次のいずれかを実行します。-
OnKeyListener
を登録します。 または、 - ビューを拡張し、その
dispatchKeyEvent()
メソッドをオーバーライドします。
-
- ビューがナッジを処理する必要がある場合は、ナッジ イベント (
KEYCODE_DPAD_UP
、KEYCODE_DPAD_DOWN
、KEYCODE_DPAD_LEFT
、またはKEYCODE_DPAD_RIGHT
) をリッスンする必要があります。 - ビューが回転を処理したい場合は、
MotionEvent
をリッスンし、AXIS_SCROLL
で回転カウントを取得する必要があります。これを行うにはいくつかの方法があります。-
OnGenericMotionListener
を登録します。 - ビューを拡張し、その
dispatchTouchEvent()
メソッドをオーバーライドします。
-
- DM モードでスタックすることを避けるために、ビューが属するフラグメントまたはアクティビティが対話型でない場合は、DM モードを終了しなければなりません。
- ビューが 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
。