在 Wear OS 7(API 级别 37)及更高版本中,单手势框架以及属于 Compose for Wear OS 的 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。此组件用作显示滚动位置的标准滚动指示器,但它也可以向用户指示有可用的滚动手势。此指示器通常会传递到 ScreenScaffold 的 scrollIndicator slot,并与可滚动容器(例如 TransformingLazyColumn)的状态相关联。它还观察 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)) } } }
组合多个手势
您可以通过向 OneHandedGestureConfiguration 对象添加 gesturePriority,将滚动手势和点击手势配置为具有相同的主要操作:
OneHandedGesturePriority.Clickable(最高):分配给交互式控件(例如类型为Button或Card的控件),以便它们在屏幕上可见时捕获手势。OneHandedGesturePriority.Scrollable(中):分配给可滚动或可分页的容器,以便它们让位于可点击的子级,但在没有可点击的控件可见时进行滚动。OneHandedGesturePriority.Unspecified(最低):未分配优先级。这是未设置priority的手势的默认值。
通过在内部按钮上显式设置 priority = OneHandedGesturePriority.Clickable,并在其父列表上设置 priority = OneHandedGesturePriority.Scrollable,系统可以显示这种手势优先级行为。当用户通过单手势触发主要操作时,系统会先向下滚动列表,直到按钮可见,然后捕获按钮的点击操作。
使用 ADB 测试和调试手势
您可以使用 Android 调试桥 (adb) 和 IWearGestureService 系统服务在实体设备或模拟器上测试单手势,而无需进行实际的手腕运动。
启用手势模拟
在使用 ADB 模拟手势之前,请配置设备设置和限制替换:
验证您的 Wear OS 设备是否搭载 Wear OS 7(API 级别 37)及更高版本:
adb shell getprop ro.build.version.sdk如果您在未佩戴在手腕上或放在充电器上的实体设备上进行测试,请替换掉离身约束,以便手势框架保持活跃状态:
adb shell cmd IWearGestureService override-constraints offbody-state
使用 ADB 触发手势事件
如需模拟双指张合两次手势(即 Pixel 手表上的 Primary 操作),请运行以下 ADB shell 命令:
adb shell cmd IWearGestureService gesture DoublePinch
如需模拟转动手腕手势(即 Pixel 手表上的 Dismiss 操作),请运行以下 ADB shell 命令:
adb shell cmd IWearGestureService gesture WristTurn
重置手势提示跟踪
系统会跟踪用户互动历史记录,并根据全局频次设置(例如始终或每天)显示浮动的手势提示。调试应用的姿势指示器时,请重置此跟踪历史记录,以便再次显示软件包的提示:
在
userdebugbuild 或模拟器上:adb shell cmd IWearGestureService hint clear <your_package_name>在零售 (
user) build 中:在没有 root 访问权限的商用设备上,
hint clear会被系统权限阻止。清除应用的本地数据以重置提示发现:adb shell pm clear <your_package_name>
恢复默认限制条件
完成测试后,如需重置所有调试限制替换项,请执行以下操作:
adb shell cmd IWearGestureService override-constraints reset
排查手势注入问题
如果您的应用未收到模拟手势,请执行以下操作:
验证手表屏幕是否已唤醒并开启。当屏幕关闭或处于微光模式时,手势框架不会将手势分派给应用。如需使用 ADB 唤醒显示屏,请运行以下命令:
adb shell input keyevent KEYCODE_WAKEUP检查您的应用是否已注册为活跃的手势订阅者,并且目前是否持有窗口焦点:
adb shell cmd IWearGestureService get-active-gestures -readable当应用的手势界面位于前台且屏幕处于唤醒状态时,此命令会返回
[DoublePinch]或[DoublePinch, WristTurn]。如果返回空列表 ([]),请检查窗口是否具有焦点,或者离身约束是否阻止了激活。检查内部手势服务状态和有效订阅者令牌:
adb shell dumpsys IWearGestureService
其他资源
如需获取有关何时何地使用单手手势的设计指南,请参阅单手手势。
为你推荐
- 注意:当 JavaScript 处于关闭状态时,系统会显示链接文字
- 单手手势设计指南