原文自:http://android.eoe.cn/topic/ui

在Android中有多种方法可以用来拦截用户与程序的交互事件。如果想处理用户界面中触发的事件,可以通过从用户交互的View捕获事件来实现。View这个类提供了这些方法。

在用来构成布局的各种View类中,我们可以看到有几个用于UI事件的公共回调方法。当这些对象中有用户行为产生时,Android框架就会调用相应的回调方法。例如,当一个view(比如一个按钮Button)被触摸了,那么它的onTouchEvent()方法就会被调用。但是,为了拦截到这个事件,你必须扩展这个类并重写这些方法。然而,为了处理这样一个事件就扩展每一个View对象是不实际的。这就是为什么View类还提供了包含一些很方便定义的回调方法的嵌套接口的原因。这些叫做事件监听器(event listener)接口就是你拦截UI交互事件的入口。

虽然多数情况下使用事件监听器来监听用户交互,但是有时候为了创建一个自定义的组件,也需要扩展一个View类。比如你可能想要扩展一个Button类来使它更好用。这种情况下,你就可以使用事件处理器(event handler)来定义默认的事件行为。

事件监听器

一个事件监听器是View类中一个包含单一回调方法的接口。当注册了监听器的View发生了跟监听器对应的UI交互事件时,Android框架就会调用这些回调方法。

事件监听器接口中包含了以下回调方法:

onClick()

:来自View.OnClickListener接口。当用户触摸了一个对象(在触摸模式下),或者通过导航键或轨迹球使它获得焦点后再按下兼容"enter"的按键或是按下轨迹球,这个方法被调用。

onLongClick()

:来自View.OnLongClickListener接口。当用户在一个对象上触摸并按住不放(触摸模式下),或者通过导航键或轨迹球使它获得焦点后再按下兼容"enter"的按键或是轨迹球并按住不放(一秒钟时间),这个方法被调用。

onFocusChange()

:来自View.OnFocusChangeListener。当用户使用导航键或者轨迹球导航到一个对象或离开一个对象时,这个方法被调用。

onKey()

:来自View.OnKeyListener接口。当用户让焦点落在一个对象上后按下或释放设备上的一个键时,这个方法被调用。

onTouch()

:来自View.OnTouchListener接口,当用户执行一个触屏事件的动作时,包括按下动作、释放动作,或者任何在屏幕上的手势(当然必须在对象所在的区域内),这个方法被调用。

onCreateContextMenu()

:来自View.OnCreateContextMenuListener接口。当一个上下文菜单被创建(长按后的结果)时,这个方法被调用。关于上下文菜单的更多讨论,请参考关于Menus | 菜单 - Menus的开发指南。

这些方法是各自接口的唯一成员。如果要定义这些方法来处理事件,就要在Activity中实现这些嵌套接口,或者定义一个匿名内部类。然后,把你实现的实例传递给对应的View.set…Listener()方法。(例如,以实现的OnClickListener作为参数调用setOnClickListener()方法)。

下面的例子演示了如何给一个Button注册一个on-click监听器:

// 创建一个实现了OnClickListener的匿名内部类的对象
private OnClickListener mCorkyListener = new OnClickListener() {
public void onClick(View v) {
// do something when the button is clicked
}
};

protected void onCreate(Bundle savedValues) {
...
// 从布局中找到需要的按钮
Button button = (Button)findViewById(R.id.corky);
// 用上面实现的对象注册点击监听器
button.setOnClickListener(mCorkyListener);
...
}

你可能也发现把OnClickListener作为Activity的一部分来实现会更方便。这样可以避免额外的类加载和对象内存分配。如:

public class ExampleActivity extends Activity implements OnClickListener {
protected void onCreate(Bundle savedValues) {
...
Button button = (Button)findViewById(R.id.corky);
button.setOnClickListener(this);
}

12345
// 实现OnClickListener的回调public void onClick(View v) {  // 这里做按钮点击后需要做的事情}...

}

注意上例中的onClick()回调方法没有返回值,但是有些其他的事件监听器方法必须要返回一个布尔值。具体情况取决于事件。以下是针对部分情况进行说明:
* onLongClick() - 这个方法返回一个布尔值,用来指明这个事件是否已经被你消耗(使用)了而不应进一步传递。也就是说,如果返回true,表明你已经处理了这个事件,并且事件应该就此停止;如果返回false,表明你没有处理这个事件,并且/或者这个事件应该继续传递给其他的on-click监听器来处理。
* onKey() - 这个方法返回一个布尔值,用来指明这个事件是否已经被你消耗了而不应进一步传递。也就是说,如果返回true,表明你已经处理了这个事件,并且事件应该就此停止;如果返回false,表明你没有处理这个事件,并且/或者这个事件应该继续传递给其他的on-key监听器来处理。
* onTouch() - 这个方法返回一个布尔值,用来指明你的监听器是否消耗这个事件。重要的是这个事件可能包含多个彼此跟随的动作。因此,如果在接收到按下动作事件时返回了false,表明你不使用这个事件,并且对这个事件中按下动作的后续动作也不感兴趣了。这样的话,你将不会再为这个事件中的任何其他动作调用这个方法,比如手势或者结束动作事件。

记住,硬件按键事件始终是发送给当前焦点所在的View。它们从View层次结构的最顶层开始从上而下派发,直到到达合适的目标。如果你当前的View(或View的子视图)有焦点,那么通过dispatchKeyEvent()方法你能够看到事件的派发路线。通过View捕获按键事件的另一个方法是,你可以在Activity内部通过onKeyDown()和onKeyUp()两个方法来接受所有的按键事件。

而且,当考虑到程序的文本输入的时候,请注意许多设备只有软键盘输入法。这些输入法不要求必须有实体按键;一些设备可能使用语音输入、手写输入等等。即使一个输入法展现了像实体键盘一样的界面,通常它也不会触发onKeyDown()一类的事件。你永远不应该让你的UI依赖特定的按键来实现操作,除非你想将你的程序限制在拥有实体键盘的设备上。尤其,不要依赖在这些按键上按下return键来确认输入;而应该用IME_ACTION_DONE一类的动作来表示输入法完成了程序中你想要的动作,以便程序用应有的方式来改变UI。不用去猜测软键盘工作的方式,只相信它会为程序提供格式化的文本就够了。

注意:Android会首先调用事件处理器,然后再调用类定义的合适的默认处理程序。这样,如果这些事件监听器返回了true,那么事件向其他事件监听器的传递会被中止,View中默认的事件处理程序的回调也会被阻止。因此,当你却确信要中止一个事件的时候才返回true。

事件处理器

如果你要从View构建一个自定义的组件,你可以定义几个回调方法作为默认的事件处理器。在Custom Components | 自定义组件 - Custom Components文档中,你可以学到一些用来处理事件的常用回调,包括:
* onKeyDown(int, KeyEvent) - 当新的按键事件产生的时候调用
* onKeyUp(int, KeyEvent) - 当一个按键松开事件产生的时候调用
* onTrackballEvent(MotionEvent) - 当轨迹球滚动的时候调用
* onTouchEvent(MotionEvent) - 当触屏动作产生时调用
* onFocusChanged(boolean, int, Rect) - 当View获取或者失去焦点的时候调用
另外还有一些需要注意的方法,它们不属于View类,但是能够直接影响到你处理事件的方式。因此,当你管理布局中更复杂的事件的时候,考虑下面这些方法:
* Activity.dispatchTouchEvent(MotionEvent) - 这个方法允许Activity在触屏事件被传递到window之前拦截它们。
* ViewGroup.onInterceptTouchEvent(MotionEvent) - 这个方法允许ViewGroup查看传递到它的子view的触屏事件。
* ViewParent.requestDisallowInterceptTouchEvent(boolean) - 在父View中调用这个方法来表明它不应该使用onInterceptTouchEvent(MotionEvent)来拦截触屏事件。

触摸模式

当用户使用方向键或者轨迹球浏览到一个用户界面的时候,就需要把焦点交给可交互的对象(比如按钮)以便用户能够看到这些对象能够接受输入。但是,如果一个设备支持触摸,并且用户通过触摸来实现交互,那么就不必高亮显示这些对象或者把焦点交给某个View了。这样,就有了一个名为触摸模式的交互模式了。

对一个支持触摸的设备,一旦用户触摸了屏幕,设备就进入了触摸模式。从此,只要一个View的isFocusableInTouchMode()方法返回值为true,那它就是可以获得焦点的,比如一个文本编辑框。其他可以触摸的View比如按钮,在被触摸的时候是不会获得焦点的;它们仅仅是在按下的时候触发一下它们on-click监听器。

每当用户点击了一个方向键或者滚动轨迹球的时候,设备就将退出触摸模式,并寻找一个View让其获得焦点。现在,用户就恢复到了不需要触摸屏的交互模式了。

触摸模式的状态是整个系统维护的(所有的窗口和活动视图)。要查看当点设备所处的触摸模式状态,可以调用isInTouchMode()方法得到当前设备是否处于触摸模式。

处理焦点

Android框架会处理焦点的移动来响应用户的输入,包括当View被删除或隐藏,或者新的View变得可见时的焦点变化。View通过isFocusable()方法来表明它们是否希望获得焦点。通过调用setFocusable()方法来设置一个View能否获得焦点。在触摸模式下,你通过isFocusableInTouchMode()方法查询一个View是否允许获得焦点。当然你可以通过调用setFocusableInTouchMode()方法来改变设置。

焦点的移动是基于一种在特定方向查找最近的可接受焦点的View的算法。在某些不常见的情况下,这种默认的算法找到的焦点可能与开发者的期望表现不一致。这时,你就要在下面的布局文件中的xml属性来精确指定焦点的移动:nextFocusDown, nextFocusLeft, nextFocusRight, 以及 nextFocusUp。给将要失去焦点的View添加一个上面的属性,在属性值中定义下一个将要获得焦点的View的id。比如:




通常,在这样一个垂直布局中,从第一个按钮向上浏览不会到任何地方,同样从第二个按钮向下浏览也不会到任何地方。现在,top按钮定义下一个向上导航的焦点获得者为bottom按钮(反之亦然),这样焦点就可以在这两个按钮之间上下循环了。

如果你习惯在UI中声明一个默认拥有焦点的View(通常不这样做),就需要在布局声明文件中给这个View添加一个android:focusable属性并把这个属性值设置为true。你也可以在触摸模式中使用android:focusableInTouchMode属性来定义默认拥有焦点的View。

要强制让某个View获得焦点,调用它的requestFocus()方法。

就像上面“事件监听器”章节讨论的一样,使用onFocusChange()方法来监听焦点变化事件(当一个View获取到或者失去焦点的时候会发出通知)。

更多相关文章

  1. 浅谈Java中Collections.sort对List排序的两种方法
  2. Python list sort方法的具体使用
  3. python list.sort()根据多个关键字排序的方法实现
  4. android上一些方法的区别和用法的注意事项
  5. android实现字体闪烁动画的方法
  6. Android中dispatchDraw分析
  7. android studio Could not find com.android.support.constraint
  8. android ndk编译x264开源(用于android的ffmpeg中进行软编码)
  9. Android四大基本组件介绍与生命周期

随机推荐

  1. Android智能手机搜索不到路由器无线信号
  2. Google 推出 Android(安卓)Design 網站,教
  3. Android通过adb查看wifi密码
  4. Android(安卓)NDK开发学习(一)
  5. 基于移动平台的多媒体框架——移植Live55
  6. Android中我为什么发不了邮件--Android邮
  7. 别人花了几万元学的Android高级技术,我帮
  8. (三)Android数据结构学习之队列
  9. Android控制闪光灯的方法(打开与关闭)
  10. 详述Google的Android平板App开发准则