Cử chỉ bằng một tay với Compose


Bắt đầu từ Wear OS 7 (cấp độ API 37), một khung cử chỉ bằng một tay cùng với một API thuộc Compose cho Wear OS, cho phép người dùng tương tác với ứng dụng của bạn mà không cần chạm.

Mặc dù ban đầu được hỗ trợ trên các thiết bị Pixel Watch (Pixel Watch 3 trở lên), nhưng khung này vẫn có sẵn cho tất cả các OEM. Bằng cách áp dụng API này, tính năng hỗ trợ cử chỉ của ứng dụng sẽ tự động mở rộng quy mô trên toàn hệ sinh thái khi khả năng hỗ trợ phần cứng mở rộng.

Để giúp người dùng khám phá các cử chỉ hiện có mà không làm rối giao diện người dùng, khung Wear OS cung cấp các chỉ báo cử chỉ dạng động. Những gợi ý trực quan này làm nổi bật vị trí có thể thực hiện cử chỉ, trong khi hệ thống tự động quản lý nhịp độ hiển thị và tần suất tắt tiếng theo lựa chọn ưu tiên của người dùng.

Các cử chỉ và hành động được hỗ trợ

Khung cử chỉ Wear OS hỗ trợ 2 loại cử chỉ:

  • Thao tác chính (Chụm hai ngón): Tương ứng với thao tác chính trên màn hình, chẳng hạn như trả lời cuộc gọi hoặc bật/tắt chế độ phát nội dung nghe nhìn.
  • Thao tác loại bỏ (Xoay cổ tay): Chuyển đến chế độ điều hướng ngược, loại bỏ hộp thoại hoặc huỷ lời nhắc.

Định cấu hình cử chỉ trong Compose

Mặc dù API cử chỉ một tay có thể cải thiện giao diện người dùng, nhưng bạn cần lưu ý rằng một số phần cứng và OEM không hỗ trợ các cử chỉ này. Nếu phát hiện thấy ứng dụng của bạn đang chạy trên một trong những thiết bị không được hỗ trợ này, API sẽ tự động không hoạt động mà không ảnh hưởng đến các hoạt động tương tác cảm ứng tiêu chuẩn.

Giống như các hành vi Compose tiêu chuẩn, bạn có thể bật cử chỉ một tay trên các phần tử giao diện người dùng bằng cách sử dụng các đối tượng sửa đổi. Bạn định cấu hình cử chỉ của ứng dụng theo thao tác cần thực hiện (chính hoặc loại bỏ) và gestureId để phối hợp với các lựa chọn ưu tiên của người dùng ở cấp hệ thống, chẳng hạn như nhịp độ hiển thị gợi ý và tần suất tắt tiếng. Bạn thể hiện cấu hình này bằng cách tạo một đối tượng OneHandedGestureConfiguration; bạn nên dùng hàm rememberOneHandedGestureConfiguration để tạo đối tượng này. OneHandedGestureConfiguration cũng là nơi bạn có thể cung cấp mức độ ưu tiên của cử chỉ.

Hàm rememberOneHandedGestureConfiguration theo dõi nhật ký lượt tương tác của người dùng trên các lần kết hợp lại mà không làm lộ trạng thái ứng dụng. Sau khi tạo cấu hình, ứng dụng của bạn sẽ truyền cấu hình đó đến Modifier.oneHandedGesture trên thành phần kết hợp có thể tương tác.

Để giúp người dùng khám phá các cử chỉ có sẵn, thư viện này cung cấp phương thức OneHandedGestureClickIndicator. Phương thức này hoạt động như một trình bao bọc thay thế nội dung cơ bản của nó để cho người dùng biết rằng có một thao tác bằng cử chỉ.

Thành phần tương tác

Để bật cử chỉ trên một thành phần điều khiển tương tác như nút, hãy tạo một cấu hình chỉ định OneHandedGestureAction.Primary và áp dụng đối tượng sửa đổi oneHandedGesture. Truyền cùng một MutableInteractionSource cho cả thành phần điều khiển và đối tượng sửa đổi để các sự kiện cử chỉ phát ra phản hồi trực quan khi nhấn vào thành phần điều khiển.

Để bật chỉ báo cử chỉ, hãy tạo và ghi nhớ một thực thể của OneHandedGestureClickIndicatorState. Sau đó, để kích hoạt phản hồi trực quan, hãy gọi showIndicator trong lệnh gọi lại onGestureAvailable do công cụ sửa đổi oneHandedGesture cung cấp. Lệnh gọi lại này báo hiệu cho hệ thống rằng đã xảy ra một sự kiện chỉ báo. Sau khi được gọi, thành phần này sẽ nhanh chóng thay thế nội dung thông thường bằng một ảnh động cử chỉ.

var isPlaying by remember { mutableStateOf(false) }
val onClick = { isPlaying = !isPlaying }

val gestureConfig = rememberOneHandedGestureConfiguration(
    action = OneHandedGestureAction.Primary
)
val indicatorState = remember { OneHandedGestureClickIndicatorState() }
val coroutineScope = rememberCoroutineScope()
val interactionSource = remember { MutableInteractionSource() }

Button(
    onClick = onClick,
    interactionSource = interactionSource,
    modifier = Modifier
        .fillMaxWidth()
        .oneHandedGesture(
            gestureConfiguration = gestureConfig,
            interactionSource = interactionSource,
            onGestureLabel = if (isPlaying) "pause" else "play",
            onGestureAvailable = { coroutineScope.launch { indicatorState.showIndicator() } },
            onGesture = onClick
        )
) {
    OneHandedGestureClickIndicator(
        gestureConfiguration = gestureConfig,
        state = indicatorState
    ) {
        Text(if (isPlaying) "Pause" else "Play", modifier = Modifier.fillMaxWidth())
    }
}

Vùng chứa có thể cuộn

Đối với các màn hình hoặc danh sách có thể cuộn, hãy tạo một cấu hình chỉ định OneHandedGestureAction.Primary và áp dụng đối tượng sửa đổi oneHandedGesture cho vùng chứa của bạn, gọi một trình trợ giúp cuộn như scrollDown.

Để cung cấp phản hồi trực quan cho các thao tác di chuyển, bạn có thể sử dụng OneHandedGestureScrollIndicator. Thành phần này hoạt động như một chỉ báo cuộn tiêu chuẩn cho biết vị trí cuộn, nhưng cũng có thể cho biết rằng người dùng có thể thực hiện cử chỉ cuộn. Chỉ báo này thường được truyền đến khe scrollIndicator của ScreenScaffold và được ghép với trạng thái của một vùng chứa có thể cuộn, chẳng hạn như TransformingLazyColumn. Nó cũng quan sát một OneHandedGestureScrollIndicatorState để quản lý các hiệu ứng chuyển đổi trực quan.

Để kích hoạt phản hồi trực quan, hãy gọi showIndicator trên trạng thái này – thường là bên trong lệnh gọi lại onGestureAvailable của đối tượng sửa đổi oneHandedGesture. Sau khi được kích hoạt, chỉ báo sẽ tạm thời thay thế trạng thái hình ảnh tiêu chuẩn bằng một chuỗi hình động cử chỉ để cảnh báo người dùng.

val scrollState = rememberTransformingLazyColumnState()
val gestureConfig = rememberOneHandedGestureConfiguration(
    action = OneHandedGestureAction.Primary,
    priority = OneHandedGesturePriority.Scrollable
)
val indicatorState = remember(gestureConfig) { OneHandedGestureScrollIndicatorState() }
val coroutineScope = rememberCoroutineScope()

ScreenScaffold(
    scrollState = scrollState,
    scrollIndicator = {
        OneHandedGestureScrollIndicator(
            gestureConfiguration = gestureConfig,
            indicatorState = indicatorState,
            scrollState = scrollState,
            modifier = Modifier.align(Alignment.CenterEnd)
        )
    }
) { contentPadding ->
    TransformingLazyColumn(
        state = scrollState,
        contentPadding = contentPadding,
        modifier = Modifier
            .fillMaxSize()
            .oneHandedGesture(
                gestureConfiguration = gestureConfig,
                onGestureLabel = "scroll",
                onGestureAvailable = {
                    coroutineScope.launch { indicatorState.showIndicator() }
                },
                onGesture = { OneHandedGestureDefaults.scrollDown(scrollState) }
            )
    ) {
        items(10) { index ->
            Text("Item $index", modifier = Modifier.padding(8.dp))
        }
    }
}

Kết hợp nhiều cử chỉ

Bạn có thể định cấu hình cả cử chỉ cuộn và cử chỉ nhấp bằng cùng một thao tác chính bằng cách thêm gesturePriority vào đối tượng OneHandedGestureConfiguration:

  • OneHandedGesturePriority.Clickable (cao nhất): Chỉ định cho các chế độ điều khiển tương tác (chẳng hạn như những chế độ có loại Button hoặc Card) để chúng ghi lại cử chỉ khi hiển thị trên màn hình.
  • OneHandedGesturePriority.Scrollable (trung bình): Chỉ định cho các vùng chứa có thể cuộn hoặc phân trang để chúng nhường chỗ cho các phần tử con có thể nhấp nhưng cuộn khi không có chế độ điều khiển có thể nhấp nào xuất hiện.
  • OneHandedGesturePriority.Unspecified (thấp nhất): Mức độ ưu tiên chưa được chỉ định. Đây là giá trị mặc định cho một cử chỉ không có priority được đặt.

Bằng cách đặt priority = OneHandedGesturePriority.Clickable một cách rõ ràng trên nút bên trong và priority = OneHandedGesturePriority.Scrollable trên danh sách mẹ, hệ thống có thể cho thấy hành vi ưu tiên cử chỉ này. Khi người dùng kích hoạt thao tác chính bằng cử chỉ một tay, thao tác này sẽ cuộn danh sách xuống cho đến khi nút xuất hiện. Sau đó, nó sẽ ghi lại thao tác nhấp vào nút.

Kiểm thử và gỡ lỗi cử chỉ bằng ADB

Bạn có thể kiểm thử cử chỉ bằng một tay trên thiết bị thực hoặc trình mô phỏng mà không cần thực hiện các chuyển động cổ tay thực tế bằng cách sử dụng Cầu gỡ lỗi Android (adb) và dịch vụ hệ thống IWearGestureService.

Bật tính năng mô phỏng cử chỉ

  1. Xác minh rằng thiết bị Wear OS của bạn đang chạy Wear OS 7 (cấp độ API 37) trở lên.
  2. Nếu bạn đang kiểm thử trên một thiết bị thực không đeo trên cổ tay hoặc không cắm sạc, hãy ghi đè trạng thái cảm biến ngoài cơ thể để thiết bị vẫn hoạt động:
adb shell cmd sensorservice set-off-body-state 0

Kích hoạt các sự kiện cử chỉ bằng ADB

Để mô phỏng cử chỉ Chụm ngón tay hai lần (là thao tác Primary trên đồng hồ Pixel), hãy chạy lệnh sau trong ADB shell:

adb shell cmd IWearGestureService gesture 1

Để mô phỏng cử chỉ Xoay cổ tay (là thao tác Dismiss trên đồng hồ Pixel), hãy chạy lệnh shell ADB sau:

adb shell cmd IWearGestureService gesture 2

Đặt lại tính năng theo dõi gợi ý cử chỉ

Hệ thống theo dõi nhật ký tương tác của người dùng và hiển thị các gợi ý về cử chỉ nổi dựa trên chế độ cài đặt nhịp độ chung (chẳng hạn như Luôn luôn hoặc Hằng ngày). Khi gỡ lỗi chỉ báo cử chỉ của ứng dụng, hãy đặt lại nhật ký theo dõi này để các gợi ý xuất hiện lại cho gói của bạn:

adb shell cmd IWearGestureService hint clear <your_package_name>

Cách đặt lại trạng thái cảm biến khi không tiếp xúc với cơ thể sau khi hoàn tất kiểm thử:

adb shell cmd sensorservice reset-off-body-state

Tài nguyên khác

Để biết hướng dẫn thiết kế về thời điểm và vị trí sử dụng cử chỉ một tay, hãy xem phần Cử chỉ một tay.