在 Wear 上使用手腕手势

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

例如,用户可以用一只手滚动浏览通知,同时用另一只手拿着一杯水。展示手腕手势用途的示例还包括:

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

要查看手表上的手腕手势,请通过依次选择设置 > 手势 > 手腕手势:开启,确认已开启手势。然后,学完手表上的手势教程(依次选择设置 > 手势 > 启动教程)。

Wear OS 帮助中的以下手势对应用不可用:

  • 晃动手腕

可通过以下方式使用手腕手势:

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

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

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

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

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

即使您使用 WearableRecyclerView,也可能希望使用 KeyEvent 类中的常量。可通过创建 WearableRecyclerView 的子类并重新实现 onKeyDown() 回调来替换预定义的操作。可使用 setEnableGestureNavigation(false) 完全停用该行为。另请参阅处理键盘操作

直接使用按键事件

您可以使用 WearableRecyclerView 之外的按键事件触发新操作以响应手势事件。重要的是,这些手势事件具有以下特点:

  • 当设备处于活动模式时可识别
  • 传递方式与所有按键事件相同

具体来说,这些事件将传递到顶部 Activity 具有键盘焦点的视图。就像其他任何按键事件一样,与实现 KeyEvent.Callback 的用户互动(如视图或 Activity)相关的类可监听与手腕手势相关的按键事件。Android 框架使用按键事件调用具有焦点的视图或 Activity;对于手势,在做出手势时调用 onKeyDown() 方法回调。

例如,应用可能会替换视图或 Activity(两者都实现 KeyEvent.Callback)中的预定义操作,如下所示:

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 deemed
                    // 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 deemed 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 页面,了解如何将按键事件传递到您的视图和 Activity。
  • 保持一致的方向触发要求:
    • 使用“向外翻动手腕”手势可转到下一项,使用“向内翻动手腕”手势可转到上一项
  • 为手势提供对等的轻触功能。
  • 提供视觉反馈。
  • 不要使用键码实现与系统其他部分的常理背道而驰的功能。例如,不要使用 KEYCODE_NAVIGATE_NEXT 取消操作,也不要使用翻动姿势导航左-右轴。
  • 不要拦截不属于界面的元素(例如,在屏幕之外或部分被遮盖的视图)上的按键事件。这与其他任何按键事件都一样。
  • 不要将重复的翻动手势重新解释为您自己的新手势。它可能与系统的“晃动手腕”手势冲突。
  • 要使视图接收手势按键事件,它必须具有焦点;请参阅 View::setFocusable()。由于手势被视为按键事件,因此它们会触发一个转换过程,让系统退出“轻触模式”,这可能会引发意外行为。又因为用户可能会交替使用轻触和手势,所以可能需要使用 View::setFocusableInTouchmode() 方法。在某些情况下,可能还需要使用 setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS),这样一来,当从其他模式改为“轻触模式”或从“轻触模式”改为其他模式后焦点发生变化时,您的目标视图会获得焦点。
  • 小心地使用 requestFocus()clearFocus()
    • 调用 requestFocus() 时,请确保相应视图确实应该具有焦点。如果该视图在屏幕之外或被另一个视图遮盖,那么当手势触发回调时,可能会出现意外情况。
    • clearFocus() 将启动焦点搜索,以查找一个合适的视图。根据视图层次结构,此搜索可能需要比较复杂的计算。它最后还可能会将焦点分配给您不希望其接收焦点的视图。
  • 首先将按键事件传递到视图层次结构中具有焦点的视图。如果聚焦的视图不处理该事件(即,返回 false),那么即使父视图可以接收焦点并具有 KeyListener,系统也不会将该事件传递到父视图,而是将该事件传递到容纳具有焦点的视图层次结构的当前 Activity。因此,可能需要在更高的级别捕捉所有事件,然后将相关代码向下传递。或者,您也可以创建 Activity 的子类并替换 dispatchKeyEvent(KeyEvent event) 方法,以确保在必要时拦截相应按键,或对其进行处理(如果在较低层未处理)。