在 Android 中,从用户与应用的交互中截获事件的方法不止一种。如考虑截获界面内的事件,则可从用户与之交互的特定 View 对象中捕获事件。为此,View 类提供了多种方法。
在用于构建布局的各种 View 类中,您可能会注意到几种看起来适用于界面事件的公开回调方法。当该对象上发生相应的操作时,Android 框架会调用这些方法。例如,在轻触一个 View 对象(例如“按钮”)时,系统对该对象调用 onTouchEvent() 方法。不过,为截获此事件,您必须扩展 View 类并重写该方法。然而,为处理此类事件而扩展每个 View 对象并不现实。正因如此,View 类还包含一系列嵌套接口以及您可以更轻松定义的回调。这些接口称为事件侦听器,是您捕获用户与界面之间交互的票证。
尽管您通常会使用事件侦听器来侦听用户交互,但有时您确实需通过扩展 View 类来构建自定义组件。也许,您想通过扩展 Button 类来丰富某些内容的样式。在此情况下,您将能够使用该类的
事件处理程序为类定义默认事件行为。
事件侦听器
事件侦听器是 View 类中包含一个回调方法的接口。当用户与界面项目之间的交互触发已注册此视图的侦听器时,Android 框架将调用这些方法。
事件侦听器接口包含以下回调方法:
onClick()在
View.OnClickListener 中。当用户轻触项目(处于轻触模式下),或者使用导航键或轨迹球聚焦于项目,然后按适用的“Enter”键或按下轨迹球时,系统会调用此方法。onLongClick()在
View.OnLongClickListener 中。当用户轻触并按住项目(处于轻触模式下),或者使用导航键或轨迹球聚焦于项目,然后按住适用的“Enter”键或按住轨迹球(持续一秒钟)时,系统会调用此方法。onFocusChange()在
View.OnFocusChangeListener 中。当用户使用导航键或轨迹球导航到或远离项目时,系统会调用此方法。onKey()在
View.OnKeyListener 中。当用户聚焦于项目并按下或释放设备上的硬按键时,系统会调用此方法。onTouch()在
View.OnTouchListener 中。当用户执行可视为轻触事件的操作时,包括按下、释放或屏幕上的任何移动手势(在项目边界内),系统会调用此方法。onCreateContextMenu()在
View.OnCreateContextMenuListener 中。当(因持续“长按”而)生成上下文菜单时,系统会调用此方法。请参阅
菜单开发者指南中有关上下文菜单的介绍。
以下示例展示如何为按钮注册点击侦听器。
// Create an anonymous implementation of OnClickListener
private OnClickListener corkyListener = new OnClickListener() {
public void onClick(View v) {
// do something when the button is clicked
}
};
protected void onCreate(Bundle savedValues) {
...
// Capture our button from layout
Button button = (Button)findViewById(R.id.corky);
// Register the onClick listener with the implementation above
button.setOnClickListener(corkyListener);
...
}
您可能还会发现,将 OnClickListener 作为 Activity 的一部分来实现更为方便。这样可避免加载额外的类和分配对象。例如:
public class ExampleActivity extends Activity implements OnClickListener {
protected void onCreate(Bundle savedValues) {
...
Button button = (Button)findViewById(R.id.corky);
button.setOnClickListener(this);
}
// Implement the OnClickListener callback
public void onClick(View v) {
// do something when the button is clicked
}
...
}
此外,考虑应用的文本输入时,请记住:许多设备只有软件输入法。此类方法无需基于按键;某些可能使用语音输入、手写等。尽管输入法提供类似键盘的界面,但其通常不会触发
onKeyDown() 系列的事件。除非您想将应用限制为带有硬键盘的设备,否则,在设计界面时切勿要求必须通过特定按键进行控制。特别是,当用户按下返回键时,不要依赖这些方法验证输入;请改用
IME_ACTION_DONE 等操作让输入法知晓您的应用预计会作何反应,以便其通过一种有意义的方式更改其界面。不要推断软件输入法应如何工作,只要相信它能为应用提供已设置格式的文本即可。
事件处理程序
如果您从视图构建自定义组件,则可定义几种用作默认事件处理程序的回调方法。在有关自定义视图组件的文档中,您将了解某些用于事件处理的常见回调,包括:
还有一些其他方法值得您注意,尽管它们并非 View 类的一部分,但可能会直接影响所能采取的事件处理方式。因此,在管理布局内更复杂的事件时,请考虑使用以下其他方法:
轻触模式
当用户使用方向键或轨迹球导航界面时,必须聚焦到可操作项目上(如按钮),以便用户看到将接受输入的对象。但是,如果设备具有轻触功能且用户开始通过轻触界面与之交互,那么便不再需要突出显示项目或聚焦到特定视图对象上。因此,有一种交互模式称为“轻触模式”。
对于支持轻触功能的设备,当用户轻触屏幕时,设备会立即进入轻触模式。自此,只有 isFocusableInTouchMode() 为 true 的视图才可聚焦,如文本编辑微件。针对其他可轻触的视图(如按钮),您在轻触时不会获得焦点,按下时只会触发点击侦听器。
无论何时,只要用户点击方向键或滚动轨迹球,设备便会退出轻触模式,并找到一个视图使其获得焦点。现在,用户可在不轻触屏幕的情况下继续与界面交互。
整个系统(所有窗口和 Activity)都将保持轻触模式状态。如要查询当前状态,您可以通过调用 isInTouchMode(),检查设备目前是否处于轻触模式。
处理焦点
在运行 Android 9(API 级别 28)或更高版本的设备上,Activity 不会分配初始焦点。如有需要,您必须显式请求初始焦点。
焦点移动所使用的算法会查找指定方向上距离最近的元素。在极少数情况下,默认算法可能与开发者的期望行为不一致。在这些情况下,您可以在布局文件中显式重写以下 XML 属性:nextFocusDown、nextFocusLeft、nextFocusRight 和 nextFocusUp。将其中一个属性添加到失去焦点的视图。将属性的值定义为应聚焦视图的 ID。例如:
<LinearLayout
android:orientation="vertical"
... >
<Button android:id="@+id/top"
android:nextFocusUp="@+id/bottom"
... />
<Button android:id="@+id/bottom"
android:nextFocusDown="@+id/top"
... />
</LinearLayout>
一般来说,在此垂直布局中,无论是从第一个按钮向上导航还是从第二个按钮向下导航,焦点都不会移到任何其他位置。现在,顶部按钮已将底部按钮定义为 nextFocusUp(反之亦然),因而导航焦点将通过自上而下和自下而上循环往复。
若要将视图声明为在界面中可聚焦(通常并非如此),请在布局声明中将 android:focusable XML 属性添加到该视图。将值设置为 true。此外,您还可使用 android:focusableInTouchMode,将视图声明为在轻触模式下可聚焦。