在大屏设备上,用户通常使用键盘、鼠标、触控板、触控笔或游戏手柄与应用互动。如需让应用能够接受来自外部设备的输入,请执行以下操作:
- 测试基本键盘支持,例如使用 Ctrl+Z 执行撤消操作、使用 Ctrl+C 执行复制操作,以及使用 Ctrl+S 执行保存操作。如需查看默认键盘快捷键的列表,请参阅处理键盘操作。
- 测试高级键盘支持,例如通过 Tab 键和箭头键进行键盘导航、按 Enter 键确认文字输入,以及在媒体应用中使用 Spacebar 键执行播放和暂停操作。
- 测试基本的鼠标交互,包括通过右键点击打开上下文菜单、悬停鼠标时图标的变化,以及自定义组件上的鼠标滚轮或触控板滚动事件。
- 测试应用专用的输入设备,如触控笔、游戏控制器和音乐应用 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 键发送消息,媒体应用使用 Spacebar 键开始和停止播放,游戏使用 w、a、s 和 d 键控制移动,等等。
大多数应用都会替换 onKeyUp()
回调,并针对收到的每个键码添加预期的行为:
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),从而启用快捷键。如需检查是否有特定的修饰符键,请使用以下命令:
KeyEvent.isCtrlPressed()
;KeyEvent.isShiftPressed()
;KeyEvent.isAltPressed()
;KeyEvent.isMetaPressed()
,或者
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()
)分开处理可使代码维护工作更容易,还能默认接受修饰键,而不必在每种情况下都手动实现修饰键检查。有些用户习惯使用不同的键盘布局和操作系统,允许使用所有修饰符键组合对他们来说也会更加方便。
不过,您也可以在 onKeyUp()
中实现快捷键,方法是检查 KeyEvent.isCtrlPressed()
、KeyEvent.isShiftPressed()
或 KeyEvent.isAltPressed()
。如果修改后的按键行为更多的是对应用行为而非快捷方式的更改,就会使代码更容易维护。例如,在游戏中,当 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
对象包含与事件相关的信息:
MotionEvent#getToolType()
将返回TOOL_TYPE_FINGER
、TOOL_TYPE_STYLUS
或TOOL_TYPE_ERASER
,具体取决于与显示屏接触的工具MotionEvent#getPressure()
将报告施加到触控笔的物理压力(如果支持)MotionEvent#getAxisValue()
与MotionEvent.AXIS_TILT
和MotionEvent.AXIS_ORIENTATION
一起使用,可提供触控笔的物理倾斜度和方向(如果支持)
历史点
Android 会对输入事件进行批处理,并且每帧传送一次。触控笔可以按比显示屏高得多的频率来报告事件。创建绘图应用时,请使用 getHistorical
API 检查最近可能发生的事件:
MotionEvent#getHistoricalX()
MotionEvent#getHistoricalY()
MotionEvent#getHistoricalPressure()
MotionEvent#getHistoricalAxisValue()
防止手掌误触
使用触控笔绘图、手写或与您的应用互动时,用户有时会用手掌触摸屏幕。系统可能会在将其识别为手掌误触并予以忽略之前便将相应触摸事件(设置为 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
[TBD remove section.]
如需测试在没有主动式触控笔的情况下应用是否可以正确响应记事 intent,请使用以下方法显示 ChromeOS 上的记事选项:
- 切换到开发模式并使设备可写入
- 按 Ctrl+Alt+F2 打开终端
- 运行命令
sudo vi /etc/chrome_dev.conf
- 按
i
进行修改,并将--ash-enable-palette
添加到文件末尾且要另起一行 - 按 Esc 键进行保存,然后输入 :、w、q 并按 Enter 键
- 按 Ctrl+Alt+F1 键以返回 ChromeOS 常规界面
- 退出账号,然后重新登录
任务栏中现在应该有一个触控笔菜单:
- 点按任务栏中的触控笔按钮,然后选择新建记事。这样应该会打开一条空白绘图记事。
- 截取屏幕截图。在任务栏中依次选择触控笔按钮 > 截取屏幕,或者下载图片。通知中应该会有标注图片的选项。这样应该会启动应用,并且图片已做好标注的准备。
鼠标和触控板支持
大多数应用通常只需要处理三种以大屏幕为中心的事件:右键点击、悬停和拖放。
右键点击
会使应用显示上下文菜单的所有操作(如轻触并按住列表项)也应该对右键点击事件作出反应。
为了处理右键点击事件,应用应注册 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 - 实现拖放博文。
有关 ChromeOS 的特殊注意事项
- 请务必使用
requestDragAndDropPermissions()
请求权限,然后才能访问从应用外部拖入的项目 项目必须带有
View.DRAG_FLAG_GLOBAL
标记才能被拖出到其他应用
高级指针支持
对鼠标和触控板输入进行高级处理的应用应实现
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" />