跟touch事件相关的3个方法:
public boolean dispatchTouchEvent(MotionEvent ev); //用来分派event
public boolean onInterceptTouchEvent(MotionEvent ev); //用来拦截event
public boolean onTouchEvent(MotionEvent ev); //用来处理event
拥有这三个方法的类:

三个方法的用法:

首先,来看一个例子:这是一个基本的activity,在LinearLayout中放置了一个Button。我们将通过对Button等空间的触摸来分析android系统触摸事件处理机制。
public class ListenerActivity extends Activity implements View.OnTouchListener, View.OnClickListener {
private LinearLayout mLayout;
private Button mButton;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mLayout = (LinearLayout) this.findViewById(R.id.mylayout);
mButton = (Button) this.findViewById(R.id.my_btn);
//对layout和button分别注册触摸监听器
mLayout.setOnTouchListener(this);
mButton.setOnTouchListener(this);
mLayout.setOnClickListener(this);
mButton.setOnClickListener(this);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.i(null, "OnTouchListener--onTouch-- action="+event.getAction()+" --"+v);
return false;
}
@Override
public void onClick(View v) {
Log.i(null, "OnClickListener--onClick--"+v);
}
}
ViewGroup是View的子类)布局,布局中包含一个按钮(View的子类);然后分别对这两个控件设置了Touch与Click的监听事件,具体运行结果如下:.
-
当稳稳的点击Button时打印如下:可以看见,触发了button注册的onTouch和onClick事件
触摸Button时的日志
-
当稳稳的点击除过Button以外的其他地方时打印如下: 可以看见,触发了LinearLayout注册的onTouch和onClick事件
触摸button之外地方的日志
- 当收指点击Button时按在Button上晃动了一下松开后的打印如下: 触发了button的多次onTouch事件
Paste_Image.png
我们看下onTouch和onClick,从参数都能看出来onTouch比onClick强大灵活,毕竟多了一个event参数。这样onTouch里就可以处理ACTION_DOWN、ACTION_UP、ACTION_MOVE等等的各种触摸。现在来分析下上面的打印结果;在1中,当我们点击Button时会先触发onTouch事件(之所以打印action为0,1各一次是因为按下抬起两个触摸动作被触发)然后才触发onClick事件;在2中也同理类似1;在3中会发现onTouch被多次调运后才调运onClick,是因为手指晃动了,所以触发了ACTION_DOWN->ACTION_MOVE…->ACTION_UP。
如果你眼睛比较尖你会看见onTouch会有一个返回值,而且在上面返回了false。你可能会疑惑这个返回值有啥效果?那就验证一下吧,我们将上面的onTouch返回值改为ture。如下:
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.i(null, "OnTouchListener--onTouch-- action="+event.getAction()+" --"+v);
return true;
}

如果onTouch返回true则onClick不会被调运了。
总结结论
经过这个简单的实例验证你可以总结发现:
- Android控件的Listener事件触发顺序是先触发onTouch,其次onClick。
- 如果控件的onTouch返回true将会阻止事件继续传递,返回false事件会继续传递。
从View的dispatchTouchEvent方法说起
在Android中你只要触摸控件首先都会触发控件的dispatchTouchEvent方法(其实这个方法一般都没在具体的控件类中,而在他的父类View中),所以我们先来看下View的dispatchTouchEvent方法,如下:
public boolean dispatchTouchEvent(MotionEvent event) {
// If the event should be handled by accessibility focus first.
if (event.isTargetAccessibilityFocus()) {
// We don't have focus or no virtual descendant has it, do not handle the event.
if (!isAccessibilityFocusedViewOrHost()) {
return false;
}
// We have focus and got the event, then use normal event dispatch.
event.setTargetAccessibilityFocus(false);
}
boolean result = false;
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
final int actionMasked = event.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Defensive cleanup for new gesture
stopNestedScroll();
}
if (onFilterTouchEventForSecurity(event)) {
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;//如果view时enable的测onTouch返回true
}
//在这里调用onTouchEvent()
if (!result && onTouchEvent(event)) {
result = true;
}
}
if (!result && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
}
// Clean up after nested scrolls if this is the end of a gesture;
// also cancel it if we tried an ACTION_DOWN but we didn't want the rest
// of the gesture.
if (actionMasked == MotionEvent.ACTION_UP ||
actionMasked == MotionEvent.ACTION_CANCEL ||
(actionMasked == MotionEvent.ACTION_DOWN && !result)) {
stopNestedScroll();
}
return result;
}
li.mOnTouchListener是不是null取决于控件(View)是否设置setOnTouchListener监听,在上面的实例中我们是设置过Button的setOnTouchListener方法的,所以也不为null;接着通过位与运算确定控件(View)是不是ENABLED 的,默认控件都是ENABLED 的;接着判断onTouch的返回值是不是true。通过如上判断之后如果都为true则设置默认为false的result为true,那么接下来的if (!result && onTouchEvent(event))就不会执行,最终dispatchTouchEvent也会返回true。而如果if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event))语句有一个为false则if (!result && onTouchEvent(event))就会执行,如果onTouchEvent(event)返回false则dispatchTouchEvent返回false,否则返回true。
这下再看前面的实例部分明白了吧?控件触摸就会调运dispatchTouchEvent方法,而在dispatchTouchEvent中先执行的是onTouch方法,所以验证了实例结论总结中的onTouch优先于onClick执行道理。如果控件是ENABLE且在onTouch方法里返回了true则dispatchTouchEvent方法也返回true,不会再继续往下执行;反之,onTouch返回false则会继续向下执行onTouchEvent方法,且dispatchTouchEvent的返回值与onTouchEvent返回值相同。
所以依据这个结论和上面实例打印结果你指定已经大胆猜测认为onClick一定与onTouchEvent有关系?是不是呢?先告诉你,是的。下面我们会分析。
网友评论