使用 Compose 实现单手手势


从 Wear OS 7(API 级别 37)开始,单手势框架以及作为适用于 Wear OS 的 Compose 的一部分的 API 可让用户无需触摸即可与您的应用互动。

虽然该框架最初在 Pixel Watch 设备(Pixel Watch 3 及更高版本)上受支持,但现在所有原始设备制造商 (OEM) 都可以使用它。通过采用此 API,随着硬件支持的扩展,您的应用的手势支持会自动在整个生态系统中进行扩缩。

为了帮助用户发现可用的手势,同时又不会使界面杂乱无章,Wear OS 框架提供了动画手势指示器。这些视觉提示会突出显示可以执行手势的位置,而系统会根据用户偏好自动管理其显示节奏和静音频率。

支持的手势和操作

Wear OS 手势框架支持两种手势类型:

  • 主要操作(双指张合两次): 映射到屏幕上的主要操作,例如接听电话或切换媒体播放。
  • 关闭操作(转动手腕): 映射到后退导航、关闭对话框或取消提示。

在 Compose 中配置手势

虽然单手势 API 可以增强您的界面,但请务必注意,某些硬件和 OEM 不支持这些手势。如果 API 检测到您的应用在其中一个不受支持的设备上运行,该库会自动执行空操作,而不会影响标准触摸互动。

与标准 Compose 行为一样,您可以使用 修饰符在界面 元素上启用单手势。您可以根据要执行的操作(主要操作或关闭操作)以及与系统级用户偏好设置(例如提示显示节奏和静音频率)协调的 gestureId 来配置应用的手势。您可以通过创建 OneHandedGestureConfiguration 对象来表达此配置;我们建议您使用 rememberOneHandedGestureConfiguration 函数来创建该对象。您还可以在 OneHandedGestureConfiguration 中提供手势优先级。

rememberOneHandedGestureConfiguration 函数会在重新组合时跟踪用户互动历史记录,而不会公开应用状态。应用创建配置后,应将该配置传递给互动可组合项上的 Modifier.oneHandedGesture

为了帮助用户发现可用的手势,该库提供了 OneHandedGestureClickIndicator 方法。此方法充当一个封装容器,用于替换其底层内容,以向用户表明有手势操作可用。

互动组件

如需在按钮等互动控件上启用手势,请创建一个指定 OneHandedGestureAction.Primary 的配置,并应用 oneHandedGesture 修饰符。将相同的 MutableInteractionSource 传递给控件和修饰符,以便手势事件向控件发出视觉按压反馈。

如需启用手势指示器,请创建并记住 OneHandedGestureClickIndicatorState 的实例。然后,如需触发视觉反馈,请在 oneHandedGesture 修饰符提供的 onGestureAvailable 回调中调用 showIndicator,这会向系统发出指示事件已发生的信号。调用后,该组件会将其正常内容暂时替换为手势动画。

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

可滚动容器

对于可滚动的屏幕或列表,请创建一个指定 OneHandedGestureAction.Primary 的配置,并将 oneHandedGesture 修饰符应用于容器,并调用滚动帮助程序(例如 scrollDown)。

如需为滚动操作提供视觉反馈,您可以使用 OneHandedGestureScrollIndicator。此组件充当显示滚动位置的标准滚动指示器,但它还可以向用户表明有滚动条手势可用。此指示器通常会传递给 scrollIndicator 插槽,并与可滚动容器(例如 TransformingLazyColumn)的状态相关联。ScreenScaffold它还会观察 OneHandedGestureScrollIndicatorState 以管理其视觉转换。

如需触发视觉反馈,请对此状态调用 showIndicator,通常是在 oneHandedGesture 修饰符的 onGestureAvailable 回调中。 触发后,指示器会暂时将其标准视觉状态替换为手势动画序列,以提醒用户。

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

组合多个手势

您可以通过将 gesturePriority 添加到 OneHandedGestureConfiguration 对象,为滚动条手势和点击手势配置相同的主要操作:

通过在内部按钮上显式设置 priority = OneHandedGesturePriority.Clickable,并在其父列表上设置 priority = OneHandedGesturePriority.Scrollable,系统可以显示此手势优先级行为。 当用户通过单手势触发主要操作时,系统会先向下滚动列表,直到按钮可见。然后,它会捕获按钮的点击操作。

使用 ADB 测试和调试手势

您可以使用 Android 调试桥 (adb) 和 IWearGestureService 系统服务在实体设备或模拟器上测试单手势而无需执行物理手腕移动。

启用手势模拟

  1. 验证您的 Wear OS 设备是否搭载 Wear OS 7(API 级别 37)及更高版本。
  2. 如果在实体设备上进行测试,而该设备不在手腕上或充电器上,请替换离身传感器状态,以使设备保持活动状态:
adb shell cmd sensorservice set-off-body-state 0

使用 ADB 触发手势事件

如需模拟双指捏合 手势(这是 Pixel 手表上的 Primary 操作),请运行以下 ADB shell 命令:

adb shell cmd IWearGestureService gesture 1

如需模拟转动手腕 手势(这是 Pixel 手表上的 Dismiss 操作),请运行以下 ADB shell 命令:

adb shell cmd IWearGestureService gesture 2

重置手势提示跟踪

系统会跟踪用户互动历史记录,并根据全局节奏设置(例如始终每日 )显示浮动手势提示。调试应用的手势指示器时,请重置此跟踪历史记录,以便提示再次显示在您的软件包中:

adb shell cmd IWearGestureService hint clear <your_package_name>

如需在测试完成后重置离身传感器状态,请执行以下操作:

adb shell cmd sensorservice reset-off-body-state

其他资源

如需了解有关何时何地使用单手势的设计指南,请参阅 单手势