開發應用程式

以下內容適用於應用程式開發人員。

如要讓應用程式支援旋轉功能,您必須:

  1. 在相應的活動版面配置中放置 FocusParkingView
  2. 確認可 (或不可) 聚焦的檢視畫面。
  3. 使用 FocusArea 包裝所有可聚焦的 View (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. 輕觸工具列底部的三點圖示:

    存取模擬的旋轉控制器
    圖 1.存取模擬的旋轉控制器
  2. 在「Extended controls」視窗中,選取「Car rotary」

    選取 Car rotary
    圖 2.選取「車輛旋轉」

USB 鍵盤

  • 將 USB 鍵盤插入執行 Android Automotive OS (AAOS) 的裝置,在某些情況下,這會導致螢幕小鍵盤無法顯示。
  • 使用 userdebugeng 版本。
  • 啟用按鍵事件篩選功能:
    adb shell settings put secure android.car.ROTARY_KEY_EVENT_FILTER 1
    
  • 請參閱下表,找出每個動作的對應鍵:
    旋轉動作
    Q 逆時針旋轉
    E 順時針旋轉
    A 左推
    D 右推
    W 上推
    S 下推
    F 或 Comma 中間按鈕
    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

FocusParkingViewCar UI 程式庫 (car-ui-library) 中的透明檢視畫面。RotaryService 會使用此方法支援旋轉控制器導覽功能。FocusParkingView 必須是版面配置中第一個可聚焦的檢視畫面。必須放置在所有 FocusArea 之外。每個視窗都必須有一個 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 會重新將焦點放在該視窗中的檢視畫面,導致兩個視窗同時獲得焦點。將 FocusParkingView 新增至每個視窗,即可修正這個問題。這個檢視畫面是透明的,且預設的焦點醒目顯示功能已停用,因此無論是否有焦點,使用者都無法看到這個檢視畫面。它可以接收焦點,讓 RotaryService 停放焦點,以便移除焦點醒目標示。
  2. 如果目前視窗中只有一個 FocusArea,在 FocusArea 中旋轉控制器會導致 RotaryService 將焦點從右側的檢視畫面移至左側的檢視畫面 (反之亦然)。將這個檢視畫面新增至每個視窗即可解決問題。當 RotaryService 判斷焦點目標為 FocusParkingView 時,它可以判斷即將發生的迴繞,並在那個時間點避免迴繞,方法是不要移動焦點。
  3. 旋轉控制項啟動應用程式時,Android 會將焦點設在第一個可聚焦的檢視畫面,而這個檢視畫面一律是 FocusParkingViewFocusParkingView 會判斷要聚焦的最佳檢視畫面,然後套用聚焦效果。

可聚焦的檢視畫面

RotaryService 建構在 Android 架構的現有觀點焦點概念上,可追溯至手機有實體鍵盤和 D-pad 的時代。現有的 android:nextFocusForward 屬性已重新用於 rotary (請參閱「FocusArea 自訂」),但 android:nextFocusLeftandroid:nextFocusRightandroid:nextFocusUpandroid:nextFocusDown 則未重新用。

RotaryService 只會聚焦於可聚焦的檢視畫面。Button 等部分檢視畫面通常可供聚焦。其他類型 (例如 TextViewViewGroup) 通常不會。可點擊的檢視區塊會自動聚焦,且在具有點擊事件監聽器時,檢視區塊會自動變成可點擊。如果這個自動邏輯可產生所需的焦點可用性,您就不需要明確設定檢視畫面的焦點可用性。如果自動邏輯無法產生所需的焦點可用性,請將 android:focusable 屬性設為 truefalse,或者以程式設計方式使用 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. 讓檢視畫面的點擊處理常式根據狀態執行不同的動作。舉例來說,點擊處理常式可能什麼都不做,或是在 mRotaryEnabledfalse 時彈出 Toast。
  6. 如要讓按鈕顯示為已停用,請在檢視畫面的背景可繪項目中使用 app:state_rotary_enabled,而非 android:state_enabled。如果您還沒有這些項目,請新增:
    xmlns:app="http://schemas.android.com/apk/res-auto"
    
  7. 如果您的檢視區塊在任何版面配置中都已停用,請將 android:enabled="false" 替換為 app:state_rotary_enabled="false",然後如上方所述新增 app 命名空間。
  8. 如果您是透過程式輔助方式停用檢視畫面,請將對 setEnabled() 的呼叫替換為對 setRotaryEnabled() 的呼叫。

FocusArea

使用 FocusAreas 將可聚焦的檢視區塊劃分為區塊,以便更輕鬆地導覽,並與其他應用程式保持一致。舉例來說,如果應用程式有工具列,則工具列應位於與應用程式其他部分分開的 FocusArea 中。分頁列和其他導覽元素也應與應用程式其他部分分開。大型清單通常應有自己的 FocusArea。否則,使用者必須輪流瀏覽整個清單,才能存取某些檢視畫面。

FocusArea 是 car-ui-library 中 LinearLayout 的子類別。啟用這項功能後,FocusArea 會在其中一個子項獲得焦點時繪製醒目顯示。詳情請參閱「自訂焦點重點」。

在版面配置檔案中建立導覽區塊時,如果您想使用 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 的運作方式如下:

  1. 在處理旋轉和微調動作時,RotaryService 會在檢視區塊階層中尋找 FocusArea 的例項。
  2. 收到旋轉事件時,RotaryService 會將焦點移至另一個可在同一個 FocusArea 中接收焦點的檢視區塊。
  3. 收到推送事件時,RotaryService 會將焦點移至另一個可在另一個 (通常是相鄰) FocusArea 中接收焦點的檢視區塊。

如果版面配置中沒有任何 FocusAreas,系統會將根檢視區塊視為隱含焦點區域。使用者無法輕推以在應用程式中瀏覽,而是會輪流瀏覽所有可聚焦的檢視畫面,這可能適用於對話方塊。

FocusArea 自訂

您可以使用兩個標準的 View 屬性自訂旋轉導覽功能:

  • android:nextFocusForward 可讓應用程式開發人員指定焦點區域中的旋轉順序。這項屬性與用於控制鍵盤導覽的分頁順序相同。請勿使用這項屬性建立迴圈。請改用 app:wrapAround (請參閱下方說明) 建立迴圈。
  • android:focusedByDefault 可讓應用程式開發人員在視窗中指定預設焦點檢視畫面。請勿在同一個 FocusArea 中使用這項屬性和 app:defaultFocus (請參閱下方說明)。

FocusArea 也定義了一些屬性,可自訂旋轉導覽功能。您無法使用這些屬性自訂隱含焦點區域。

  1. (Android 11 QPR3、Android 11 Car、Android 12)
    app:defaultFocus 可用於指定可聚焦的子項檢視畫面 ID,當使用者輕觸此 FocusArea 時,系統應將焦點放在該 ID 上。
  2. (Android 11 QPR3、Android 11 Car、Android 12)
    app:defaultFocusOverridesHistory 可設為 true,讓上述指定的檢視畫面取得焦點,即使歷史記錄指出此 FocusArea 中的另一個檢視畫面已取得焦點也是如此。
  3. (Android 12)
    請使用 app:nudgeLeftShortcutapp:nudgeRightShortcutapp:nudgeUpShortcutapp:nudgeDownShortcut 指定可聚焦的子項檢視畫面 ID,當使用者在特定方向輕推時,系統應聚焦於該 ID。如需瞭解詳情,請參閱下方的推薦捷徑內容。

    (Android 11 QPR3、Android 11 Car,已在 Android 12 中淘汰) app:nudgeShortcutapp:nudgeShortcutDirection 僅支援一個推送捷徑。

  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:highlightPaddingHorizontalapp:highlightPaddingVertical
  6. (Android 11 QPR3、Android 11 Car、Android 12)
    如要調整這個 FocusArea 的認知邊界,以便找到推送目標,請使用 app:startBoundOffsetapp:endBoundOffsetapp:topBoundOffsetapp:bottomBoundOffsetapp:horizontalBoundOffsetapp:verticalBoundOffset
  7. (Android 11 QPR3、Android 11 Car、Android 12)
    如要在指定方向明確指定相鄰 FocusArea (或區域) 的 ID,請使用 app:nudgeLeftapp:nudgeRightapp:nudgeUpapp:nudgeDown。如果預設使用的幾何搜尋功能找不到所需目標,請使用此選項。

推送通常會在 FocusAreas 之間切換。不過,如果使用推送捷徑,推送動作有時會先在 FocusArea 內瀏覽,因此使用者可能需要推送兩次才能前往下一個 FocusArea。當 FocusArea 包含長清單,後面接著是浮動操作按鈕時,推薦捷徑就很實用,如以下範例所示:

推送捷徑
圖 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
  • (Android 12)
    car_ui_rotary_focus_pressed_fill_secondary_color
    car_ui_rotary_focus_pressed_stroke_secondary_color

例如:

聚焦,未按下 已聚焦、已按下
已聚焦,但未按下 已聚焦、已按下

旋轉捲動

如果您的應用程式使用 RecyclerView,請改用 CarUiRecyclerView。這樣可確保您的 UI 與其他 UI 一致,因為 OEM 的客製化設定會套用至所有 CarUiRecyclerView

如果清單中的元素皆可聚焦,您就不需要採取其他行動。旋轉導覽會透過清單中的元素移動焦點,並捲動清單,讓新焦點元素顯示。

(Android 11 QPR3、Android 11 Car、Android 12)
如果有可聚焦和不可聚焦的元素混合,或是所有元素皆不可聚焦,您可以啟用旋轉捲動功能,讓使用者使用旋轉控制器逐漸捲動清單,而不會略過不可聚焦的項目。如要啟用旋轉捲動功能,請將 app:rotaryScrollEnabled 屬性設為 true

(Android 11 QPR3、Android 11 Car、Android 12)
您可以使用 CarUiUtils 中的 setRotaryScrollEnabled() 方法,在任何可捲動的檢視畫面 (包括 avCarUiRecyclerView) 中啟用旋轉捲動功能。如要這樣做,您必須:

  • 讓可捲動的檢視區塊可聚焦,這樣在沒有任何可聚焦的子項檢視區塊可見時,檢視區塊就能聚焦。
  • 呼叫 setDefaultFocusHighlightEnabled(false) 可停用捲動檢視畫面上的預設焦點醒目顯示功能,讓捲動檢視畫面不會顯示為已聚焦。
  • 請呼叫 setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS),確保可捲動的檢視區塊在其子項之前獲得焦點。
  • 使用 SOURCE_ROTARY_ENCODERAXIS_VSCROLLAXIS_HSCROLL 監聽 MotionEvents,以便指出捲動距離和方向 (透過標誌)。

CarUiRecyclerView 啟用旋轉捲動功能,且使用者旋轉至沒有可聚焦檢視畫面的區域時,捲軸會從灰色變為藍色,以表示捲軸已聚焦。您可以視需要實作類似效果。

除了來源之外,MotionEvents 與滑鼠捲軸產生的事件相同。

直接操控模式

一般來說,輕推和旋轉可在使用者介面中進行導覽,而按下中間按鈕則可執行操作,但實際情況不一定如此。舉例來說,如果使用者想調整鬧鐘音量,可能會使用旋轉控制器前往音量滑桿,然後按下中央按鈕,旋轉控制器調整鬧鐘音量,接著按下返回按鈕返回導覽畫面。這就是所謂的「直接操控」模式。在這個模式中,旋轉控制器會用於直接與檢視畫面互動,而非用於導覽。

實作 DM 的方式有兩種。如果您只需要處理旋轉,且要操作的檢視畫面可適當回應 ACTION_SCROLL_FORWARDACTION_SCROLL_BACKWARD AccessibilityEvent,請使用簡單機制。否則,請使用進階機制。

簡單機制是系統視窗中唯一的選項;應用程式可使用任一機制。

簡單機制

(Android 11 QPR3、Android 11 Car、Android 12)
應用程式應呼叫 DirectManipulationHelper.setSupportsRotateDirectly(View view, boolean enable)RotaryService 會在使用者處於 DM 模式時進行辨識,並在使用者在將焦點放在檢視畫面時按下 Center 按鈕時進入 DM 模式。在 DM 模式下,旋轉會執行 ACTION_SCROLL_FORWARDACTION_SCROLL_BACKWARD,並在使用者按下返回按鈕時退出 DM 模式。當您進入及離開 DM 模式時,這個簡單機制會切換檢視畫面的選取狀態。

如要提供視覺提示,讓使用者知道自己處於 DM 模式,請在選取時顯示不同的檢視畫面。例如,當 android:state_selectedtrue 時變更背景。

進階機制

應用程式會決定 RotaryService 何時進入及退出 DM 模式。為提供一致的使用者體驗,當 DM 檢視畫面處於聚焦狀態時,按下中間按鈕應會進入 DM 模式,而「返回」按鈕則應退出 DM 模式。如果未使用「居中」按鈕和/或「輕推」功能,則可改用這兩種方式退出 DM 模式。對於地圖等應用程式,可使用代表 DM 的按鈕進入 DM 模式。

如要支援進階 DM 模式,檢視區塊必須符合下列條件:

  1. (Android 11 QPR3、Android 11 Car、Android 12) 必須監聽 KEYCODE_DPAD_CENTER 事件才能進入 DM 模式,並監聽 KEYCODE_BACK 事件才能退出 DM 模式,在每個情況下都會呼叫 DirectManipulationHelper.enableDirectManipulationMode()。如要監聽這些事件,請執行下列任一操作:
    • 註冊 OnKeyListener
    • 擴充檢視畫面,然後覆寫其 dispatchKeyEvent() 方法。
  2. 如果檢視區塊應處理自動提醒,則應監聽自動提醒事件 (KEYCODE_DPAD_UPKEYCODE_DPAD_DOWNKEYCODE_DPAD_LEFTKEYCODE_DPAD_RIGHT)。
  3. 如果檢視區塊要處理旋轉,則應監聽 MotionEvent 並在 AXIS_SCROLL 中取得旋轉計數。您可以透過下列方式進行:
    1. 註冊 OnGenericMotionListener
    2. 擴充檢視畫面並覆寫其 dispatchTouchEvent() 方法。
  4. 為避免卡在 DM 模式,當檢視區塊所屬的 Fragment 或 Activity 無法互動時,請務必退出 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 時:

  • ActivityView 不應可供聚焦。
  • (Android 11 QPR3、Android 11 Car,已在 Android 11 中淘汰)
    ActivityView 的內容必須包含 FocusParkingView 做為第一個可聚焦的檢視畫面,且其 app:shouldRestoreFocus 屬性必須為 false
  • ActivityView 的內容不應有 android:focusByDefault 檢視畫面。

對使用者而言,ActivityView 不應影響導覽,但焦點區域不得跨越 ActivityView。換句話說,您無法在單一焦點區域中,同時顯示 ActivityView 內部和外部的內容。如果您未將任何 FocusAreas 新增至 ActivityViewActivityView 中檢視區塊階層的根目錄會視為隱含的焦點區域。

按住時會運作的按鈕

大部分的按鈕在點選後都會觸發某些動作。部分按鈕則需要按住才能運作。舉例來說,快速前進和快轉按鈕通常會在按住時運作。如要讓這類按鈕支援旋轉功能,請如以下方式監聽 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 會執行動作 (例如倒帶),並排程在延遲後執行。

觸控模式

使用者可以透過旋轉控制器或觸控螢幕,以兩種方式與車用運算主機互動。使用旋轉控制器時,系統會醒目顯示其中一個可聚焦的檢視畫面。觸碰螢幕時,系統不會顯示焦點醒目顯示。使用者隨時可以切換以下輸入模式:

  • Rotary → touch。使用者輕觸螢幕時,焦點高亮顯示會消失。
  • 輕觸 → 旋轉。當使用者輕推、旋轉或按下「Center」按鈕時,系統就會顯示焦點高亮顯示。

返回和主畫面按鈕不會影響輸入模式。

旋轉模式會附加 Android 現有的觸控模式概念。您可以使用 View.isInTouchMode() 判斷使用者使用的輸入模式。您可以使用 OnTouchModeChangeListener 來監聽變更。雖然這可用於為目前輸入模式自訂使用者介面,但請避免進行任何重大變更,因為這可能會造成困擾。

疑難排解

在設計給觸控螢幕的應用程式中,通常會使用可聚焦的巢狀檢視區塊。舉例來說,ImageButton 周圍可能有 FrameLayout,兩者皆可聚焦。這對觸控操作沒有影響,但會導致旋轉操作的使用者體驗不佳,因為使用者必須旋轉控制器兩次才能移動到下一個互動檢視畫面。為了提供良好的使用者體驗,Google 建議您將外部檢視畫面或內部檢視畫面設為可聚焦,但兩者不能同時設為可聚焦。

如果按下旋轉控制器時,按鈕或切換鈕失去焦點,可能會發生下列任一情況:

  • 按鈕或切換鈕因按下而停用 (暫時或無限期)。無論是哪種情況,您可以透過以下兩種方式解決這個問題:
    • android:enabled 狀態設為 true,並使用自訂狀態將按鈕或切換鈕設為灰色,如「自訂狀態」一文所述。
    • 使用容器環繞按鈕或切換鈕,並讓容器可聚焦,而非按鈕或切換鈕。(點擊事件監聽器必須位於容器上)。
  • 按鈕或切換鈕正在更換。舉例來說,按下按鈕或切換開關時所採取的動作,可能會觸發可用動作的重新整理作業,導致新按鈕取代現有按鈕。解決方法有兩種:
    • 請勿建立新的按鈕或切換鈕,而是設定現有按鈕或切換鈕的圖示和/或文字。
    • 如上所述,請在按鈕或切換鈕周圍加入可聚焦的容器。

RotaryPlayground

RotaryPlayground 是旋轉輸入的參考應用程式。您可以使用這份文件瞭解如何將旋轉功能整合至應用程式。RotaryPlayground 已納入模擬器版本,以及搭載 Android Automotive OS (AAOS) 的裝置版本。

  • RotaryPlayground 存放區:packages/apps/Car/tests/RotaryPlayground/
  • 版本:Android 11 QPR3、Android 11 Car 和 Android 12

RotaryPlayground 應用程式會在左側顯示下列分頁:

  • 資訊卡:測試在焦點區域中導覽、略過無法聚焦的元素和文字輸入。
  • 直接操控。測試支援簡易和進階直接操作模式的小工具。這個分頁專門用於在應用程式視窗中直接操作。
  • 系統 UI 操作。測試在系統視窗中支援直接操作的設定小工具,這些視窗僅支援簡單的直接操作模式。
  • 格狀檢視。使用捲動功能測試 z 型旋轉導覽。
  • 通知。測試抬頭通知的推送和退出功能。
  • 捲動。測試捲動可聚焦和不可聚焦的內容組合。
  • WebView。測試在 WebView 中透過連結導覽。
  • 自訂 FocusArea測試 FocusArea 自訂項目:
    • 繞網進球。
    • android:focusedByDefaultapp:defaultFocus
    • 明確的推送目標。
    • 輕推捷徑。
    • FocusArea 沒有可聚焦的檢視畫面。