Phát triển ứng dụng

Tài liệu sau đây là dành cho nhà phát triển ứng dụng.

Để ứng dụng hỗ trợ ở chế độ xoay, bạn PHẢI:

  1. Đặt FocusParkingView vào bố cục hoạt động tương ứng.
  2. Đảm bảo các chế độ xem có thể (hoặc không thể) làm tâm điểm.
  3. Dùng FocusArea để bao quanh tất cả khung hiển thị có thể làm tâm điểm, ngoại trừ FocusParkingView.

Từng tác vụ này được nêu chi tiết dưới đây, sau khi bạn thiết lập môi trường của bạn để phát triển các ứng dụng hỗ trợ xoay.

Thiết lập bộ điều khiển xoay

Trước khi có thể bắt đầu phát triển các ứng dụng hỗ trợ xoay, bạn cần một bộ điều khiển xoay hoặc quảng cáo đứng. Bạn có các lựa chọn như mô tả ở bên dưới.

Trình mô phỏng

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

Bạn cũng có thể sử dụng aosp_car_x86_64-userdebug.

Cách truy cập vào bộ điều khiển xoay được mô phỏng:

  1. Nhấn vào biểu tượng ba dấu chấm ở cuối thanh công cụ:

    Truy cập vào bộ điều khiển xoay được mô phỏng
    Hình 1. Truy cập vào bộ điều khiển xoay được mô phỏng
  2. Chọn Chế độ xoay ô tô trong cửa sổ điều khiển mở rộng:

    Chọn chế độ xoay trên ô tô
    Hình 2. Chọn chế độ xoay của ô tô

Bàn phím USB

  • Cắm bàn phím USB vào thiết bị chạy Android Automotive OS (AAOS). Trong một số trường hợp, thao tác này sẽ ngăn bàn phím ảo xuất hiện.
  • Sử dụng bản dựng userdebug hoặc eng.
  • Bật tính năng lọc sự kiện chính:
    adb shell settings put secure android.car.ROTARY_KEY_EVENT_FILTER 1
    
  • Hãy xem bảng dưới đây để tìm khoá tương ứng cho từng hành động:
    Khoá Thao tác xoay
    Hỏi Xoay ngược chiều kim đồng hồ
    E Xoay theo chiều kim đồng hồ
    A Dịch sang trái
    D Dịch sang phải
    W Dịch lên trên
    S Dịch xuống dưới
    F hoặc Dấu phẩy Nút giữa
    R hoặc Esc Nút quay lại

Lệnh ADB

Bạn có thể dùng các lệnh car_service để chèn các sự kiện nhập dữ liệu xoay. Các lệnh này có thể chạy trên các thiết bị chạy Android Automotive OS (AAOS) hoặc trên một trình mô phỏng.

lệnh car_service Phương thức nhập dữ liệu xoay
adb shell cmd car_service inject-rotary Xoay ngược chiều kim đồng hồ
adb shell cmd car_service inject-rotary -c true Xoay theo chiều kim đồng hồ
adb shell cmd car_service inject-rotary -dt 100 50 Xoay ngược chiều kim đồng hồ nhiều lần (100 mili giây trước và 50 mili giây trước)
adb shell cmd car_service inject-key 282 Dịch sang trái
adb shell cmd car_service inject-key 283 Dịch sang phải
adb shell cmd car_service inject-key 280 Dịch lên trên
adb shell cmd car_service inject-key 281 Dịch xuống dưới
adb shell cmd car_service inject-key 23 Nhấp vào nút giữa
adb shell input keyevent inject-key 4 Nhấp vào nút quay lại

Bộ điều khiển xoay của OEM

Khi phần cứng bộ điều khiển xoay của bạn được thiết lập và chạy, đây là tùy chọn thực tế. Điều này đặc biệt hữu ích khi kiểm thử tính năng xoay nhanh.

Chế độ xem đỗ xe lấy nét

FocusParkingView là chế độ xem trong suốt Thư viện Giao diện người dùng ô tô (car-ui-library). RotaryService sử dụng lớp này để hỗ trợ điều hướng bộ điều khiển xoay. FocusParkingView phải là chế độ xem có thể làm tâm điểm đầu tiên trong bố cục. Đoạn mã sự kiện phải được đặt bên ngoài tất cả các FocusArea. Mỗi cửa sổ phải có một FocusParkingView. Nếu bạn đã sử dụng bố cục cơ sở car-ui-library, chứa FocusParkingView, bạn không cần thêm đoạn mã khác FocusParkingView. Dưới đây là một ví dụ về FocusParkingView trong RotaryPlayground.

<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>

Dưới đây là lý do bạn cần có FocusParkingView:

  1. Android không tự động xoá tiêu điểm khi đặt tiêu điểm trong một cửa sổ khác. Nếu bạn cố gắng xóa tiêu điểm trong cửa sổ trước, Android sẽ lấy nét lại chế độ xem trong cửa sổ đó. dẫn đến việc hai cửa sổ được lấy làm tiêu điểm cùng một lúc. Thêm một FocusParkingView cho mỗi cửa sổ có thể khắc phục sự cố này. Chế độ xem này có màu trong suốt và được làm nổi bật theo tiêu điểm mặc định bị tắt để người dùng không thể nhìn thấy tiện ích bổ sung này bất kể có lấy tiêu điểm hay không. Thành phần này có thể lấy tiêu điểm để RotaryService có thể thu hút tiêu điểm vào đó để xoá tiêu điểm được làm nổi bật.
  2. Nếu chỉ có một FocusArea trong cửa sổ hiện tại, hãy xoay bộ điều khiển trong FocusArea khiến RotaryService di chuyển tâm điểm từ chế độ xem ở bên phải sang chế độ xem ở bên trái (và ngược lại). Đang thêm chế độ xem này cho từng cửa sổ có thể khắc phục sự cố. Khi RotaryService xác định tiêu điểm mục tiêu là FocusParkingView, nó có thể xác định vòng bao quanh là sắp xảy ra tại thời điểm mà nó tránh được vòng bao quanh bằng cách không di chuyển tiêu điểm.
  3. Khi chế độ điều khiển xoay chạy một ứng dụng, Android sẽ tập trung vào khung hiển thị có thể làm tâm điểm đầu tiên, luôn là FocusParkingView. FocusParkingView xác định chế độ xem tối ưu để lấy tiêu điểm, sau đó áp dụng tiêu điểm.

Chế độ xem có thể lấy tiêu điểm

RotaryService được xây dựng dựa trên khung Android hiện tại về tiêu điểm xem, có từ khi điện thoại có bàn phím vật lý và bàn phím D. Thuộc tính android:nextFocusForward hiện có được chuyển đổi mục đích sử dụng cho chế độ xoay (xem tuỳ chỉnh FocusArea), nhưng android:nextFocusLeft, android:nextFocusRight, android:nextFocusUpandroid:nextFocusDown thì không.

RotaryService chỉ tập trung vào các chế độ xem có thể làm tâm điểm. Một số chế độ xem, chẳng hạn như Button, thường có thể lấy làm tâm điểm. Các thuộc tính khác, chẳng hạn như TextViewViewGroup, thường thì không. Chế độ xem có thể nhấp vào có thể tự động lấy tiêu điểm và chế độ xem sẽ tự động có thể nhấp khi chúng có trình nghe lượt nhấp. Nếu logic tự động này dẫn đến kết quả mong muốn bạn không cần đặt rõ ràng khả năng lấy tiêu điểm của thành phần hiển thị. Nếu logic tự động không để có thể lấy nét như mong muốn, hãy đặt thuộc tính android:focusable thành true hoặc false hoặc đặt khả năng lấy tiêu điểm của thành phần hiển thị theo cách lập trình với View.setFocusable(boolean). Để RotaryService tập trung vào đó, một khung hiển thị PHẢI đáp ứng các yêu cầu sau:

  • Có thể lấy tiêu điểm
  • Đã bật
  • Đã hiển thị
  • Có các giá trị chiều rộng và chiều cao khác 0

Nếu một khung hiển thị không đáp ứng tất cả các yêu cầu này (ví dụ: một nút có thể làm tâm điểm nhưng bị vô hiệu hoá), người dùng không thể sử dụng nút điều khiển xoay để tập trung vào đó. Nếu bạn muốn tập trung vào các chế độ xem đã tắt, hãy cân nhắc sử dụng trạng thái tuỳ chỉnh thay vì android:state_enabled để kiểm soát cách khung hiển thị đó xuất hiện mà không cho biết rằng Android nên xem như nó đã tắt. Ứng dụng của bạn có thể cung cấp thông tin cho người dùng lý do chế độ xem bị tắt khi được nhấn. Phần tiếp theo sẽ giải thích cách thực hiện việc này.

Trạng thái tuỳ chỉnh

Cách thêm trạng thái tuỳ chỉnh:

  1. Cách thêm thuộc tính tuỳ chỉnh vào chế độ xem của bạn. Ví dụ: để thêm trạng thái tuỳ chỉnh state_rotary_enabled vào giá trị Lớp khung hiển thị CustomView, hãy sử dụng:
    <declare-styleable name="CustomView">
        <attr name="state_rotary_enabled" format="boolean" />
    </declare-styleable>
    
  2. Để theo dõi trạng thái này, hãy thêm một biến thực thể vào khung hiển thị của bạn cùng với các phương thức trình truy cập:
    private boolean mRotaryEnabled;
    public boolean getRotaryEnabled() { return mRotaryEnabled; }
    public void setRotaryEnabled(boolean rotaryEnabled) {
        mRotaryEnabled = rotaryEnabled;
    }
    
  3. Cách đọc giá trị của thuộc tính khi khung hiển thị được tạo:
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView);
    mRotaryEnabled = a.getBoolean(R.styleable.CustomView_state_rotary_enabled);
    
  4. Trong lớp thành phần hiển thị, hãy ghi đè phương thức onCreateDrawableState(), sau đó thêm trạng thái tuỳ chỉnh, khi thích hợp. Ví dụ:
    @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. Làm cho trình xử lý lượt nhấp của khung hiển thị hoạt động khác nhau tuỳ thuộc vào trạng thái của trình xử lý lượt nhấp. Ví dụ: trình xử lý lượt nhấp có thể không làm gì hoặc có thể bật thông báo ngắn khi mRotaryEnabledfalse.
  6. Để nút này có vẻ như bị vô hiệu hoá, trong đối tượng có thể vẽ ở chế độ nền của chế độ xem, hãy sử dụng app:state_rotary_enabled thay vì android:state_enabled. Nếu chưa có, bạn cần phải thêm:
    xmlns:app="http://schemas.android.com/apk/res-auto"
    
  7. Nếu chế độ xem của bạn bị tắt trong bất kỳ bố cục nào, hãy thay thế android:enabled="false" bằng app:state_rotary_enabled="false", sau đó thêm không gian tên app, như trên.
  8. Nếu chế độ xem của bạn bị tắt theo phương thức lập trình, hãy thay thế các lệnh gọi thành setEnabled() bằng các lệnh gọi tới setRotaryEnabled().

Khu vực tiêu điểm

Dùng FocusAreas để phân vùng chế độ xem có thể làm tâm điểm thành các khối để hỗ trợ điều hướng dễ dàng hơn và nhất quán với các ứng dụng khác. Ví dụ: nếu ứng dụng có thanh công cụ, thanh công cụ phải được đặt trong một FocusArea riêng biệt với phần còn lại của ứng dụng. Thanh tab và các thành phần điều hướng khác cũng cần được tách biệt với phần còn lại của ứng dụng. Danh sách lớn thường sẽ có FocusArea riêng. Nếu không, người dùng phải xoay vòng toàn bộ danh sách để truy cập vào một số chế độ xem.

FocusArea là một lớp con của LinearLayout trong car-ui-library. Khi tính năng này được bật, FocusArea sẽ vẽ một vùng đánh dấu khi một trong các thành phần con được lấy làm tâm điểm. Để tìm hiểu thêm, hãy xem Tuỳ chỉnh tính năng làm nổi bật tiêu điểm.

Khi tạo khối điều hướng trong tệp bố cục, nếu bạn định sử dụng LinearLayout làm vùng chứa khối đó, hãy sử dụng FocusArea. Nếu không, hãy gói khối trong một FocusArea.

KHÔNG lồng FocusArea vào một FocusArea khác. Làm như vậy sẽ dẫn đến hành vi điều hướng không xác định. Đảm bảo rằng tất cả các chế độ xem có thể làm tâm điểm được lồng trong một FocusArea.

Ví dụ về FocusArea trong RotaryPlayground được hiển thị dưới đây:

<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 hoạt động như sau:

  1. Khi xử lý các thao tác xoay và nhắc, RotaryService sẽ tìm các thực thể FocusArea trong hệ phân cấp khung hiển thị.
  2. Khi nhận được một sự kiện xoay, RotaryService sẽ di chuyển tiêu điểm đến một sự kiện khác Khung hiển thị có thể lấy tiêu điểm trong cùng một FocusArea.
  3. Khi nhận được một sự kiện nhắc, RotaryService sẽ di chuyển tiêu điểm sang một khung hiển thị khác có thể lấy tiêu điểm ở một FocusArea (thường liền kề) khác.

Nếu bạn không đưa FocusAreas nào vào bố cục, thì khung hiển thị gốc sẽ được xử lý làm khía cạnh ngầm tập trung. Người dùng không thể nhắc nhở hoạt động trong ứng dụng. Thay vào đó, họ sẽ xoay qua tất cả các khung hiển thị có thể làm tâm điểm (có thể vừa đủ cho hộp thoại).

Tuỳ chỉnh FocusArea

Bạn có thể dùng 2 thuộc tính Khung hiển thị chuẩn để tuỳ chỉnh chế độ điều hướng xoay:

  • android:nextFocusForward cho phép nhà phát triển ứng dụng chỉ định chế độ xoay theo một khía cạnh trọng tâm. Đây cũng là thuộc tính được dùng để kiểm soát Thứ tự thẻ cho điều hướng bằng bàn phím. KHÔNG sử dụng thuộc tính này để tạo vòng lặp. Thay vào đó, hãy sử dụng app:wrapAround (xem bên dưới) để tạo vòng lặp.
  • android:focusedByDefault cho phép nhà phát triển ứng dụng chỉ định chế độ xem tiêu điểm mặc định trong cửa sổ. KHÔNG sử dụng thuộc tính này và app:defaultFocus (xem bên dưới) trong cùng một FocusArea.

FocusArea cũng xác định một số thuộc tính để tuỳ chỉnh thao tác xoay. Bạn không thể tuỳ chỉnh các khu vực tâm điểm ngầm ẩn bằng những thuộc tính này.

  1. (Android 11 QPR3, Ô tô Android 11, Android 12)
    app:defaultFocus có thể được dùng để chỉ định mã nhận dạng của một khung hiển thị con có thể làm tâm điểm. Cần tập trung vào thời điểm người dùng nhắc đến FocusArea này.
  2. (Android 11 QPR3, Ô tô Android 11, Android 12)
    app:defaultFocusOverridesHistory có thể được đặt thành true để làm cho chế độ xem được chỉ định ở trên lấy tiêu điểm ngay cả khi để cho biết một chế độ xem khác trong FocusArea này đã được lấy làm tâm điểm.
  3. (Android 12)
    Sử dụng app:nudgeLeftShortcut, app:nudgeRightShortcut app:nudgeUpShortcutapp:nudgeDownShortcut để chỉ định mã nhận dạng của một khung hiển thị con có thể làm tâm điểm. Chế độ xem này cần được lấy tiêu điểm khi người dùng nhắc theo một hướng nhất định. Để tìm hiểu thêm, hãy xem nội dung dành cho nhắc nhở hoạt động lối tắt ở bên dưới.

    (Android 11 QPR3, Ô tô Android 11, không dùng nữa trong Android 12) app:nudgeShortcutapp:nudgeShortcutDirection chỉ hỗ trợ một phím tắt nhắc nhở hoạt động.

  4. (Android 11 QPR3, Ô tô Android 11, Android 12)
    Để bật tính năng xoay bao quanh FocusArea này, hãy app:wrapAround có thể đặt thành true. Thành phần này thường được sử dụng nhất khi các thành phần hiển thị được sắp xếp trong một hình tròn hoặc hình bầu dục.
  5. (Android 11 QPR3, Ô tô Android 11, Android 12)
    Để điều chỉnh khoảng đệm của vùng đánh dấu trong FocusArea này, sử dụng app:highlightPaddingStart, app:highlightPaddingEnd, app:highlightPaddingTop, app:highlightPaddingBottom, app:highlightPaddingHorizontal, và app:highlightPaddingVertical.
  6. (Android 11 QPR3, Ô tô Android 11, Android 12)
    Để điều chỉnh giới hạn quan sát được của FocusArea này nhằm tìm mục tiêu nhắc nhở, sử dụng app:startBoundOffset, app:endBoundOffset, app:topBoundOffset, app:bottomBoundOffset, app:horizontalBoundOffsetapp:verticalBoundOffset.
  7. (Android 11 QPR3, Ô tô Android 11, Android 12)
    Để chỉ định rõ ID của một FocusArea (hoặc các khu vực) liền kề theo chỉ dẫn đã cho, sử dụng app:nudgeLeft, app:nudgeRight, app:nudgeUpapp:nudgeDown. Sử dụng tùy chọn này khi tìm kiếm hình học được sử dụng theo mặc định nhưng không tìm thấy mục tiêu mong muốn.

Tính năng Nhắc nhở hoạt động thường di chuyển giữa các FocusAreas. Tuy nhiên, với các lối tắt nhắc nhở, nhắc nhở hoạt động đôi khi điều hướng lần đầu trong FocusArea để người dùng có thể cần nhắc hai lần để chuyển đến FocusArea tiếp theo. Phím tắt nhắc rất hữu ích khi FocusArea chứa một danh sách dài, theo sau là một Nút hành động nổi, như trong ví dụ dưới đây:

Dịch lối tắt
Hình 3. Dịch lối tắt

Nếu không có lối tắt nhắc nhở hoạt động, người dùng sẽ phải xoay qua toàn bộ danh sách để truy cập FAB.

Tuỳ chỉnh tính năng làm nổi bật tiêu điểm

Như đã lưu ý ở trên, RotaryService được xây dựng dựa trên khái niệm hiện có của khung Android tiêu điểm xem. Khi người dùng xoay và nhắc, RotaryService sẽ di chuyển tiêu điểm xung quanh, lấy tiêu điểm một thành phần hiển thị và không lấy tiêu điểm thành tiêu điểm khác. Trong Android, khi một khung hiển thị được lấy làm tâm điểm, nếu khung hiển thị đó:

  • Đã chỉ định điểm làm nổi bật tiêu điểm riêng, Android sẽ vẽ tiêu điểm của thành phần hiển thị này.
  • Không chỉ định tính năng làm nổi bật tiêu điểm và tính năng làm nổi bật tiêu điểm mặc định không bị tắt, Android vẽ tiêu điểm đánh dấu mặc định cho khung hiển thị.

Ứng dụng được thiết kế cho thao tác chạm thường không chỉ định các điểm nổi bật cần lấy nét thích hợp.

Làm nổi bật tiêu điểm mặc định do khung Android cung cấp và có thể ghi đè bởi OEM (Nhà sản xuất thiết bị gốc). Nhà phát triển ứng dụng sẽ nhận được mã này khi giao diện họ đang sử dụng được lấy từ Theme.DeviceDefault.

Để mang đến trải nghiệm nhất quán cho người dùng, hãy sử dụng tính năng làm nổi bật tiêu điểm theo mặc định bất cứ khi nào có thể. Nếu bạn cần làm nổi bật tiêu điểm theo hình dạng tuỳ chỉnh (ví dụ: hình tròn hoặc hình viên thuốc), hoặc nếu bạn đang sử dụng một giao diện không bắt nguồn từ Theme.DeviceDefault, hãy dùng car-ui-library để chỉ định tiêu điểm nổi bật của riêng bạn cho mỗi khung hiển thị.

Để chỉ định một tiêu điểm làm nổi bật tuỳ chỉnh cho một khung hiển thị, hãy thay đổi đối tượng có thể vẽ ở nền trước hoặc nền trước của chế độ xem thành một đối tượng có thể vẽ sẽ khác khi chế độ xem được lấy làm tâm điểm. Thông thường, bạn sẽ thay đổi nền. Các đối tượng có thể vẽ sau đây, nếu được dùng làm nền cho chế độ xem hình vuông, sẽ tạo ra một tiêu điểm dạng tròn:

<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, Ô tô Android 11, Android 12) Tệp tham chiếu tài nguyên In đậm trong mẫu ở trên xác định các tài nguyên do car-ui-library xác định. OEM ghi đè các giá trị này để đảm bảo tính nhất quán với tiêu điểm đánh dấu mặc định mà họ chỉ định. Điều này đảm bảo rằng màu làm nổi bật tiêu điểm, chiều rộng của nét vẽ, v.v. không thay đổi khi người dùng di chuyển giữa một khung hiển thị có tiêu điểm tuỳ chỉnh và khung hiển thị có làm nổi bật tiêu điểm mặc định. Mục cuối cùng là một gợn sóng dùng để chạm. Giá trị mặc định được sử dụng cho các tài nguyên in đậm xuất hiện như sau:

Giá trị mặc định cho tài nguyên in đậm
Hình 4. Giá trị mặc định cho tài nguyên in đậm

Ngoài ra, tính năng làm nổi bật tiêu điểm tuỳ chỉnh sẽ được gọi khi một nút nhấn có một khối màu nền để thu hút sự chú ý của người dùng, như trong ví dụ dưới đây. Điều này có thể giúp điểm nổi bật lấy tiêu điểm khó nhìn. Trong trường hợp này, hãy chỉ định đánh dấu tiêu điểm tuỳ chỉnh bằng cách sử dụng Màu secondary (phụ):

Màu nền đồng nhất
  • (Android 11 QPR3, Ô tô Android 11, 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

Ví dụ:

Đã lấy tiêu điểm, không nhấn Đã lấy tiêu điểm, đã nhấn
Lấy nét, không nhấn Lấy nét, nhấn

Cuộn xoay

Nếu ứng dụng của bạn dùng RecyclerView, bạn NÊN sử dụng CarUiRecyclerView. Điều này đảm bảo rằng giao diện người dùng nhất quán với các lỗi khác vì chế độ tuỳ chỉnh của OEM áp dụng cho tất cả các CarUiRecyclerView.

Nếu tất cả các phần tử trong danh sách của bạn đều có thể làm tâm điểm, thì bạn không cần làm gì thêm. Chế độ điều hướng xoay sẽ di chuyển tiêu điểm qua các phần tử trong danh sách, còn danh sách sẽ cuộn để hiển thị phần tử mới được đặt tiêu điểm.

(Android 11 QPR3, Ô tô Android 11, Android 12)
Nếu có sự kết hợp giữa nội dung có thể làm tâm điểm và không thể làm tâm điểm hoặc nếu tất cả các phần tử đều không thể làm tâm điểm, bạn có thể bật tính năng cuộn xoay để cho phép người dùng sử dụng bộ điều khiển xoay để cuộn dần qua danh sách mà không bỏ qua mục không thể đặt tiêu điểm. Để bật tính năng cuộn xoay, hãy đặt app:rotaryScrollEnabled cho true.

(Android 11 QPR3, Ô tô Android 11, Android 12)
Bạn có thể bật tính năng cuộn xoay trong bất kỳ chế độ xem có thể cuộn, bao gồm cả avCarUiRecyclerView, với setRotaryScrollEnabled() trong CarUiUtils. Nếu bạn làm như vậy, bạn cần:

  • Đặt chế độ xem có thể cuộn làm tâm điểm để có thể lấy tiêu điểm khi không có các khung hiển thị con có thể làm tâm điểm đều có thể nhìn thấy
  • Tắt tính năng làm nổi bật tiêu điểm mặc định trên chế độ xem có thể cuộn bằng cách gọi setDefaultFocusHighlightEnabled(false) để chế độ xem có thể cuộn có vẻ không được đặt tiêu điểm,
  • Đảm bảo rằng chế độ xem có thể cuộn được lấy tiêu điểm trước các thành phần con cháu bằng cách gọi setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS).
  • Nghe MotionEvents với SOURCE_ROTARY_ENCODERAXIS_VSCROLL hoặc AXIS_HSCROLL để cho biết khoảng cách cuộn và hướng (thông qua dấu hiệu).

Khi tính năng cuộn xoay được bật trên CarUiRecyclerView và người dùng xoay sang một khu vực không có chế độ xem có thể làm tâm điểm, thanh cuộn sẽ chuyển từ màu xám sang màu xanh dương, như thể để cho biết thanh cuộn được lấy làm tâm điểm. Bạn có thể triển khai hiệu ứng tương tự nếu muốn.

MotionEvents cũng giống như các sự kiện do con lăn chuột tạo ra, ngoại trừ nguồn.

Chế độ thao túng trực tiếp

Thông thường, tính năng nhắc nhở hoạt động và xoay sẽ di chuyển trong giao diện người dùng, trong khi thao tác nhấn nút giữa hành động, mặc dù điều này không phải lúc nào cũng đúng. Ví dụ: nếu người dùng muốn điều chỉnh âm lượng báo thức, họ có thể sử dụng bộ điều khiển xoay để điều hướng đến thanh trượt âm lượng, nhấn Nút giữa, xoay bộ điều khiển để điều chỉnh âm lượng chuông báo, sau đó nhấn nút Quay lại để quay lại điều hướng. Đây được gọi là chế độ thao tác trực tiếp (DM). Trong phần này chế độ xem này, bộ điều khiển xoay được dùng để tương tác trực tiếp với thành phần hiển thị thay vì điều hướng.

Triển khai DM theo một trong hai cách. Nếu bạn chỉ cần xử lý chế độ xoay và khung hiển thị mà bạn muốn để thao tác phản hồi với ACTION_SCROLL_FORWARDACTION_SCROLL_BACKWARD AccessibilityEvent một cách thích hợp, hãy sử dụng đơn giản. Nếu không, hãy sử dụng cơ chế nâng cao.

Cơ chế đơn giản là lựa chọn duy nhất trong các cửa sổ hệ thống; ứng dụng có thể sử dụng một trong hai cơ chế.

Cơ chế đơn giản

(Android 11 QPR3, Ô tô Android 11, Android 12)
Ứng dụng của bạn sẽ gọi DirectManipulationHelper.setSupportsRotateDirectly(View view, boolean enable). RotaryService nhận ra khi người dùng đang ở chế độ DM và chuyển sang chế độ DM khi người dùng nhấn vào nút Căn giữa khi một khung hiển thị được lấy tiêu điểm. Khi ở chế độ DM, hoạt động xoay sẽ thực hiện ACTION_SCROLL_FORWARD hoặc ACTION_SCROLL_BACKWARD và thoát khỏi chế độ tin nhắn trực tiếp khi người dùng nhấn vào nút Quay lại. Cơ chế đơn giản chuyển đổi trạng thái đã chọn của chế độ xem khi vào và thoát khỏi chế độ DM.

Để đưa ra chỉ dẫn trực quan cho biết người dùng đang ở chế độ tin nhắn trực tiếp (DM), hãy làm cho chế độ xem của bạn có vẻ khác khi được chọn. Ví dụ: thay đổi nền khi android:state_selectedtrue.

Cơ chế nâng cao

Ứng dụng này xác định thời điểm RotaryService chuyển sang và thoát khỏi chế độ DM. Để có sự nhất quán trải nghiệm người dùng, nhấn nút Trung tâm với chế độ xem DM tập trung sẽ chuyển sang chế độ DM và nút Quay lại sẽ thoát khỏi chế độ DM. Nếu bạn không sử dụng nút Căn giữa và/hoặc lời nhắc, chúng có thể là những cách khác để thoát khỏi chế độ DM. Đối với các ứng dụng như Maps, một nút để thể hiện Có thể dùng DM để chuyển sang chế độ DM.

Để hỗ trợ chế độ DM nâng cao, một chế độ xem sẽ:

  1. (Android 11 QPR3, Ô tô Android 11, Android 12) PHẢI theo dõi KEYCODE_DPAD_CENTER sự kiện để vào chế độ DM và theo dõi sự kiện KEYCODE_BACK để thoát khỏi chế độ DM, gọi DirectManipulationHelper.enableDirectManipulationMode() trong mỗi trường hợp. Để nghe những sự kiện này, hãy làm theo một trong những cách sau:
    • Đăng ký một OnKeyListener.
    • hoặc
    • Mở rộng khung hiển thị, sau đó ghi đè phương thức dispatchKeyEvent().
  2. NÊN theo dõi các sự kiện nhắc nhở (KEYCODE_DPAD_UP, KEYCODE_DPAD_DOWN, KEYCODE_DPAD_LEFT hoặc KEYCODE_DPAD_RIGHT) nếu khung hiển thị này xử lý thông báo nhắc nhở.
  3. NÊN nghe MotionEvent và nhận số lượng xoay trong AXIS_SCROLL nếu khung hiển thị muốn xử lý việc xoay. Có một số cách để thực hiện việc này:
    1. Đăng ký một OnGenericMotionListener.
    2. Mở rộng khung hiển thị và ghi đè phương thức dispatchTouchEvent().
  4. Để tránh bị mắc kẹt ở chế độ DM, PHẢI thoát khỏi chế độ DM khi Mảnh hoặc Hoạt động hiển thị thuộc về không tương tác.
  5. NÊN cung cấp chỉ dẫn bằng hình ảnh để cho biết chế độ xem đang ở chế độ DM.

Dưới đây là mẫu của chế độ xem tuỳ chỉnh sử dụng chế độ DM để xoay và thu phóng bản đồ:

/** 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(); }

Bạn có thể xem thêm ví dụ trong Dự án RotaryPlayground.

ActivityView (chế độ xem hoạt động)

Khi sử dụng ActivityView:

  • ActivityView không được đặt làm tâm điểm.
  • (Android 11 QPR3, Ô tô Android 11, không dùng nữa trong Android 11)
    Nội dung của ActivityView PHẢI chứa FocusParkingView là chế độ xem có thể làm tâm điểm đầu tiên và app:shouldRestoreFocus thuộc tính PHẢI là false.
  • Nội dung của ActivityView không được có android:focusByDefault lượt xem.

Đối với người dùng, ActivityViews sẽ không ảnh hưởng đến hoạt động điều hướng ngoại trừ tiêu điểm đó không thể mở rộng ActivityViews. Nói cách khác, bạn không thể có một lĩnh vực trọng tâm duy nhất có nội dung bên trong bên ngoài ActivityView. Nếu bạn không thêm bất kỳ FocusAreas nào vào ActivityView của bạn, gốc của hệ phân cấp khung hiển thị trong ActivityView được coi là một khu vực tập trung ngầm ẩn.

Các nút hoạt động khi nhấn giữ

Hầu hết các nút đều thực hiện một số thao tác khi nhấp vào. Một số nút sẽ hoạt động khi bạn nhấn giữ. Ví dụ: các nút Tua nhanh và Tua lại thường hoạt động khi nhấn giữ. Để thực hiện điều này các nút hỗ trợ xoay, hãy lắng nghe KEYCODE_DPAD_CENTER KeyEvents như sau:

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;
});

Trong đó mRunnable thực hiện một hành động (chẳng hạn như tua lại) và tự lên lịch để sẽ chạy sau độ trễ.

Chế độ cảm ứng

Người dùng có thể sử dụng bộ điều khiển xoay để tương tác với đầu phát trung tâm trong ô tô theo hai cách: bằng cách sử dụng bộ điều khiển xoay hoặc chạm vào màn hình. Khi sử dụng bộ điều khiển xoay, một trong các chế độ xem có thể làm tâm điểm được làm nổi bật. Khi chạm vào màn hình, không có phần nổi bật lấy tiêu điểm sẽ xuất hiện. Người dùng có thể chuyển đổi giữa các chế độ nhập này bất kỳ lúc nào:

  • Xoay → chạm. Khi người dùng chạm vào màn hình, điểm nổi bật lấy tiêu điểm sẽ biến mất.
  • Chạm → xoay. Khi người dùng di chuyển, xoay hoặc nhấn nút giữa, làm nổi bật tiêu điểm.

Nút Quay lại và Màn hình chính không ảnh hưởng đến chế độ nhập.

Xoay vòng khái niệm về chế độ cảm ứng. Bạn có thể sử dụng View.isInTouchMode() để xác định chế độ nhập mà người dùng đang sử dụng. Bạn có thể sử dụng OnTouchModeChangeListener để theo dõi những thay đổi. Mặc dù công cụ này có thể được dùng để tuỳ chỉnh giao diện người dùng của bạn cho chế độ nhập, hãy tránh mọi thay đổi lớn vì chúng có thể gây khó chịu.

Khắc phục sự cố

Trong một ứng dụng được thiết kế cho thao tác chạm, thông thường, chúng tôi sẽ lồng ghép các khung hiển thị có thể làm tâm điểm. Ví dụ: có thể có FrameLayout xung quanh ImageButton, cả hai đều có thể làm tâm điểm. Điều này không có hại khi chạm vào nhưng có thể dẫn đến trải nghiệm người dùng khi xoay vì người dùng phải xoay bộ điều khiển hai lần để di chuyển tới chế độ xem tương tác tiếp theo. Để có được trải nghiệm người dùng tốt, Google khuyên bạn nên thực hiện một trong hai chế độ xem bên ngoài hoặc chế độ xem bên trong có thể làm tâm điểm, nhưng không thể làm tâm điểm cả hai.

Nếu một nút hoặc công tắc bị mất tiêu điểm khi được nhấn qua bộ điều khiển xoay, thì một trong các điều kiện sau có thể áp dụng:

  • Nút hoặc nút chuyển đang bị tắt (trong một thời gian ngắn hoặc vô thời hạn) do đang được nhấn. Trong cả hai trường hợp, có hai cách để giải quyết vấn đề này:
    • Để trạng thái android:enabledtrue và sử dụng chế độ tuỳ chỉnh trạng thái để chuyển nút sang màu xám hoặc chuyển đổi như được mô tả trong Trạng thái tuỳ chỉnh.
    • Dùng một vùng chứa để bao quanh nút hoặc nút chuyển và làm cho vùng chứa đó có thể làm tâm điểm thay vì dùng nút hoặc công tắc. (Trình nghe lượt nhấp phải nằm trên vùng chứa.)
  • Nút hoặc công tắc đang được thay thế. Ví dụ: hành động được thực hiện khi nút được nhấn hoặc nút bật/tắt có thể kích hoạt làm mới các hành động hiện có khiến các nút mới thay thế các nút hiện có. Có hai cách để giải quyết vấn đề này:
    • Thay vì tạo một nút hoặc nút chuyển mới, hãy đặt biểu tượng và/hoặc văn bản của nút hoặc công tắc hiện có.
    • Như trên, hãy thêm một vùng chứa có thể làm tâm điểm xung quanh nút hoặc nút chuyển.

Sân chơi xoay

RotaryPlayground là một ứng dụng tham khảo cho chế độ xoay. Hãy sử dụng công cụ này để tìm hiểu cách tích hợp các tính năng xoay trong ứng dụng của bạn. RotaryPlayground có trong các bản dựng trình mô phỏng và trong dành cho các thiết bị chạy Android Automotive OS (AAOS).

  • Kho lưu trữ RotaryPlayground: packages/apps/Car/tests/RotaryPlayground/
  • Phiên bản: Android 11 QPR3, Android 11 Car, và Android 12

Ứng dụng RotaryPlayground hiện các thẻ sau ở bên trái:

  • Thẻ. Thử nghiệm việc di chuyển xung quanh các khu vực trọng tâm, bỏ qua những phần không thể làm tâm điểm và nhập văn bản.
  • Thao túng trực tiếp. Tiện ích kiểm thử hỗ trợ đơn giản và nâng cao chế độ thao túng trực tiếp. Thẻ này dành riêng cho thao tác trực tiếp trong cửa sổ ứng dụng.
  • Thao tác với giao diện người dùng hệ thống. Kiểm thử các tiện ích hỗ trợ thao tác trực tiếp trong cửa sổ hệ thống chỉ hỗ trợ chế độ thao tác trực tiếp đơn giản.
  • Lưới. Kiểm thử thao tác điều hướng xoay mẫu z bằng thao tác cuộn.
  • Thông báo. Thử nghiệm nhắc vào và ra khỏi thông báo quan trọng.
  • Cuộn. Thử nghiệm cuộn qua cả lựa chọn có thể làm tâm điểm và không thể làm tâm điểm nội dung.
  • WebView. Kiểm thử việc điều hướng qua các đường liên kết trong WebView.
  • Tuỳ chỉnh FocusArea. Kiểm thử tuỳ chỉnh FocusArea:
    • Tổng kết.
    • android:focusedByDefaultapp:defaultFocus
    • .
    • Mục tiêu nhắc nhở rõ ràng.
    • Dịch lối tắt.
    • FocusArea không có chế độ xem có thể làm tâm điểm.