事件概论
在Android开发中,事件的处理是非常重要的一部分,Android程序的事件流程参考了Java中的事件处理操作。 如要进行事件处理,必须要有一个事件源。事件源的产生可以有很多形式,如单击按钮、长按等,之后根据此事件源找到相应的事件处理操作类对事件进行处理。这篇文章中将详细的分析Android事件。
上图中事件处理主要涉及3个对象:
- Event Source(事件源):事件发生的场所,可以简单的理解为组件。
- Event(事件):事件封装了界面组件上发生的特定事件,通常是用户的操作,比如点击事件。可以通过Event获取具体事件。
- Event Listener(事件监听器):监听事件源所发生的事件,并做出相应的处理。
事件监听器
事件监听器是View类中包含一个回调方法的接口。 当用户与APP发生交互,触发已注册此视图的监听器时,Android 将调用这些方法。
View.OnClickListener
:当用户点击组件时, 调用此方法。
View.OnLongClickListener
: 当用户长按组件时,将调用此方法。此方法必须返回一个布尔值,返回 true 表示您已经处理事件且事件应就此停止;如果您尚未处理事件和或事件应该继续传递给其他任何点击监听器,则返回 false。
View.OnFocusChangeListener
: 当组件的焦点改变时,将调用此方法。
View.OnKeyListener
:当APP的键盘发生状态改变时,将调用此方法。此方法必须返回一个布尔值,表示您是否已处理完事件,以及是否应该将它继续传下去。 也就是说,返回 true 表示您已经处理事件且事件应就此停止;如果您尚未处理事件和或事件应该继续传递给其他任何按键监听器,则返回 false。
View.OnTouchListener
: 当用户执行可视为触摸事件的操作时,其中包括按下、释放或屏幕上的任何移动手势,将调用此方法。在复杂的布局中,此方法的调用永远一定的先后顺序(在后面将详细提及)。
View.OnCreateContextMenuListener
: 当需要生成菜单时,将调用此方法。
这些方法是其相应接口的唯一成员。要定义其中一个方法并处理事件,一般的做法是在 Activity 中实现嵌套接口或将其定义为匿名类。然后,将实现的实例传递给相应的View.set...Listener()
方法.例如,调用setOnClickListener()
并向其传递OnClickListener实现。
匿名类实现
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//TODO someting
}
});
事件处理程序(Event)
默认事件处理程序的回调方法中,有如下几种用于事件处理的常见回调,其中包括:
-
onKeyDown(int, KeyEvent)
:在发生新的按键事件时调用。 -
onKeyUp(int, KeyEvent)
:在发生按键弹起事件时调用。 -
onTrackballEvent(MotionEvent)
:在发生轨迹球运动事件时调用。 -
onTouchEvent(MotionEvent)
:在发生触摸屏运动事件时调用。 -
onFocusChanged(boolean, int, Rect)
:在视图获得或失去焦点时调用。
事件的类型:
ACTION_DOWN
:表示用户开始触摸。
ACTION_MOVE
:表示用户在移动(手指或者其他)。
ACTION_UP
:表示用户抬起了手指。
ACTION_CANCEL
:表示手势被取消了。
ACTION_OUTSIDE
:表示用户触碰超出了正常的UI边界。
ACTION_POINTER_DOWN
:有一个非主要的手指按下了。
ACTION_POINTER_UP
:一个非主要的手指抬起来了。
事件发生的位置,x,y轴:
getX()
:获得事件发生时,触摸的中间区域在屏幕的X轴。
getY()
:获得事件发生时,触摸的中间区域在屏幕的X轴。
在多点触控中还可以通过:
getX(int pointerIndex)
:来获得对应手指事件的发生位置. 获得X轴用。
getY(int pointerIndex)
:来获得对应手指事件的发生位置. 获得Y轴用 。
事件分发机制
在管理布局内更复杂的事件时,对于事件的响应处理会从最顶层的组件不断向子组件传递,一直到最后的View组件。因此,在复杂布局中,对事件的处理也要考虑一下如下方法。
onTouchEvent
:在发生触摸屏运动事件时调用。是真正用来进行业务逻辑处理的地方,返回true表示已经将该事件消费,返回false表明事件继续传递。
onInterceptTouchEvent
:此方法允许ViewGroup监视分派给子视图的事件,返回false表示无需拦截,则递归的调用子组件的dispatchTouchEvent
。返回true表示需要拦截,则直接调用本组件的onTouchEvent
方法进行处理。因此,次方法只会存在于ViewGroup中。
dispatchTouchEvent
:此方法的情况比较复杂,要区分在ViewGroup和View中的其区别。
ViewGroup中dispatchTouchEvent()
具体的执行逻辑:首先执行本组件的
onInterceptTouchEvent
。如果返回false,表明无需拦截,则调用第二个方法,即子组件的dispatchTouchEvent
方法;如果返回true,无需向子组件传递,则直接调用本组件的onTouchEvent
方法。 如果需要向子组件传递事件。如果递归调用子组件的dispatchTouchEvent
返回false,则调用本组件的onTouchEvent
方法;如果递归调用子组件的dispatchTouchEvent
返回true,则无需调用本组件的onTouchEvent
方法。 最后子组件的dispatchTouchEvent
的返回值会返回给父组件的dispatchTouchEvent
方法。view中的
dispatchTouchEvent
会直接调用其自身的onTouchEvent
。
总结
对于事件,重要的是在设计开发中的应用和理解。这里只是分享了我的一些认识。如有不足之处,希望多多指出。文章参考了AndroidAPI指南和Android MotionEvent事件响应机制