我们会经常在代码中看到触屏事件的处理,有时候使用onTouch,有时候使用了onTouchEvent,还有的时候使用了onClick,那么我们有没有对这三个方法进行过思考:
- 三者有什么区别呢?
- 要是三者都在代码中出现,应该是怎样的调用关系呢?
首先,我先把结论给出来:
- onTouch, onTouchEvent, onClick,三个方法的调用顺序是:
onTouch ----> onTouchEvent -----> onclick - onTouch,onClick需要实现相应的接口;onTouchEvent是View的成员方法【onTouch()的使用需要实现View.OnTouchListener,onclick()要实现View.OnClickListener】
然后,答案还是要从源码里面找:
我们之前有对Android_activity事件分发流程分析,我们已经对Android的事件分发有了整体上的了解,事件处理中主要是在dispatchTouchEvent()方法中进行处理的;接下来我们就再来看看这个dispatchTouchEvent()方法,并寻找我们需要的答案:
/frameworks/base/core/java/android/view/View.java
public boolean dispatchTouchEvent(MotionEvent event) {
...
// ①验证符不符合安全策略
if (onFilterTouchEventForSecurity(event)) {
...
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
// ②调用OnTouchListener的ontouch()方法
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
// ③调用ontouchEvent()方法
if (!result && onTouchEvent(event)) {
result = true;
}
}
...
}
1 onTouch()
我们在dispatchTouchEvent()方法中,看到,当当前事件满足安全策略后,就会进入处理事件的过程中,首先,我们看到有li.mOnTouchListener.onTouch的调用。在这里我们需要来看看这个mOnTouchListener是什么?
1.1 OnTouchListener
/**
* Interface definition for a callback to be invoked when a touch event is
* dispatched to this view. The callback will be invoked before the touch
* event is given to the view.
*/
public interface OnTouchListener {
/**
* Called when a touch event is dispatched to a view. This allows listeners to
* get a chance to respond before the target view.
*
* @param v The view the touch event has been dispatched to.
* @param event The MotionEvent object containing full information about
* the event.
* @return True if the listener has consumed the event, false otherwise.
*/
boolean onTouch(View v, MotionEvent event);
}
如上所示,我们可以看到。OnTouchListener是在将触摸事件分派到此视图时要调用的回调的接口定义。在将touch事件提供给视图之前将被回调。如果我们要能够回调这个onTouch()方法,就需要实现OnTouchListener接口。当我们自己在开发中实现了该接口,就在onTouch()方法中对事件进行处理。并且当前onTouch()是最早别调用的。
2 onTouchEvent()
当前面的OnTouchListener.onTouch()方法未被调用或者没有实现OnTouchListener接口,代码就会走到下面的onTouchEvent()方法中:
public boolean onTouchEvent(MotionEvent event) {
...
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
...
// 使用Runnable并发布此消息,而不是直接调用performClick。
// 这允许在单击操作开始之前更新视图的其他可视状态
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClickInternal();
}
...
case MotionEvent.ACTION_DOWN:
...
return false;
}
在OnTouchEvent()方法中我们看到。在action为MotionEvent.ACTION_UP时,下面有段代码:
if (!post(mPerformClick)) {
performClickInternal();
}
我们先在这里给出一个小结果:post(mPerformClick)方法最后会走到onClick()方法,所以onClick()方法是在按键抬起后才执行的,而onTouchEvent()方法会分派事件时就会被执行,所有我们这里明显可以看到。onTouchEventHub()方法要在onClick()方法之前被执行;
3. onClick()
我们在上面代码中看到有post(mPerformClick)的执行,这个post方法我们在Android_UI_update(子线程更新UI的原理和源码梳理)讨论过,这里就不细说了,我们只要知道它将会在主线程中被执行,我们就直接到mPerformClick的定义出看看:
mPerformClick = new PerformClick();
...
private final class PerformClick implements Runnable {
@Override
public void run() {
performClickInternal();
}
}
mPerfromClick是PerformClick实例,它继承了Runable接口。然后我们在它的run()方法看到有performClickInternal()方法,这就是mPerformClick需要做的事,我们继续接着看:
private boolean performClickInternal() {
...
return performClick();
}
....
public boolean performClick() {
...
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
...
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
...
return result;
}
顺着performClickInternal()方法一直往下看,最后我们回来到performClick()方法中,我们就主要关注一下这个方法:在performClick()方法中,会有以下的判断:
if (li != null && li.mOnClickListener != null) {
以上判断就是确认我们当前是不是有注册监听,监听中有没有OnClickListener?如果成立的话,就会调用onClick()方法来处理当前事件。
小结
经过以上代码的梳理,我们对刚开始提出的问题和给出答案进行了源码层次的验证。再次给出三者的区别和调用关系:
- onTouch, onTouchEvent, onClick,三个方法的调用顺序是:
onTouch ----> onTouchEvent -----> onclick - onTouch,onClick需要实现相应的接口;onTouchEvent是View的成员方法【onTouch()的使用需要实现View.OnTouchListener,onclick()要实现View.OnClickListener】