大屏幕上的输入兼容性

在大屏设备上,用户通常会使用键盘、鼠标、触控板、触控笔或游戏手柄与应用互动。如需让应用能够接受来自外部设备的输入,请执行以下操作:

  • 测试基本键盘支持,例如 Ctrl+Z 表示撤消,Ctrl+C 表示复制,Ctrl+S 表示保存。如需查看默认键盘快捷键列表,请参阅处理键盘操作。
  • 测试高级键盘操作支持,例如通过 Tab 键和箭头键进行键盘导航、按 Enter 键确认文字输入以及在媒体应用中使用空格键执行播放和暂停操作。
  • 测试基本的鼠标交互,包括通过右键点击打开上下文菜单、悬停鼠标时图标的变化,以及自定义组件上的鼠标滚轮或触控板滚动事件等等。
  • 测试应用专用的输入设备,如触控笔、游戏控制器和音乐应用 MIDI 控制器。
  • 考虑支持高级输入方式,让您的应用在桌面环境中脱颖而出,例如,支持将触控板用作 DJ 应用的唱片平滑转换器、让游戏支持鼠标捕捉,以及支持为以键盘为主要输入方式的用户设置键盘快捷键。

键盘

应用对键盘输入的响应方式会影响大屏设备上的用户体验。有三种类型的键盘输入:导航、按键和快捷键。

键盘导航很少在以触控为中心的应用中实现,但用户在使用应用且双手放在键盘上时,会期望能够使用键盘导航。对于有无障碍需求的用户,键盘导航在手机、平板电脑、可折叠设备和桌面设备上至关重要。

对于许多应用,箭头键和 Tab 键导航由 Android 框架自动处理。例如,Button 默认可聚焦,并且键盘导航通常无需任何额外代码即可正常运行。如需为默认不可聚焦的视图启用键盘导航,请将其标记为可聚焦,这可以通过程序化方式或在 XML 中完成:

Kotlin

yourView.isFocusable = true

Java

yourView.setFocusable(true);

或者,您也可以在布局文件中设置 focusable 属性:

android:focusable="true"

如需了解详情,请参阅焦点处理。

启用焦点后,Android 框架会根据所有可聚焦视图的位置为其创建导航映射。这通常会按预期运行,无需进一步开发。如果默认映射不符合应用的需求,则可以按如下方式替换映射:

Kotlin

// Arrow keys
yourView.nextFocusLeftId = R.id.view_to_left
yourView.nextFocusRightId = R.id.view_to_right
yourView.nextFocusTopId = R.id.view_above
yourView.nextFocusBottomId = R.id.view_below
// Tab key
yourView.nextFocusForwardId = R.id.next_view

Java

// Arrow keys
yourView.setNextFocusLeftId(R.id.view_to_left);
yourView.setNextFocusRightId(R.id.view_to_left);
yourView.setNextFocusTopId(R.id.view_to_left);
yourView.setNextFocusBottomId(R.id.view_to_left);
// Tab key
yourView.setNextFocusForwardId(R.id.next_view);

仅使用键盘测试对应用中每个界面元素的访问。在没有鼠标输入或触控输入的情况下,应该也可以访问常用元素。

请注意,对于需要使用无障碍功能的用户来说,键盘支持可能是必不可少的。

按键

对于由屏幕虚拟键盘 (IME) 处理的文本输入,例如 EditText ,应用在大屏设备上应按预期运行,无需进行额外的开发工作。对于框架无法预期的按键,应用需要自行处理相应行为。对于具有自定义视图的应用来说尤其如此。

例如,聊天应用使用 Enter 键发送消息,媒体应用使用空格键开始和停止播放,游戏使用 w、a、s 和 d 键控制移动。

大多数应用会替换 onKeyUp() 回调,并为每个收到的键码添加预期行为:

Kotlin

kotlin override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean { return when (keyCode) { KeyEvent.KEYCODE_ENTER -> { sendChatMessage() true } KeyEvent.KEYCODE_SPACE -> { playOrPauseMedia() true } else -> super.onKeyUp(keyCode, event) } }

Java

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_ENTER) {
        sendMessage();
        return true;
    } else if (KeyEvent.KEYCODE_SPACE){
        playOrPauseMedia();
        return true;
    } else {
        return super.onKeyUp(keyCode, event);
    }
}

当按键被释放时,会发生 onKeyUp 事件。使用回调可防止应用在按键被按住或缓慢松开时需要处理多个 onKeyDown 事件。需要检测按键按下时刻或用户是否按住按键的游戏和应用可以监听 onKeyDown 事件,并自行处理重复的 onKeyDown 事件。

如需了解详情,请参阅处理键盘操作。

快捷键

使用硬件键盘时,用户希望能够使用包含 Ctrl、Alt、Shift 和 Meta 键的常用键盘快捷键。如果应用未实现快捷方式,用户可能会感到沮丧。高级用户也喜欢使用快捷方式来执行应用特有的常用任务。快捷方式可让应用更易于使用,并使其有别于没有快捷方式的应用。

一些常见的快捷键包括 Ctrl+S(保存)、Ctrl+Z(撤消)和 Ctrl+Shift+Z(重做)。如需查看默认快捷键列表,请参阅处理键盘操作。

您可以通过实现 dispatchKeyShortcutEvent() 来拦截给定键码的所有组合键(Alt、Ctrl、Shift 和 Meta),从而启用快捷键。 如需检查特定辅助键,请使用:

Kotlin

override fun dispatchKeyShortcutEvent(event: KeyEvent): Boolean {
  return when (event.keyCode) {
    KeyEvent.KEYCODE_O -> {
      openFile() // Ctrl+O, Shift+O, Alt+O
      true
    }
    KeyEvent.KEYCODE_Z-> {
      if (event.isCtrlPressed) {
        if (event.isShiftPressed) {
          redoLastAction() // Ctrl+Shift+Z pressed
          true
        } else {
          undoLastAction() // Ctrl+Z pressed
          true
        }
      }
    }
    else -> {
      return super.dispatchKeyShortcutEvent(event)
    }
  }
}

Java

@Override
public boolean dispatchKeyShortcutEvent(KeyEvent event) {
  if (event.getKeyCode() == KeyEvent.KEYCODE_O) {
      openFile(); // Ctrl+O, Shift+O, Alt+O
      return true;
  } else if(event.getKeyCode() == KeyEvent.KEYCODE_Z) {
      if (event.isCtrlPressed()) {
          if (event.isShiftPressed()) {
              redoLastAction();
              return true;
          }
          else {
              undoLastAction();
              return true;
          }
      }
  }
  return super.dispatchKeyShortcutEvent(event);
}

将快捷键代码与其他按键处理(例如 onKeyUp() 和 onKeyDown())分开,默认情况下会接受辅助键,而无需在每种情况下手动实现辅助键检查。允许所有辅助键组合还可以为习惯于不同键盘布局和操作系统的用户提供更多便利。

不过,您也可以通过检查 KeyEvent.isCtrlPressed()、KeyEvent.isShiftPressed() 或 KeyEvent.isAltPressed() 在 onKeyUp() 中实现快捷键。如果修改后的按键行为更多的是对应用行为的更改,而不是快捷键,则这种方式可能更易于维护。例如,在游戏中,W 表示“向前走”,Shift+W 表示“向前跑”。

Kotlin

override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
  return when(keyCode) {
    KeyEvent.KEYCODE_W-> {
      if (event.isShiftPressed) {
        if (event.isCtrlPressed) {
          flyForward() // Ctrl+Shift+W pressed
          true
        } else {
          runForward() // Shift+W pressed
          true
        }
      } else {
        walkForward() // W pressed
        true
      }
    }
    else -> super.onKeyUp(keyCode, event)
  }
}

Java

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
    if (keyCode == KeyEvent.KEYCODE_W) {
        if (event.isShiftPressed()) {
            if (event.isCtrlPressed()) {
                flyForward(); // Ctrl+Shift+W pressed
                return true;
            } else {
                runForward(); // Shift+W pressed
                return true;
            }
        } else {
            walkForward();
            return true;
        }
    }
    return super.onKeyUp(keyCode, event);
}

另请参阅键盘快捷键辅助工具。

触控笔

许多大屏设备都配有触控笔。Android 应用会将触控笔输入作为触摸屏输入进行处理。某些设备可能还配有 USB 或蓝牙绘图板,例如 Wacom Intuos。Android 应用可以接收蓝牙输入,但无法接收 USB 输入。

触控笔事件由 View#onTouchEvent() 或 View#onGenericMotionEvent() 报告为触摸屏事件,并包含类型为 SOURCE_STYLUS 的 MotionEvent#getSource()。

MotionEvent 对象包含有关事件的信息:

历史点

Android 会对输入事件进行批处理,并且每帧传送一次。触控笔报告事件的频率可能远高于显示屏。创建绘图应用时,请使用 getHistorical API 检查最近可能发生的事件:

防止手掌误触

使用触控笔绘图、手写或与您的应用互动时,用户有时会用手掌触摸屏幕。系统可能会在将其识别为手掌误触并予以忽略之前便将相应触摸事件(设置为 ACTION_DOWN 或 ACTION_POINTER_DOWN)报告给您的应用。

Android 通过分派 MotionEvent 来取消手掌触摸事件。如果您的应用收到 ACTION_CANCEL,则应取消相应手势。如果您的应用收到 ACTION_POINTER_UP,则应检查 FLAG_CANCELED 是否已设置。如果已设置,则应取消相应手势。

请勿仅检查 FLAG_CANCELED。在 Android 13(API 级别 33)及更高版本中,系统会为 ACTION_CANCEL 事件设置 FLAG_CANCELED,但在较低的 Android 版本中,系统不会设置该标志。

Android 12

在 Android 12(API 级别 32)及更低版本上,只能针对单指针触摸事件检测手掌误触。如果手掌触摸是唯一的指针,系统会通过在动作事件对象上设置 ACTION_CANCEL 来取消相应事件。如果还有其他指针,系统会设置 ACTION_POINTER_UP,这不足以检测手掌误触。

Android 13

在 Android 13(API 级别 33)及更高版本中,如果手掌触摸是唯一的指针,系统会对动作事件对象设置 ACTION_CANCEL 和 FLAG_CANCELED 以取消相应事件。如果还有其他指针,系统会设置 ACTION_POINTER_UP 和 FLAG_CANCELED。

每当您的应用收到带有 ACTION_POINTER_UP 的动作事件时,请检查 FLAG_CANCELED 以确定相应事件是否指示防止手掌误触(或其他事件取消)。

记事应用

ChromeOS 具有一个特殊的 intent,用于将已注册的记事应用呈现给用户。如需将应用注册为记事应用,请将以下内容添加到应用清单中:

<intent-filter>
    <action android:name="org.chromium.arc.intent.action.CREATE_NOTE" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

当应用向系统注册后,用户可以选择将其作为默认记事应用。当用户请求创建新记事时,应用应创建一个空白记事,以便用户使用触控笔输入内容。当用户希望为图片(例如屏幕截图或下载的图片)添加注释时,应用会启动并显示 ClipData,其中包含一个或多个具有 content:// URI 的内容。该应用应创建一条记事,这条记事将附加的第一张图片用作背景图片,该应用还应进入一种模式,用户可以在这种模式下使用触控笔在屏幕上进行绘制。

在没有触控笔的情况下测试记事 intent

如需测试在没有主动式触控笔的情况下应用是否可以正确响应记事 intent,请使用以下方法显示 ChromeOS 上的记事选项:

  1. 切换到开发模式并使设备可写入
  2. 按 Ctrl+Alt+F2 打开终端
  3. 运行命令 sudo vi /etc/chrome_dev.conf
  4. 按 i 进行修改,并在文件末尾的新行中添加 --ash-enable-palette
  5. 按 Esc 键进行保存,然后输入 :、w 和 q 并按 Enter 键
  6. 按 Ctrl + Alt + F1 键以返回 ChromeOS 常规界面
  7. 退出,然后重新登录

现在,任务栏上应该会显示触控笔菜单:

  • 点按任务栏中的触控笔按钮,然后选择新建记事。这样应该会打开一条空白绘图记事。
  • 截取屏幕截图。在任务栏中依次选择触控笔按钮 > 截取屏幕,或者下载图片。通知中应有为图片添加注释选项。这样应该会启动应用,并且图片已做好标注的准备。

鼠标和触控板支持

大多数应用通常只需处理三个以大屏设备为中心的事件:右键点击、悬停以及拖放。

右键点击

任何导致应用显示上下文菜单的操作(例如轻触并按住列表项)也应响应右键点击事件。

如需处理右键点击事件,应用应注册 View.OnContextClickListener:

Kotlin

yourView.setOnContextClickListener {
    showContextMenu()
    true
}

Java

yourView.setOnContextClickListener(v -> {
    showContextMenu();
    return true;
});

如需详细了解如何构建上下文菜单,请参阅创建上下文菜单。

悬停

您可以通过处理悬停事件,让应用布局更精致、更易于使用。对于自定义 视图:

Kotlin

// Change the icon to a "hand" pointer on hover.
// Highlight the view by changing the background.
yourView.setOnHoverListener { view, _ ->
    addVisualHighlighting(true)
    view.pointerIcon =
        PointerIcon.getSystemIcon(view.context, PointerIcon.TYPE_HAND)
    true // Listener consumes the event.
}

Java

// Change the icon to a "hand" pointer on hover.
// Highlight the view by changing the background.
yourView.setOnHoverListener((view, event) -> {
    addVisualHighlighting(true);
    view.setPointerIcon(
        PointerIcon.getSystemIcon(view.getContext(), PointerIcon.TYPE_HAND)
    );
    return true; // Listener consumes the event.
});

最常见的两个示例如下:

  • 通过更改鼠标指针图标,向用户指示元素是否具有互动行为,例如是否可点击或可编辑
  • 当指针悬停在大型列表或网格中的项目上时,向这些项目添加视觉反馈

拖放

在多窗口环境中,用户希望能够在应用之间拖放项目。这适用于桌面设备以及处于分屏模式下的平板电脑、手机和可折叠设备。

考虑用户是否有可能将项目拖入您的应用。例如,照片编辑器应该能接收照片、音频播放器应该能接收音频文件、绘图程序应该能接收照片。

如需添加拖放支持,请参阅 启用拖放 ,并参阅 Android on ChromeOS — Implementing Drag & Drop(ChromeOS 上的 Android - 实现拖放)博文。

有关 ChromeOS 的特殊注意事项

高级指针支持

对鼠标和触控板输入进行高级处理的应用应实现 View#onGenericMotionEvent() 并使用 [MotionEvent.getSource()][] 区分 SOURCE_MOUSE 和 SOURCE_TOUCHSCREEN。

检查 MotionEvent 对象以实现所需的行为:

  • 移动生成 ACTION_HOVER_MOVE 事件。
  • 按钮生成 ACTION_BUTTON_PRESS 和 ACTION_BUTTON_RELEASE 事件。您还可以使用 getButtonState() 检查所有鼠标和触控板按钮的当前状态。
  • 鼠标滚轮滚动生成 ACTION_SCROLL 事件。

游戏控制器

部分大屏 Android 设备最多支持四个游戏控制器。使用标准 Android 游戏控制器 API 处理游戏控制器(请参阅支持游戏控制器)。

游戏控制器按钮按照通用映射映射到通用值。 但并非所有游戏控制器制造商都遵循相同的映射惯例。 如果您允许用户选择不同的常见控制器映射,就能提供更好的体验。如需了解详情,请参阅处理游戏手柄按钮按下操作。

输入转换模式

默认情况下,ChromeOS 会启用输入转换模式。对于大多数 Android 应用来说,此模式有助于应用在桌面环境中按预期运行。例如,自动启用触控板上的双指滚动、鼠标滚轮滚动,以及将原始显示坐标映射到窗口坐标。通常,应用开发者无需自行实现这些行为。

如果某个应用实现了自定义输入行为(例如,定义一种自定义触控板双指张合操作),或者这些输入转换未提供该应用预期的输入事件,您可以停用输入翻译模式,方法是将以下代码添加到 Android 清单:

<uses-feature
    android:name="android.hardware.type.pc"
    android:required="false" />

其他资源