在 Wear 上使用手腕手势

在不方便轻触屏幕时,可通过手腕手势快速地与应用进行单手互动。

例如,用户可以用一只手拿着一杯水,用另一只手滚动浏览通知。手腕手势的其他用例包括:

  • 在一款慢跑应用中,浏览垂直屏幕,查看所跑的步数、经过的时间以及当前的步速
  • 在旅行应用中,滚动浏览航班和登机口信息
  • 在新闻应用中,滚动浏览报道

如需查看手表上的手腕手势,请通过依次选择设置 > 高级功能 > 手势 > 手腕手势开启,确认已开启手势。然后选择启动教程,学完手表上的手势教程。

注意:晃动手腕是系统级的返回或撤消手势,不适用于应用自定义的操作。

如本指南所述,您可以通过以下方式使用手腕手势:

每个手腕手势都将映射到 KeyEvent 类中的 int 常量,如下表所示:

手势 KeyEvent 说明
向外翻动手腕 KEYCODE_NAVIGATE_NEXT 此键码用于转到下一项。
向内翻动手腕 KEYCODE_NAVIGATE_PREVIOUS 此键码用于转到上一项。

使用曲线布局支持手腕手势

WearableRecyclerView 类为列表提供一个曲线布局,并自动支持手腕手势。该类针对视图具有焦点时做出的手腕手势预定义了操作。如需了解如何使用 WearableRecyclerView 类,请参阅在 Wear OS 上创建列表。另请参阅本指南的最佳做法部分。

注意:在穿戴式设备支持库中,WearableRecyclerView 类取代了一个与其类似且已废弃的类。

即使使用 WearableRecyclerView,您可能也要使用 KeyEvent 类中的常量。可通过创建 WearableRecyclerView 的子类和重新实现 onKeyDown() 回调来替换预定义的操作。此行为可通过使用 setEnableGestureNavigation(false) 完全停用。如需了解详情,请参阅处理键盘操作

直接使用按键事件

您可以使用 WearableRecyclerView 之外的按键事件触发新操作,以响应手势事件。重要的是,当设备处于活动模式时,系统会识别这些手势事件,并且这些事件的传递方式与所有按键事件相同。

就像其他任何按键事件一样,与实现 KeyEvent.Callback 的用户互动(如 ViewActivity)相关的类可监听与手腕手势相关的按键事件。Android 框架使用按键事件调用具有焦点的 ViewActivity。对于手势,在做出手势时调用 onKeyDown() 方法回调。

例如,应用可以替换实现 KeyEvent.CallbackViewActivity 中的预定义操作,如下所示:

Kotlin

class GesturesActivity : Activity() {

    /* KeyEvent.Callback */
    override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
        return when (keyCode) {
            KeyEvent.KEYCODE_NAVIGATE_NEXT ->
                // Do something that advances a user View to the next item in an ordered list.
                moveToNextItem()
            KeyEvent.KEYCODE_NAVIGATE_PREVIOUS ->
                // Do something that advances a user View to the previous item in an ordered list.
                moveToPreviousItem()
            else -> {
                // If you did not handle it, let it be handled by the next possible element as determined
                // by the Activity.
                super.onKeyDown(keyCode, event)
            }
        }
    }

    /** Shows the next item in the custom list.  */
    private fun moveToNextItem(): Boolean {
        ...
        // Return true if handled successfully, otherwise return false.
        return false
    }

    /** Shows the previous item in the custom list.  */
    private fun moveToPreviousItem(): Boolean {
        ...
        // Return true if handled successfully, otherwise return false.
        return false
    }
}

Java

public final class GesturesActivity extends Activity {

 @Override /* KeyEvent.Callback */
 public boolean onKeyDown(int keyCode, KeyEvent event) {
  switch (keyCode) {
   case KeyEvent.KEYCODE_NAVIGATE_NEXT:
    // Do something that advances a user View to the next item in an ordered list.
    return moveToNextItem();
   case KeyEvent.KEYCODE_NAVIGATE_PREVIOUS:
    // Do something that advances a user View to the previous item in an ordered list.
    return moveToPreviousItem();
  }
  // If you did not handle it, let it be handled by the next possible element as determined by the Activity.
  return super.onKeyDown(keyCode, event);
 }

 /** Shows the next item in the custom list. */
 private boolean moveToNextItem() {
  boolean handled = false;
  ...
  // Return true if handled successfully, otherwise return false.
  return handled;
 }

 /** Shows the previous item in the custom list. */
 private boolean moveToPreviousItem() {
  boolean handled = false;
  ...
  // Return true if handled successfully, otherwise return false.
  return handled;
 }
}

最佳实践

  • 查看 KeyEvent KeyEvent.Callback 页面,了解如何将按键事件传递到您的 ViewActivity
  • 保持一致的方向功能可见性:使用“向外翻动手腕”手势可转到下一项,使用“向内翻动手腕”手势可转到上一项
  • 为手势提供对等的轻触功能。
  • 提供视觉反馈。
  • 不要使用键码实现与系统其他部分的常理背道而驰的功能。例如,不要使用 KEYCODE_NAVIGATE_NEXT 取消操作,也不要使用翻动姿势导航左-右轴。
  • 不要拦截不属于界面的元素(例如,在屏幕之外或部分被遮盖的视图)上的按键事件。这与其他任何按键事件都一样。
  • 不要将重复的翻动手势重新解释为您自己的新手势。它可能与系统的“晃动手腕”手势冲突。
  • 如需让视图接收手势键事件,该视图必须具有焦点;请参阅 View.setFocusable()

    由于手势被视为按键事件,因此它们会触发一个转换过程,让系统退出“轻触模式”,这可能会引发意外行为。由于用户可能会交替使用轻触和手势,所以可能需要使用 View::setFocusableInTouchmode() 方法。在某些情况下,可能还需要使用 setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS),这样一来,当从其他模式改为“轻触模式”或从“轻触模式”改为其他模式后焦点发生变化时,您的目标视图会获得焦点。

  • 谨慎使用 requestFocus()clearFocus()
    • 调用 requestFocus() 时,请确保视图适合具有焦点。如果该视图在屏幕之外或被另一个视图遮盖,那么当手势触发回调时,可能会出现意外情况。
    • clearFocus() 方法将启动焦点搜索,以查找另一个合适的视图。根据视图层次结构,此搜索可能需要比较复杂的计算。它最后还可能会将焦点分配给您不希望其接收焦点的视图。
  • 首先将按键事件传递到视图层次结构中具有焦点的视图。如果聚焦的视图不处理该事件(即,返回 false),那么即使父视图可以接收焦点并具有 KeyListener,系统也不会将该事件传递到父视图,而是将该事件传递到容纳具有焦点的视图层次结构的当前 activity。

    因此,可能需要在更高的级别捕捉所有事件,然后将相关代码向下传递。或者,您也可以创建 activity 的子类并替换 dispatchKeyEvent(KeyEvent event) 方法,以在必要时拦截相应按键,或对其进行处理(如果未在较低层对其进行处理)。