Android中View的事件分发是一个复杂的过程,看了很多大牛关于这方面的博客和文章,受益匪浅,特写此文章以记录放置忘记!尊重原创,转载请注明http://write.blog.csdn.net/postlist。
网上很多文章都是带大家看源码,我这里通俗易懂的写出,想看源码的自己可以去深入的研究一下。
首先我们看一个事件分发的简单流程,其中不做任何的拦截的操作,直接上代码:
Activity的布局activity_main
<com.example.vg.VG2 xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<com.example.vg.V3
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#00a0e9"
/>
</com.example.vg.VG2>
public class V2 extends Button{
public V2(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public V2(Context context, AttributeSet attrs) {
super(context, attrs);
}
public V2(Context context) {
super(context);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.i("log", "View:dispatchTouchEvent-down");
break;
case MotionEvent.ACTION_MOVE:
Log.i("log", "View:dispatchTouchEvent-move");
break;
case MotionEvent.ACTION_UP:
Log.i("log", "View:dispatchTouchEvent-up");
break;
default:
break;
}
return super.dispatchTouchEvent(event);
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.i("log", "View:onTouchEvent-down");
break;
case MotionEvent.ACTION_MOVE:
Log.i("log", "View:onTouchEvent-move");
break;
case MotionEvent.ACTION_UP:
Log.i("log", "View:onTouchEvent-up");
break;
default:
break;
}
return super.onTouchEvent(event);
// return true;
// return false;
}
输出结果(注意顺序):
ViewGroup:dispatchTouchEvent-down
ViewGroup:onInterceptTouchEvent-down
View:dispatchTouchEvent-down
View:onTouchEvent-down
ViewGroup:onTouchEvent-down
通过看了这个打印结果,有的人可能会说了,为什么你这个没有做拦截操作,输出的只有down,没有move和up,你问的好,看来你的领悟能力很强,听我慢慢将来,因为ViewGroup和View在onTouchEvent中都没有消费事件,所以后续的事件交给了Activity去处理,现在我在Activity中重写dispatchTouchEvent和onTouchEvent:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
int action = ev.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.i("log", "activity:dispatchTouchEvent-down");
break;
case MotionEvent.ACTION_MOVE:
Log.i("log", "activity:dispatchTouchEvent-move");
break;
case MotionEvent.ACTION_UP:
Log.i("log", "activity:dispatchTouchEvent-up");
break;
default:
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
Log.i("log", "onTouchEvent-down");
break;
case MotionEvent.ACTION_MOVE:
Log.i("log", "onTouchEvent-move");
break;
case MotionEvent.ACTION_UP:
Log.i("log", "onTouchEvent-up");
break;
default:
break;
}
return super.onTouchEvent(event);
}
}
打印结果:
activity:dispatchTouchEvent-down //Activity开始分发dowm事件
ViewGroup:dispatchTouchEvent-down //ViewGroup开始分发dowm事件
ViewGroup:onInterceptTouchEvent-down //ViewGroup不拦截,向下传递,交给子View的dispatchTouchEvent
View:dispatchTouchEvent-down //View开始分发dowm事件
View:onTouchEvent-down //View不消费事件,交给上一级处理的onTouchEvent处理
ViewGroup:onTouchEvent-down //ViewGroup也不消费事件,也交给上一级(Activity)
onTouchEvent-down //在Activity的onTouchEvent处理(最终交给Activity处理)
activity:dispatchTouchEvent-move //Activity开始分发move事件
onTouchEvent-move //Activity消费move事件
activity:dispatchTouchEvent-up //Activity开始分发up事件
onTouchEvent-up //Activity消费up事件
总结:
1)事件分发是自上而下,ViewGroup中有3个方法,而View中有2个,少了一个onInterceptTouchEvent方法,因为view是最低一级,它不需要传递事件给子View。
2)事件分发是从顶层的View一直往下分发到手指按下的最里面的View,如果这个View的onTouchEvent()返回false,即不消费Touch事件,这个Touch事件就会向上找父布局调用其父布局的onTouchEvent()处理,如果这个View返回true,表示消费了Touch事件,就不调用父布局的onTouchEvent();注意:onTouchEvent()返回false后将事件交给上级处理,后续的事件就不交给这个操蛋的家伙了,谁让给你脸不要脸,让你不接受(这个是重点,容易让人误解)
3)onInterceptTouchEvent的返回值决定是否拦截事件并将事件交给onTouchEvent处理。true:拦截事件后交给自己的onTouchEvent处理,false:向下传递不拦截。当ViewGroup要拦截事件的时候,那么后续的事件序列都将交给它处理,而不用再调用onInterceptTouchEvent()方法了,onInterceptTouchEvent该方法并不是每次事件都会调用的
如果在onInterceptTouchEvent返回true,onInterceptTouchEvent方法中将不会收到后续的任何事件,目标子控件中除了ACTION_CANCEL外也不会接收分发的后续事件,所有的后续事件将会被交付到你自己的onTouchEvent()方法中,如果你不明白的话,直接看我下面的几个小例子秒懂!
首先,Android中的事件分发是从上至下而执行的。ACTION_DOWN事件为一个事件序列的开始,中间有若干个ACTION_MOVE,最后以ACTION_UP结束。
在事件分发中需要我们重写的有3个方法:
1)dispatchTouchEvent
这个方法的作用我们是否向下分发事件,一般我们不会去重写这个方法
2) onInterceptTouchEvent (默认不拦截):在dispatchTouchEvent中调用(自己去看源码)
这个方法的作用是拦截,此方法默认返回的是false,false的作用是不拦截,分发的事件将传递给子View;如果我们返回了true就代表拦截,同一个事件序列的其他所有事件都会交由这个View处理,此时不再调View(ViewGroup)的onIntercept()方法去询问是否要拦截了,那么拦截到的事件将会交给onTouchEvent处理(如果onTouchEvent向处理的话它就返回true去处理吧)同样子View的事件将被拦截,后续事件也不会接收到。
3)onTouchEvent:它也在dispatchTouchEvent中调用(自己去看源码)
此方法返回true表示对事件进行消费,不向上传递;如果返回false表示对事件不进行消费,那么后续事件将不会再交给它处理,消费事件是自下向上,如果子View不进行消费,将交给上一级的onTouchEvent处理,如果上一级也不处理,再交给上一级,加入没有View或ViewGroupView去处理,最后将交给Activity中的onTouchEvent处理。
几个例子让你秒懂一切奥秘:
代码就不写了,直接用上面的代码。
1)ViewGroup的onInterceptTouchEvent返回true拦截,onTouchEvent返回true消费事件
2)ViewGroup返回onInterceptTouchEvent返回false,和返回默认值的效果一样
3)ViewGroup的onInterceptTouchEvent返回true
4)View的onTouchEvent返回true