Tài liệu sau đây dành cho nhà phát triển ứng dụng.
Để ứng dụng của bạn hỗ trợ tính năng xoay, bạn PHẢI:
- Đặt
FocusParkingView
vào bố cục hoạt động tương ứng. - Đảm bảo các thành phần hiển thị có thể (hoặc không thể) lấy tiêu điểm.
- Sử dụng
FocusArea
để bao bọc tất cả các thành phần hiển thị có thể lấy tiêu điểm, ngoại trừFocusParkingView
.
Bạn có thể tham khảo thông tin chi tiết về từng việc cần làm ở bên dưới, sau khi thiết lập môi trường để phát triển ứng dụng hỗ trợ màn hình xoay.
Thiết lập bộ điều khiển xoay
Trước khi có thể bắt đầu phát triển ứng dụng hỗ trợ thao tác xoay, bạn cần có bộ điều khiển xoay hoặc thiết bị thay thế. Bạn có các lựa chọn được 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:
- Nhấn vào biểu tượng ba dấu chấm ở cuối thanh công cụ:
Hình 1. Truy cập vào bộ điều khiển xoay được mô phỏng - Chọn Car rotary (Xe xoay) trong cửa sổ điều khiển mở rộng:
Hình 2. Chọn Xe xoay
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, việc này sẽ ngăn bàn phím ảo xuất hiện.
- Sử dụng bản dựng
userdebug
hoặceng
. - Bật tính năng lọc sự kiện nhấn phím:
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ể sử dụng các lệnh car_service
để chèn các sự kiện đầu vào xoay. Bạn có thể chạy các lệnh này trên các thiết bị chạy Android Automotive OS (AAOS) hoặc trên 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 tay điều khiển xoay của bạn đang hoạt động, đây là tuỳ chọn thực tế nhất. Điều này đặc biệt hữu ích khi kiểm thử tính năng xoay nhanh.
FocusParkingView
FocusParkingView
là một thành phần hiển thị trong suốt trong Thư viện giao diện người dùng ô tô (car-ui-library).
RotaryService
sử dụng nó để hỗ trợ điều hướng bằng bộ điều khiển xoay.
FocusParkingView
phải là thành phần hiển thị có thể lấy tiêu điểm đầu tiên trong bố cục. Bạn phải đặt lớp này bên ngoài tất cả FocusArea
. Mỗi cửa sổ phải có một FocusParkingView
. Nếu đang sử dụng bố cục cơ sở car-ui-library chứa FocusParkingView
, bạn không cần thêm FocusParkingView
khác. Dưới đây là 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>
Sau đây là lý do bạn cần có FocusParkingView
:
- Android không tự động xoá tiêu điểm khi tiêu điểm được đặt trong một cửa sổ khác. Nếu bạn cố gắng xoá tiêu điểm trong cửa sổ trước đó, Android sẽ lấy lại tiêu điểm cho một thành phần hiển thị trong cửa sổ đó, dẫn đến việc hai cửa sổ được lấy tiêu điểm cùng lúc. Bạn có thể khắc phục vấn đề này bằng cách thêm
FocusParkingView
vào mỗi cửa sổ. Chế độ xem này có tính chất trong suốt và điểm nổi bật tiêu điểm mặc định của chế độ xem này bị tắt, vì vậy, người dùng sẽ không nhìn thấy chế độ xem này bất kể có được lấy tiêu điểm hay không. Nó có thể lấy tiêu điểm đểRotaryService
có thể đặt tiêu điểm trên đó để xoá điểm nổi bật của tiêu điểm. - Nếu chỉ có một
FocusArea
trong cửa sổ hiện tại, thì việc xoay bộ điều khiển trongFocusArea
sẽ khiếnRotaryService
di chuyển tiêu điểm từ thành phần hiển thị bên phải sang thành phần hiển thị bên trái (và ngược lại). Bạn có thể khắc phục vấn đề này bằng cách thêm thành phần hiển thị này vào từng cửa sổ. KhiRotaryService
xác định mục tiêu tiêu điểm làFocusParkingView
, nó có thể xác định một vòng lặp sắp diễn ra tại thời điểm nào, tránh vòng lặp bằng cách không di chuyển tiêu điểm. - Khi nút điều khiển xoay khởi chạy một ứng dụng, Android sẽ đặt tiêu điểm vào thành phần hiển thị có thể lấy tiêu đ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 khái niệm hiện có về tiêu điểm thành phần hiển thị của khung Android, từ thời điện thoại có bàn phím thực và bàn phím D-pad.
Thuộc tính android:nextFocusForward
hiện có được sử dụng lại cho màn hình xoay (xem phần Tuỳ chỉnh FocusArea), nhưng android:nextFocusLeft
, android:nextFocusRight
, android:nextFocusUp
và android:nextFocusDown
thì không.
RotaryService
chỉ tập trung vào các thành phần hiển thị có thể lấy tiêu điểm. Một số thành phần hiển thị, chẳng hạn như Button
, thường có thể lấy tiêu điểm. Các loại khác, chẳng hạn như TextView
và ViewGroup
, thường không được hỗ trợ. Các thành phần hiển thị có thể nhấp sẽ tự động có thể lấy tiêu điểm và các thành phần hiển thị sẽ tự động có thể nhấp khi có trình nghe lượt nhấp. Nếu logic tự động này mang lại khả năng lấy tiêu điểm mong muốn, bạn không cần phải đặ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 mang lại khả năng lấy tiêu điểm 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ị bằng View.setFocusable(boolean)
theo phương thức lập trình. Để RotaryService
lấy tiêu điểm, thành phần hiển thị PHẢI đáp ứng các yêu cầu sau:
- Có thể làm tâm điểm
- Đã bật
- Đã hiển thị
- Có giá trị khác 0 cho chiều rộng và chiều cao
Nếu một thành phần hiển thị không đáp ứng tất cả các yêu cầu này, chẳng hạn như một nút có thể lấy tiêu điểm nhưng bị tắt, thì người dùng không thể sử dụng nút điều khiển xoay để lấy tiêu điểm trên nút đó. Nếu bạn muốn tập trung vào các thành phần hiển thị bị vô hiệu hoá, 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 thành phần hiển thị xuất hiện mà không cho biết Android nên coi thành phần hiển thị đó là bị vô hiệu hoá. Ứng dụng của bạn có thể thông báo cho người dùng lý do chế độ xem bị tắt khi nhấn vào. 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:
- Để thêm thuộc tính tuỳ chỉnh vào thành phần hiển thị. Ví dụ: để thêm trạng thái tuỳ chỉnh
state_rotary_enabled
vào lớp thành phần hiển thịCustomView
, hãy sử dụng:<declare-styleable name="CustomView"> <attr name="state_rotary_enabled" format="boolean" /> </declare-styleable>
- Để theo dõi trạng thái này, hãy thêm một biến thực thể vào thành phần hiển thị cùng với các phương thức truy cập:
private boolean mRotaryEnabled; public boolean getRotaryEnabled() { return mRotaryEnabled; } public void setRotaryEnabled(boolean rotaryEnabled) { mRotaryEnabled = rotaryEnabled; }
- Cách đọc giá trị của thuộc tính khi thành phần hiển thị được tạo:
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView); mRotaryEnabled = a.getBoolean(R.styleable.CustomView_state_rotary_enabled);
- 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; }
- Tùy vào trạng thái của thành phần hiển thị mà trình xử lý lượt nhấp của thành phần hiển thị đó sẽ hoạt động theo cách khác nhau. Ví dụ: trình xử lý lượt nhấp có thể không làm gì cả hoặc có thể bật lên một thông báo ngắn khi
mRotaryEnabled
làfalse
. - Để nút xuất hiện ở trạng thái tắt, trong nền có thể vẽ của thành phần hiển thị, hãy sử dụng
app:state_rotary_enabled
thay vìandroid:state_enabled
. Nếu chưa có, bạn cần thêm:xmlns:app="http://schemas.android.com/apk/res-auto"
- Nếu thành phần hiển thị của bạn bị tắt trong bất kỳ bố cục nào, hãy thay thế
android:enabled="false"
bằngapp:state_rotary_enabled="false"
, sau đó thêm không gian tênapp
như trên. - 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 đến
setEnabled()
bằng các lệnh gọi đếnsetRotaryEnabled()
.
FocusArea
Sử dụng FocusAreas
để phân vùng các thành phần hiển thị có thể lấy tiêu điểm thành các khối để giúp việc đ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ủa bạn có thanh công cụ, thì thanh công cụ phải nằm trong một FocusArea
riêng biệt với phần còn lại của ứng dụng. Thanh thẻ và các thành phần điều hướng khác cũng phải được tách biệt với phần còn lại của ứng dụng. Các danh sách lớn thường phải có FocusArea
riêng. Nếu không, người dùng phải xoay qua toàn bộ danh sách để truy cập vào một số chế độ xem.
FocusArea
là lớp con của LinearLayout
trong thư viện car-ui-library.
Khi tính năng này được bật, FocusArea
sẽ vẽ một điểm nổi bật khi một trong các thành phần con của nó được lấy tiêu điểm. Để tìm hiểu thêm, hãy xem phần Tuỳ chỉnh điểm nhấn tiêu điểm.
Khi tạo một 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 cho khối đó, hãy sử dụng FocusArea
.
Nếu không, hãy gói khối trong FocusArea
.
KHÔNG lồng FocusArea
trong một FocusArea
khác.
Việc này dẫn đến hành vi điều hướng không xác định. Đảm bảo rằng tất cả thành phần hiển thị có thể lấy tiêu điểm đều được lồng trong FocusArea
.
Dưới đây là ví dụ về FocusArea
trong RotaryPlayground
:
<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:
- Khi xử lý các thao tác xoay và đẩy,
RotaryService
sẽ tìm các thực thể củaFocusArea
trong hệ phân cấp chế độ xem. - Khi nhận được sự kiện xoay,
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 trong cùng mộtFocusArea
. - Khi nhận được sự kiện đẩy,
RotaryService
sẽ di chuyển tiêu điểm sang một thành phần hiển thị khác có thể lấy tiêu điểm trong mộtFocusArea
khác (thường là liền kề).
Nếu bạn không đưa FocusAreas
nào vào bố cục, thì thành phần hiển thị gốc sẽ được coi là vùng tiêu điểm ngầm ẩn. Người dùng không thể nhấn để điều hướng trong ứng dụng. Thay vào đó, họ sẽ xoay qua tất cả các chế độ xem có thể lấy tiêu điểm, điều này có thể phù hợp với các hộp thoại.
Tuỳ chỉnh FocusArea
Bạn có thể sử dụng hai thuộc tính Chế độ xem chuẩn để tuỳ chỉnh tính năng điều hướng xoay:
android:nextFocusForward
cho phép nhà phát triển ứng dụng chỉ định thứ tự xoay trong một khu vực tiêu điểm. Đây cũng là thuộc tính dùng để kiểm soát thứ tự Tab cho thao tác đ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ụngapp:wrapAround
(xem bên dưới) để tạo một 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ộtFocusArea
.
FocusArea
cũng xác định một số thuộc tính để tuỳ chỉnh thao tác điều hướng xoay.
Bạn không thể tuỳ chỉnh các vùng tiêu điểm ngầm ẩn bằng các thuộc tính này.
- (Android 11 QPR3, Android 11 Car, Android 12)
app:defaultFocus
có thể được dùng để chỉ định mã nhận dạng của thành phần hiển thị con cháu có thể lấy tiêu điểm. Thành phần hiển thị này sẽ được lấy tiêu điểm khi người dùng nhấn vàoFocusArea
này. - (Android 11 QPR3, Android 11 Car, Android 12)
app:defaultFocusOverridesHistory
có thể được đặt thànhtrue
để đưa thành phần hiển thị được chỉ định ở trên vào tiêu điểm ngay cả khi có nhật ký để cho biết một thành phần hiển thị khác trongFocusArea
này đã được lấy tiêu điểm. - (Android 12)
Hãy sử dụngapp:nudgeLeftShortcut
,app:nudgeRightShortcut
,app:nudgeUpShortcut
vàapp:nudgeDownShortcut
để chỉ định mã nhận dạng của thành phần hiển thị con cháu có thể lấy tiêu điểm. Thành phần này sẽ được lấy tiêu điểm khi người dùng đẩy theo một hướng nhất định. Để tìm hiểu thêm, hãy xem nội dung về lối tắt nhấn nhẹ ở bên dưới.(Android 11 QPR3, Android 11 Car, không còn được dùng trong Android 12)
app:nudgeShortcut
vàapp:nudgeShortcutDirection
chỉ hỗ trợ một lối tắt nhấn nhẹ. - (Android 11 QPR3, Android 11 Car, Android 12)
Để bật tính năng xoay vòng trongFocusArea
này, bạn có thể đặtapp:wrapAround
thànhtrue
. Phương thức này thường được dùng khi các thành phần hiển thị được sắp xếp theo hình tròn hoặc hình bầu dục. - (Android 11 QPR3, Android 11 Car, Android 12)
Để điều chỉnh khoảng đệm của phần đánh dấu trongFocusArea
này, hãy sử dụngapp:highlightPaddingStart
,app:highlightPaddingEnd
,app:highlightPaddingTop
,app:highlightPaddingBottom
,app:highlightPaddingHorizontal
vàapp:highlightPaddingVertical
. - (Android 11 QPR3, Android 11 Car, Android 12)
Để điều chỉnh các giới hạn được nhận biết củaFocusArea
này nhằm tìm mục tiêu nhấn nhẹ, hãy sử dụngapp:startBoundOffset
,app:endBoundOffset
,app:topBoundOffset
,app:bottomBoundOffset
,app:horizontalBoundOffset
vàapp:verticalBoundOffset
. - (Android 11 QPR3, Android 11 Car, Android 12)
Để chỉ định rõ mã nhận dạng của mộtFocusArea
(hoặc các khu vực) liền kề theo hướng đã cho, hãy sử dụngapp:nudgeLeft
,app:nudgeRight
,app:nudgeUp
vàapp:nudgeDown
. Sử dụng phương thức này khi phương thức tìm kiếm hình học được sử dụng theo mặc định không tìm thấy mục tiêu mong muốn.
Thao tác đẩy thường di chuyển giữa các FocusArea. Tuy nhiên, với lối tắt cho thao tác đẩy, đôi khi thao tác đẩy trước tiên sẽ điều hướng trong FocusArea
để người dùng có thể cần đẩy hai lần để điều hướng đến FocusArea
tiếp theo. Lối tắt Nudge (Đẩy) rất hữu ích khi FocusArea
chứa một danh sách dài, theo sau là Nút hành động nổi, như trong ví dụ bên dưới:

Nếu không có lối tắt nhấn nhẹ, người dùng sẽ phải xoay qua toàn bộ danh sách để đến 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 về tiêu điểm thành phần hiển thị. Khi người dùng xoay và đẩy, RotaryService
sẽ di chuyển tiêu điểm xung quanh, tập trung vào một thành phần hiển thị và bỏ tập trung vào một thành phần hiển thị khác. Trong Android, khi một thành phần hiển thị được lấy tiêu điểm, nếu thành phần hiển thị đó:
- Đã chỉ định điểm nổi bật tiêu điểm riêng, Android sẽ vẽ điểm nổi bật tiêu điểm của thành phần hiển thị.
- Không chỉ định điểm nhấn tiêu điểm và điểm nhấn tiêu điểm mặc định không bị tắt, Android sẽ vẽ điểm nhấn tiêu điểm mặc định cho thành phần hiển thị.
Các ứng dụng được thiết kế để cảm ứng thường không chỉ định các điểm nổi bật phù hợp cho tiêu điểm.
Vùng làm nổi bật tiêu điểm mặc định do khung Android cung cấp và có thể bị nhà sản xuất thiết bị gốc ghi đè. Nhà phát triển ứng dụng sẽ nhận được thông báo này khi giao diện họ đang sử dụng được lấy từ Theme.DeviceDefault
.
Để mang lại 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 mặc định bất cứ khi nào có thể.
Nếu bạn cần một điểm nổi bật có hình dạng tuỳ chỉnh (ví dụ: hình tròn hoặc hình viên nang) hoặc nếu bạn đang sử dụng giao diện không bắt nguồn từ Theme.DeviceDefault
, hãy sử dụng tài nguyên thư viện giao diện người dùng ô tô để chỉ định điểm nổi bật tiêu điểm của riêng bạn cho mỗi thành phần hiển thị.
Để chỉ định một điểm nổi bật tiêu điểm tuỳ chỉnh cho một thành phần hiển thị, hãy thay đổi nền hoặc đối tượng có thể vẽ ở nền trước của thành phần hiển thị thành một đối tượng có thể vẽ khác khi thành phần hiển thị được lấy làm tiêu điểm. Thông thường, bạn sẽ thay đổi nền. Đối tượng có thể vẽ sau đây, nếu được dùng làm nền cho thành phần hiển thị hình vuông, sẽ tạo ra một điểm nổi bật tập trung hình 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, Android 11 Car, Android 12) Các 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 để nhất quán với điểm nhấn tiêu điểm mặc định mà họ chỉ định. Điều này đảm bảo rằng màu đánh dấu tiêu điểm, chiều rộng nét vẽ, v.v. không thay đổi khi người dùng di chuyển giữa một thành phần hiển thị có màu đánh dấu tiêu điểm tuỳ chỉnh và một thành phần hiển thị có màu đánh dấu tiêu điểm mặc định. Mục cuối cùng là hiệu ứng gợn sóng dùng cho thao tác chạm. Giá trị mặc định dùng cho tài nguyên in đậm sẽ xuất hiện như sau:

Ngoài ra, một điểm nhấn tiêu điểm tuỳ chỉnh sẽ được gọi khi một nút được đặt màu nền đồng nhất để thu hút sự chú ý của người dùng, như trong ví dụ bên dưới. Điều này có thể khiến bạn khó nhìn thấy điểm nhấn tiêu điểm. Trong trường hợp này, hãy chỉ định một điểm nhấn tiêu điểm tuỳ chỉnh bằng màu phụ:
![]() |
- (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
Ví dụ:
![]() |
![]() |
|
Đã lấy tiêu điểm, chưa nhấn | Đã lấy tiêu điểm, đã nhấn |
Cuộn xoay
Nếu ứng dụng của bạn sử dụng RecyclerView
, bạn NÊN sử dụng CarUiRecyclerView
. Điều này đảm bảo giao diện người dùng của bạn nhất quán với các giao diện người dùng khác vì hoạt động tuỳ chỉnh của nhà sản xuất thiết bị gốc (OEM) áp dụng cho tất cả CarUiRecyclerView
.
Nếu tất cả các phần tử trong danh sách đều có thể lấy tiêu điểm, thì bạn không cần làm gì thêm. Thao tác điều hướng xoay sẽ di chuyển tiêu điểm qua các phần tử trong danh sách và danh sách sẽ cuộn để hiển thị phần tử mới được lấy tiêu điểm.
(Android 11 QPR3, Android 11 Car, Android 12)
Nếu có sự kết hợp giữa các phần tử có thể lấy tiêu điểm và không lấy tiêu điểm, hoặc nếu tất cả các phần tử đều không lấy tiêu điểm, thì bạn có thể bật tính năng cuộn xoay. Tính năng này 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 các mục không lấy tiêu điểm. Để bật tính năng cuộn xoay, hãy đặt thuộc tính app:rotaryScrollEnabled
thành true
.
(Android 11 QPR3, Android 11 Car, Android 12)
Bạn có thể bật tính năng cuộn xoay trong bất kỳ thành phần hiển thị cuộn nào, bao gồm cả avCarUiRecyclerView
, bằng phương thức setRotaryScrollEnabled()
trong CarUiUtils
. Nếu làm như vậy, bạn cần:
- Đặt chế độ xem cuộn thành có thể lấy tiêu điểm để có thể lấy tiêu điểm khi không có thành phần hiển thị con nào có thể lấy tiêu điểm xuất hiện,
- Tắt tính năng làm nổi bật tiêu điểm mặc định trên thành phần hiển thị có thể cuộn bằng cách gọi
setDefaultFocusHighlightEnabled(false)
để thành phần hiển thị có thể cuộn không xuất hiện là được lấy tiêu điểm, - Đảm bảo rằng chế độ xem cuộn được lấy tiêu điểm trước các thành phần con của chế độ xem đó bằng cách gọi
setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS)
. - Theo dõi MotionEvents bằng
SOURCE_ROTARY_ENCODER
vàAXIS_VSCROLL
hoặcAXIS_HSCROLL
để cho biết khoảng cách cần cuộn và hướng (thông qua dấ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ấy tiêu điểm, thanh cuộn sẽ thay đổi từ màu xám sang màu xanh dương, như thể cho biết thanh cuộn đang được lấy tiêu điểm. Bạn có thể triển khai hiệu ứng tương tự nếu muốn.
MotionEvents giống như các sự kiện do con lăn trên chuột tạo ra, ngoại trừ nguồn.
Chế độ thao tác trực tiếp
Thông thường, thao tác đẩy và xoay sẽ di chuyển qua giao diện người dùng, trong khi thao tác nhấn nút Trung tâm sẽ thực hiện 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 muốn điều chỉnh âm lượng chuông báo, người dùng có thể sử dụng bộ điều khiển xoay để chuyển đến thanh trượt âm lượng, nhấn nút Trung tâm, 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 thao tác điều hướng. Đây được gọi là chế độ thao tác trực tiếp (DM). Ở chế độ 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ý việc xoay và thành phần hiển thị mà bạn muốn thao tác phản hồi ACTION_SCROLL_FORWARD
và ACTION_SCROLL_BACKWARD
AccessibilityEvent
một cách thích hợp, hãy sử dụng cơ chế đơ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ửa sổ hệ thống; các ứng dụng có thể sử dụng một trong hai cơ chế.
Cơ chế đơn giản
(Android 11 QPR3, Android 11 Car, Android 12)
Ứng dụng của bạn phải gọi DirectManipulationHelper.setSupportsRotateDirectly(View view, boolean enable)
.
RotaryService
nhận biết thời điểm người dùng ở chế độ DM và chuyển sang chế độ DM khi người dùng nhấn nút Trung tâm trong khi một thành phần hiển thị được lấy làm tiêu điểm. Khi ở chế độ DM, các thao tác xoay sẽ thực hiện ACTION_SCROLL_FORWARD
hoặc ACTION_SCROLL_BACKWARD
và thoát khỏi chế độ DM khi người dùng nhấn nút Quay lại. Cơ chế đơn giản này sẽ bật/tắt trạng thái đã chọn của khung hiển thị khi vào và thoát khỏi chế độ DM.
Để cung cấp tín hiệu hình ảnh cho biết người dùng đang ở chế độ DM, hãy làm cho chế độ xem của bạn trông khác khi được chọn. Ví dụ: thay đổi nền khi android:state_selected
là true
.
Cơ chế nâng cao
Ứng dụng xác định thời điểm RotaryService
chuyển sang và thoát khỏi chế độ DM. Để mang lại trải nghiệm nhất quán cho người dùng, khi nhấn nút Trung tâm với chế độ xem DM được lấy làm tâm điểm, bạn sẽ chuyển sang chế độ DM và nút Quay lại sẽ thoát khỏi chế độ DM. Nếu không sử dụng nút Trung tâm và/hoặc thao tác đẩy, bạn có thể sử dụng các cách khác để thoát khỏi chế độ tin nhắn trực tiếp. Đối với các ứng dụng như Maps, bạn có thể sử dụng nút đại diện cho DM để chuyển sang chế độ DM.
Để hỗ trợ chế độ DM nâng cao, thành phần hiển thị:
- (Android 11 QPR3, Android 11 Car, Android 12) PHẢI nghe sự kiện
KEYCODE_DPAD_CENTER
để chuyển sang chế độ DM và nghe sự kiệnKEYCODE_BACK
để thoát khỏi chế độ DM, gọiDirectManipulationHelper.enableDirectManipulationMode()
trong mỗi trường hợp. Để theo dõi các sự kiện này, hãy làm theo một trong những cách sau:- Đăng ký
OnKeyListener
.
hoặc,
- Mở rộng thành phần hiển thị rồi ghi đè phương thức
dispatchKeyEvent()
của thành phần đó.
- Đăng ký
- NÊN theo dõi các sự kiện nhắc nhở (
KEYCODE_DPAD_UP
,KEYCODE_DPAD_DOWN
,KEYCODE_DPAD_LEFT
hoặcKEYCODE_DPAD_RIGHT
) nếu thành phần hiển thị phải xử lý các thao tác nhắc. - NÊN nghe
MotionEvent
và nhận số lượt xoay trongAXIS_SCROLL
nếu thành phần hiển thị muốn xử lý việc xoay. Có một số cách để thực hiện việc này:- Đăng ký
OnGenericMotionListener
. - Mở rộng thành phần hiển thị và ghi đè phương thức
dispatchTouchEvent()
của thành phần hiển thị đó.
- Đăng ký
- Để tránh bị kẹt trong chế độ DM, BẮT BUỘC phải thoát khỏi chế độ DM khi Mảnh hoặc Hoạt động mà thành phần hiển thị thuộc về không có tính tương tác.
- NÊN cung cấp tín hiệu hình ảnh để cho biết chế độ xem đang ở chế độ DM.
Dưới đây là ví dụ về một thành phần hiển thị tuỳ chỉnh sử dụng chế độ DM để kéo 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
Khi sử dụng ActivityView:
ActivityView
không được có tiêu điểm.- (Android 11 QPR3, Android 11 Car, không dùng nữa trong Android 11)
Nội dung củaActivityView
PHẢI chứaFocusParkingView
làm thành phần hiển thị có thể lấy tiêu điểm đầu tiên và thuộc tínhapp:shouldRestoreFocus
của thành phần hiển thị đó PHẢI làfalse
. - Nội dung của
ActivityView
không được có thành phần hiển thịandroid:focusByDefault
.
Đối với người dùng, ActivityViews không được ảnh hưởng đến hoạt động điều hướng, ngoại trừ việc các khu vực tiêu điểm không được trải dài trên ActivityViews. Nói cách khác, bạn không thể có một vùng tiêu điểm duy nhất có nội dung bên trong và bên ngoài ActivityView
. Nếu bạn không thêm FocusArea nào vào ActivityView
, thì phần gốc của hệ phân cấp khung hiển thị trong ActivityView
được coi là vùng tiêu điểm ngầm ẩn.
Nút hoạt động khi được giữ
Hầu hết các nút đều thực hiện một số thao tác khi được nhấp vào. Một số nút hoạt động khi bạn giữ.
Ví dụ: các nút Tua nhanh và Tua lại thường hoạt động khi được giữ chặt. Để các nút như vậy hỗ trợ xoay, hãy theo dõi 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 chạy sau một khoảng thời gian 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 bằng cách 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ấy nét sẽ được làm nổi bật. Khi chạm vào màn hình, không có điểm nổi bật tiêu điểm nào xuất hiện. Người dùng có thể chuyển đổi giữa các chế độ nhập này bất cứ lúc nào:
- Xoay → chạm. Khi người dùng chạm vào màn hình, điểm nhấn tiêu điểm sẽ biến mất.
- Cảm ứng → xoay. Khi người dùng đẩy, xoay hoặc nhấn nút Trung tâm, điểm nhấn tiêu điểm sẽ xuất hiện.
Các nút Quay lại và Trang chủ không ảnh hưởng đến chế độ nhập.
Chế độ xoay dựa trên khái niệm hiện có của Android 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 các thay đổi. Mặc dù bạn có thể sử dụng tính năng này để tuỳ chỉnh giao diện người dùng cho chế độ nhập hiện tại, nhưng 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ế để chạm, thường thì các thành phần hiển thị có thể lấy tiêu điểm sẽ được lồng.
Ví dụ: có thể có một FrameLayout
xung quanh ImageButton
, cả hai đều có thể lấy tiêu điểm. Điều này không gây hại cho thao tác chạm nhưng có thể khiến người dùng có trải nghiệm không tốt khi xoay vì người dùng phải xoay tay điều khiển hai lần để chuyển sang chế độ xem tương tác tiếp theo. Để mang lại trải nghiệm tốt cho người dùng, Google khuyên bạn nên đặt tiêu điểm vào chế độ xem bên ngoài hoặc chế độ xem bên trong, nhưng không nên đặt tiêu điểm vào cả hai.
Nếu một nút hoặc nút chuyển mất tiêu điểm khi nhấn thông qua bộ điều khiển xoay, thì một trong các điều kiện sau đây có thể áp dụng:
- Nút hoặc công tắc đang bị tắt (trong thời gian ngắn hoặc vô thời hạn) do nút đang được nhấn. Trong cả hai trường hợp, bạn có thể giải quyết vấn đề này theo hai cách:
- Giữ nguyên trạng thái
android:enabled
làtrue
và sử dụng trạng thái tuỳ chỉnh để chuyển nút hoặc nút chuyển sang màu xám như mô tả trong phần Trạng thái tuỳ chỉnh. - Sử dụng vùng chứa để bao quanh nút hoặc nút chuyển và đặt tiêu điểm vào vùng chứa thay vì nút hoặc nút chuyển. (Trình nghe lượt nhấp phải nằm trên vùng chứa.)
- Giữ nguyên trạng thái
- Nút hoặc công tắc đang được thay thế. Ví dụ: thao tác được thực hiện khi nhấn nút hoặc bật/tắt nút chuyển có thể kích hoạt việc làm mới các thao tác có sẵn, 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 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 nút chuyển hiện có.
- Như trên, hãy thêm một vùng chứa có thể lấy tiêu điểm xung quanh nút hoặc nút chuyển.
RotaryPlayground
RotaryPlayground
là một ứng dụng tham chiếu cho phương thức nhập dữ liệu xoay. Hãy sử dụng lớp này để tìm hiểu cách tích hợp các tính năng xoay vào ứng dụng của bạn. RotaryPlayground
có trong các bản dựng trình mô phỏng và trong các bản dựng dành cho 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 thị các thẻ sau ở bên trái:
- Thẻ. Kiểm thử thao tác di chuyển xung quanh các khu vực tiêu điểm, bỏ qua các phần tử không thể lấy tiêu điểm và nhập văn bản.
- Thao tác trực tiếp. Kiểm thử các tiện ích hỗ trợ chế độ thao tác trực tiếp đơn giản và nâng cao. 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 trên giao diện người dùng Sys. 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 theo mẫu z bằng tính năng cuộn.
- Thông báo. Kiểm thử thao tác đẩy vào và đẩy ra thông báo quan trọng.
- Cuộn. Kiểm thử thao tác cuộn qua nội dung có thể lấy tiêu điểm và không thể lấy tiêu điểm.
- WebView. Kiểm thử thao tác điều hướng qua các đường liên kết trong
WebView
. FocusArea
tuỳ chỉnh. Kiểm thử tuỳ chỉnhFocusArea
:- Bao quanh.
android:focusedByDefault
vàapp:defaultFocus
.
- Mục tiêu nhắc nhở rõ ràng.
- Lối tắt cho thao tác đẩy.
FocusArea
không có thành phần hiển thị có thể lấy tiêu điểm.