说明:关于安卓的事件分发机制总共有三篇,第一篇和第二篇我尝试着通过调试信息向大家说明一些情况,但相信我,越看你会觉得越乱.这跟事件分发机制的复杂有一定的关系.第一篇和第二篇其实只是在给最后一篇做铺垫而已,在最后一篇中我会借助源码和前面的铺垫尽可能的向你展示所有的细节.
前言
最近在开始学习安卓的事件分发机制,之所以要学习,是因为我个人觉得这部分很有必要深入学习一下,毕竟绝大多数的事件都是由我们触摸而触发的,而安卓中有着各种各样的控件,系统又是怎么将我们的触摸事件传递到我们正在交互的控件呢?
这篇博文先通过黑盒的方法来感受一下,即不看源码,只通过程序输出调试信息。以后再来结合源码验证一下。
在安卓学习过程中经常会看到dispatchTouchEvent
、onTouch
、onTouchEvent
、onClick
,经常都会觉得很奇怪和很混乱。别急,我们先通过程序来感受一下吧。
要验证dispatchTouchEvent
、onTouchEvent
的话必须重写View的方法。为了简单起见,用一个Button来代替View。而onTouch
和onClick
通过监听器来实现即可。
准备
先创建CustomButton.java
,然后重写相应的方法。
public class CustomButton extends Button {
private static final String TAG = "event";
public CustomButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "CustomButton---dispatchTouchEvent---action down");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "CustomButton---dispatchTouchEvent---action move");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "CustomButton---dispatchTouchEvent---action up");
break;
}
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "CustomButton---onTouchEvent---action down");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "CustomButton---onTouchEvent---action move");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "CustomButton---onTouchEvent---action up");
break;
}
return super.onTouchEvent(event);
}
}
然后在xml中引入这个自定义的按钮,这里就不贴代码了。然后是MainActivity.java
loginBtn = (CustomButton) findViewById(R.id.login);
loginBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "onClick");
}
});
loginBtn.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "CustomButton onTouch: action down");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "CustomButton onTouch: action move");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "CustomButton onTouch: action up");
break;
}
return false;
}
});
启动一下,然后点击按钮查看一下调试信息。
可以看到,这几个函数的执行顺序是dispatchTouchEvent
-> onTouch
-> onTouchEvent
-> onClick
.所以我们大胆的假设这几个函数的执行顺序正是这样的
除了onTouch
和onClick
之外,其它两个函数都是返回对应函数父类的方法。我们试着来改变他们的返回值,看有什么变化没有。
dispatchTouchEvent
首先是将dispatchTouchEvent
方法返回false,得到以下结果:
这次dispatchTouchEvent
之后的方法不执行了,而且连action up
也没有了。
再来看下返回true的结果是什么.
不同的是,这次还多了个action up
.所以当dispatchTouchEvent
返回true的时候,代表当前的View处理该事件.我们知道,所有的触摸屏幕事件都是从ACTION_DOWN
开始的,在dispatchTouchEvent
中,当前的view监听到该动作并返回false的时候,意味着该view对这个事件不感兴趣,所以ACTION_DOWN
后面的动作都不会再流向该view来处理了.相反,如果返回true的话,意味着当前的view对该事件感兴趣,也因此它才能监听到后续的事件.
我知道这里看起来不是很好理解,但我们可以这样理解(尽管不一定很准确):老板有一项工作想让我来做,这项工作有三个步骤:ACTION_DOWN
, ACTION_MOVE
, ACTION_UP
.但是我很傲娇,老板又不知道我愿不愿意,所以他先以ACTION_DOWN
来试探我.如果我说OK的话,那么好,接下来的ACTION_MOVE
,ACTION_UP
, 都会继续交给我来处理.如果我说抱歉我对这项工作不感兴趣的话,那么老板也就明白我的意思了,该工作的后续部分就都不会交给我了.注意:这里我并没有完成ACTION_DOWN
的工作,所以老板又要找另外一个人来重复这个过程了.
再延伸一点,如果我刚开始是接受该工作的,但是工作到一半的时候我发现我不感兴趣了,那么我可以返回false告诉老板说我不干了(哈哈,就是这么傲娇),那么还是一样,以后的工作老板就会交给另外的人来处理了.
关于dispatchTouchEvent
的情况有些复杂,后续博文会继续讲解的,这里只是给大家有这样一个印象.
不知道大家有没有发现,无论我们返回true还是false,往后的方法都没有继续执行了.再来回顾一下这个方法返回什么吧.
return super.dispatchTouchEvent(event);
这里可能的原因就是dispatchTouchEvent
之后的方法在父类里面被调用了.
onTouch
由于一开始在onTouch
中返回false,所以这次换成true看看有什么结果.(注意,dispatchTouchEvent
中最后要改为返回父类的方法)
后面的方法没有被调用到了.也就是说,当onTouch
返回true的时候,该事件被消费了,不再继续往下传递了.
onTouchEvent
注意到这个方法也是返回父类的方法,所以有可能像dispatchTouchEvent
一样,无论返回什么,onClick
都不会被调用了.大家可以自己试一试,这里就不贴图了.
End
由于不想让篇幅太长,就先写到这里了.后面还会继续更新的.