简单例子了解View的事件分发
什么是事件分发
我们在写自定义ViewGroup或者自定义View的时候经常要处理用户的点击事件,如果我们的View在最底层,他在很多ViewGroup里面,我们如何让我们的点击事件准确传递到View里面,这就涉及到一个View很重要的知识点,View的事件分发。事件分发,分开来讲就是事件+分发,所谓事件指的就是View的被手机触摸后产生的事件MotionEvent
,而分发指的就是MotionEvent
的传递和处理。
下面,我们说一下单手指触摸事件有哪些
ACTION_DOWN
——手指刚触摸屏幕
ACTION_MOVE
——手指在屏幕上移动
ACTION_UP
———手指从屏幕上松开的一瞬间
事件讲完了,我们接下来说一下分发过程中涉及到的方法
dispatchTouchEvent(MotionEvent ev)
onInterceptTouchEvent(MotionEvent ev)
onTouchEvent(MotionEvent event)
所以事件分发,结合这些代码就是每一个ACTION皆会触发那些方法。我们在要做就是根据需求来决定那个事件分发到那层,以及搞清楚为什么会这样分发。
接下来,我们通过一个例子来仔细讲讲这三个方法以及上述三个事件。
简单的例子了解事件分发
测试的例子如上,我们编写三个自定义view来演示这个效果,第一个是ViewGroupA,也就是最外层的绿的,第二个是ViewGroupB,也就是中间的蓝的,然后是最里面的黑色的View。XML布局如下:
<com.byhieg.viewdipatch.custormview.ViewGroupA
android:layout_width="300dp"
android:layout_height="300dp"
android:background="@android:color/holo_green_light">
<com.byhieg.viewdipatch.custormview.ViewGroupB
android:layout_width="200dp"
android:layout_height="200dp"
android:background="@android:color/holo_blue_light">
<com.byhieg.viewdipatch.custormview.ViewTest
android:layout_width="100dp"
android:layout_height="100dp" />
</com.byhieg.viewdipatch.custormview.ViewGroupB>
</com.byhieg.viewdipatch.custormview.ViewGroupA>
ViewGroupA 里面放入子View ——ViewGroupB 然后ViewGroupB放入子View-ViewTest
三者的代码如下:
ViewGroupA
:
public class ViewGroupA extends ViewGroup{
public ViewGroupA(Context context) {
super(context);
}
public ViewGroupA(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ViewGroupA(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureChildren(widthMeasureSpec,heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
for(int i = 0;i < childCount;i++) {
View child = getChildAt(i);
child.layout(0,0, Change.dip2px(getContext(),200),Change.dip2px(getContext(),200));
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("ViewGroupA","ViewGroupA dispatchTouchEvent" + ev.getAction());
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e("ViewGroupA","ViewGroupA onInterceptTouchEvent" + ev.getAction());
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("ViewGroupA","ViewGroupA onTouchEvent" + event.getAction());
return super.onTouchEvent(event);
}
}
ViewGroupB
:
public class ViewGroupB extends ViewGroup{
public ViewGroupB(Context context) {
super(context);
}
public ViewGroupB(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ViewGroupB(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureChildren(widthMeasureSpec,heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
for(int i = 0;i < childCount;i++) {
View child = getChildAt(i);
child.layout(0,0,getMeasuredWidth(),getMeasuredHeight());
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("ViewGroupB","ViewGroupB dispatchTouchEvent" + ev.getAction());
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e("ViewGroupB","ViewGroupB onInterceptTouchEvent" + ev.getAction());
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("ViewGroupB","ViewGroupB onTouchEvent" + event.getAction());
return super.onTouchEvent(event);
}
}
ViewTest
:
public class ViewTest extends View{
private Paint paint;
public ViewTest(Context context) {
super(context);
init();
}
public ViewTest(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ViewTest(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.BLACK);
paint.setStrokeWidth(10);
paint.setStyle(Paint.Style.FILL);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(0,0, Change.dip2px(getContext(),100), Change.dip2px(getContext(),100),paint);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.e("ViewTest","View dispatchTouchEvent" + event.getAction());
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("ViewTest","View onTouchEvent" + event.getAction());
return super.onTouchEvent(event);
}
}
根据我们写入的Log,当我们点击最里面的ViewTest的时候,我们会看到如下的Log输出
08-30 07:47:13.741 7574-7574/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent0
08-30 07:47:13.741 7574-7574/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent0
08-30 07:47:13.741 7574-7574/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent0
08-30 07:47:13.741 7574-7574/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent0
08-30 07:47:13.741 7574-7574/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent0
08-30 07:47:13.741 7574-7574/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent0
08-30 07:47:13.741 7574-7574/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onTouchEvent0
08-30 07:47:13.741 7574-7574/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onTouchEvent0
这些方法默认返回时false
然后按照如下传递原则:
在事件分发的时候,最外层的ViewGroup首先调用
dispatchTouchEvent()
方法,然后再执行onInterceptTouchEvent()
方法,而View是没有onInterceptTouchEvent()
的方法的,在传递的时候,如果这些方法返回的是false
,则表示不拦截,事件会下面传递。我们在覆写这些方法的时候,不作处理,默认返回时false
,所以我们可以看到事件传递到了ViewTest的dispatchTouchEvent()
,但注意dispatchTouchEvent()
与onInterceptTouchEvent()的区别,如果事件传递到了这个View,则dispatchTouchEvent()
方法一定会调用,而onInterceptTouchEvent()
方法则在dispatchTouchEvent()
内部调用,表示是否拦截事件,所以当我们需要拦截的时候一般改写onInterceptTouchEvent()
在事件处理的时候,则是从分发到了最底层的View开始向上处理,在onTouchEvent(),返回了true,则表示这个View已经处理了 ,不必在向上传递,但我们覆写这些方法的时候,不作处理,默认返回时false
,所以继续向上传递,到了最上层的ViewGroup中。这就是我们验证的结果。
这种结果我们可以用现实中的例子来解释,这个例子是网上看到了,很生动形象。我们把整个事件传递的View看成是一个公司,ViewGroupA是一个总经理,ViewGroupB是一个部长,ViewTest是一个员工。现在来了一个任务,总经理觉得不需要自己处理,返回了false
,就传递给了下一层部长,部长看了也觉得不需要处理,继续返回false
,传递给了底层员工,员工做了做,发现太难了,处理不了,就返回了false
,然后上层部长接手,发现确实很难,也处理不了,继续返回false
,传递给了总经理。整个事件分发处理就结束了。
现在,又来了一个新任务,我们总经理有了前车之鉴,决定自己处理这件事,不在交给下面的人做,因为给了他们,他们也处理不好,所以他决定自己拦截这个事件,于是在ViewGroupA中的onInterceptTouchEvent()
中返回true,查看效果
08-30 08:53:31.125 23810-23810/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent0
08-30 08:53:31.126 23810-23810/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent0
08-30 08:53:31.126 23810-23810/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onTouchEvent0
确实如我们之前所说的,是这样一个处理流程,ViewGroupA自己弄完了所有事情,随着事件的变多,总经理终于累倒了,于是他决定把事情分给部长,自己只处理部长处理不了的,于是我们总经理不拦截事件,而是部长拦截事件,我们看看效果
08-30 08:59:27.702 23810-23810/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent0
08-30 08:59:27.702 23810-23810/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent0
08-30 08:59:27.702 23810-23810/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent0
08-30 08:59:27.702 23810-23810/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent0
08-30 08:59:27.702 23810-23810/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onTouchEvent0
08-30 08:59:27.702 23810-23810/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onTouchEvent0
确实,事件分发到了部长,而部长也如愿的没处理好事件,传递给了总经理,如此以往,因为处理如此多的事件,总经理的病再也没好。于是,部长,和底层员工决定好好提高自身水平,不在把事件传递给总经理,于是,我们在底层员工ViewTest的onTouchEvent()
返回true,表示他已经处理好事情,我们看看效果
08-30 09:10:27.550 23810-23810/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent0
08-30 09:10:27.550 23810-23810/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent0
08-30 09:10:27.550 23810-23810/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent0
08-30 09:10:27.550 23810-23810/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent0
08-30 09:10:27.550 23810-23810/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent0
08-30 09:10:27.550 23810-23810/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent0
这是一部分的Log,后面还有,不过我们先讲解上半部分,在这里,我们看到事件处理到ViewTest就为止了,所以事件处理没有传递到ViewGroupB,当然有些问题,确实底层员工处理不了,于是,我们在ViewGroupB的onTouchEvent()
返回true。
08-30 09:15:28.998 23810-23810/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent0
08-30 09:15:28.998 23810-23810/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent0
08-30 09:15:28.998 23810-23810/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent0
08-30 09:15:28.998 23810-23810/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent0
08-30 09:15:28.998 23810-23810/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent0
08-30 09:15:28.998 23810-23810/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent0
这样,事件确实没有在传递给总经理。底层员工,部长已经足够胜任工作,总经理因为没有杂事压身,身体也逐渐康复,这家公司正常运转。Happy Ending
更多规则
在view进行事件处理的时候,如果他设置了onTouchListener
,那么onTouchListener
中的onTouch()
方法将被调用,这时,我们需要根据onTouch()
的返回值来决定onTouchEvent
会不会调用,如果返回的是false
,则表示会继续调用onTouchEvent
,返回的是true
,则不会。下面我们验证一下:返回为true
的时候。
在MainActivity中,设置viewTest的事件,代码如下:
ViewTest view = (ViewTest)findViewById(R.id.viewTest);
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
Log.e("onTouch","我中弹了");
return true;
}
});
08-30 10:40:38.209 32359-32359/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent0
08-30 10:40:38.209 32359-32359/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent0
08-30 10:40:38.209 32359-32359/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent0
08-30 10:40:38.209 32359-32359/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent0
08-30 10:40:38.209 32359-32359/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent0
08-30 10:40:38.209 32359-32359/com.byhieg.viewdipatch E/onTouch: 我中弹了
可以看出没有打印出viewTest的onTouchEvent
日志,证明确实没有调用,现在设置返回为false
, onTouchEvent
被调用的日志会出现,受限于篇幅,结果就不放出来了。
接下来,我们在ViewTest的onTouchEvent
中设置一个点击监听,查看一些好玩的东西。首先,我先设置我们的ViewTest为可点击,viewTest的onTouch返回值也是false,然后我们看一下onTouchEvent代码:
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("ViewTest","View onTouchEvent" + event.getAction());
Log.e("More","我被调用");
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
Log.e("ViewTest","原来中弹的不是我");
}
});
return false;
}
这时点击我们的viewTest,我们发现他竟然没有点击事件的日志。这是因为我们直接返回的false,截断了后面的点击事件的触发,所以决定什么都不做,采用默认的实现,即
return super.onTouchEvent(event);
神奇的事情发生了,出现了该有的点击效果。我们查看Log
08-30 13:07:04.834 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent0
08-30 13:07:04.834 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent0
08-30 13:07:04.834 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent0
08-30 13:07:04.834 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent0
08-30 13:07:04.834 16083-16083/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent0
08-30 13:07:04.834 16083-16083/com.byhieg.viewdipatch E/onTouch: 我中弹了
08-30 13:07:04.834 16083-16083/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent0
08-30 13:07:04.834 16083-16083/com.byhieg.viewdipatch E/More: 我被调用
08-30 13:07:04.931 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent1
08-30 13:07:04.931 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent1
08-30 13:07:04.931 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent1
08-30 13:07:04.931 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent1
08-30 13:07:04.931 16083-16083/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent1
08-30 13:07:04.931 16083-16083/com.byhieg.viewdipatch E/onTouch: 我中弹了
08-30 13:07:04.931 16083-16083/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent1
08-30 13:07:04.931 16083-16083/com.byhieg.viewdipatch E/More: 我被调用
08-30 13:07:04.932 16083-16083/com.byhieg.viewdipatch E/ViewTest: 原来中弹的不是我
这是完整的调用日志,我们可以仔细看一下,会发现出现点击事件的日志在最后一行,而之前重复出现了ViewGroupA和ViewGroupB的事件分发。是时候揭露出完整的触发过程了。
当我们手指按下时候,会触发ACTION_DOWN的事件,这时会进行一系列的事件分发,分发规则如上面所讲,当我们手指松开的时候,会触发ACTION_UP的事件,这同样会进行一系列的事件分发,最后当这些都弄完之后,会触发
onClick()
事件,也就是说onClick()
是事件触发的尾端。一旦前面决定不继续传递事件,则onClick()
事件就不会触发。
所以这里面有一个概念叫做同一事件序列:指从手指接触屏幕的那一刻起,到手指离开屏幕的那一刻结束,在这个过程中所产生的一系列事件,这个事件序列以down事件开始,中间含有数个move事件,最终以up事件结束。
简而言之就是down-> move-> move-> move-> move......->up
而上面我们提到的onclick()
如果大家看源码的话,会发现他是ACTION_UP
之后通过performClick()
调用的。所以如果view设置了点击事件并且能向后传递,则会最后调用onClick()。
如果仔细看上面的日志,会发现我们的打印日志的代码是这样写的Log.e("ViewTest","View onTouchEvent" + event.getAction());
日志最后会出现事件的名字,在日志里面,由于getAction
返回的int型的静态常量,所以是用0, 1表示,0代表ACTION_DOWN
,1代表ACTION_UP
,所以通过这个日志,我们可以验证这个同一事件序列这个过程。前面我放出的日志实际也是不全 我也只是把DOWN的会就放出来,日志后面还有UP的事件,处于篇幅就不全放了而已。
而针对同一事件序列 上面的例子还会说明一个特性
注意,一开始,我们直接将
onTouchEvent(MotionEvent event)
返回值设置为false,这样他会截断后面ACTION_UP事件的触发,通俗来讲,就是传递到VIew的DOWN事件,他都没有消耗他,没处理好他,返回了false,让他上层处理,则同一事件序列的MOVE,UP也不会给他。结合我们之前公司的例子,就是这个DOWN事件都办不好,后面也不会给他其他任务了。
我们验证一下:
08-31 07:18:40.791 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent0
08-31 07:18:40.791 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent0
08-31 07:18:40.791 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent0
08-31 07:18:40.791 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent0
08-31 07:18:40.791 16083-16083/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent0
08-31 07:18:40.791 16083-16083/com.byhieg.viewdipatch E/onTouch: 我中弹了
08-31 07:18:40.791 16083-16083/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent0
08-31 07:18:40.791 16083-16083/com.byhieg.viewdipatch E/More: 我被调用
08-31 07:18:40.791 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onTouchEvent0
08-31 07:18:40.791 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onTouchEvent0
这是全部的日志,确实没有UP事件。
复杂的规则
下面的规则是出自《Android开发艺术探索》,我们对这些规则进行讲解,验证
- 同一事件序列是指从手机接触屏幕的那一刻起,到手指离开屏幕的那一刻结束,在这个过程中所产生的一些列事件,这个事件序列以DOWN事件开始,中间含有数个MOVE事件,最终以UP事件结束
- 某个View一旦决定拦截,那么这一个事件序列都只能有他处理,并且它的
onInterceptTouchEvent
不会被调用 - 正常情况下,一个事件序列只能被一个View拦截且消耗。
- 某个View一旦开始处理事件,如果他不消耗ACTION_DOWN,那么同一事件序列中其他事件不会交给他处理,并且事件将重新交由它的父元素去处理,即父元素的
onTouchEvent
会被调用 - 如果View不消耗除了ACTION_DOWN以外的其他事件,那么这个点击事件会消失,此时父元素的
onTouchEvent
并不会被调用,并且当前View可以持续受到后续的事件,最终这些消失的点击事件会传递给Activity处理 - ViewGroup默认不拦截任何事件。
- View没有
onInterceptTouchEvent
方法,一旦有点击事件传递给他,那么他的onTouchEvent方法会被调用 - View的
onTouchEvent
默认会消耗事件,除非他是不可点击的。 - View的enable属性不影响
onTouchEvent
的默认返回值。哪怕一个View是disable
状态,只要他的clickable或者longClickable有一个true,那么他的onTouchEvent
返回true。 onClick
会发生的前提是当前View是可点击的,并且他收到了down和up事件。
我们分别讲解和验证一下大神总结这些结论。
第一条,之前已经讲过了,就不说了
第二条,一旦View决定拦截,则表明这个View要自己上手处理这个事件,那他势必不会再将事件传递下去,自然也不会调用onInterceptTouchEvent
第三条,一般,一个事件序列:down-> move-> move-> move-> move......->up
只会交给一个View处理,因为第二条,一旦决定拦截,则表明他要自己上手处理这个。特殊情况会通过onTouchEvent强行传递给其他View,这也是书中原话,但至今不清楚怎么强行传递给其他View。
第四条,这句话的意思就是如果DOWN事件返回的是false,则UP事件和MOVE事件也不会用来处理。而且会把事件交给父元素去处理,刚才的日志,已经验证了这点。
第五条,如果DOWN事件返回的是true,而MOVE事件返回的false,则这个事件会消失,父元素的onTouchEvent
并不会调用,我们修改下我们的代码验证下:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e("ViewTest","View onTouchEvent" + event.getAction());
return true;
case MotionEvent.ACTION_MOVE:
Log.e("ViewTest","View onTouchEvent" + event.getAction());
return false;
case MotionEvent.ACTION_UP:
Log.e("ViewTest","View onTouchEvent" + event.getAction());
return true;
}
return super.onTouchEvent(event);
}
我们将代码改成这个样子,然后运行看日志:
08-31 08:09:13.076 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent0
08-31 08:09:13.076 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent0
08-31 08:09:13.076 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent0
08-31 08:09:13.076 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent0
08-31 08:09:13.076 16083-16083/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent0
08-31 08:09:13.077 16083-16083/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent0
08-31 08:09:13.148 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent2
08-31 08:09:13.148 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent2
08-31 08:09:13.148 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent2
08-31 08:09:13.148 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent2
08-31 08:09:13.149 16083-16083/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent2
08-31 08:09:13.149 16083-16083/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent2
08-31 08:09:13.166 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent2
08-31 08:09:13.166 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent2
08-31 08:09:13.166 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent2
08-31 08:09:13.166 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent2
08-31 08:09:13.166 16083-16083/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent2
08-31 08:09:13.166 16083-16083/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent2
08-31 08:09:13.180 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent2
08-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent2
08-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent2
08-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent2
08-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent2
08-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent2
08-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent1
08-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent1
08-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent1
08-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent1
08-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent1
08-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent1
日志很长,因为不小心移动多了,触发了多个移动事件,我们会发现,满足了上述的条件,然后我们的父View确实没有触发onTouchEvent
。斯国一,确实是这个样子的
第六条和第七条,上文提到过,这个不说了 ,因为ViewGroup的这个方法默认返回是false,默认不拦截。而View没有拦截方法,所以必须要处理。
第八条和第九条,处理事件的时候,view的onTouchEvent
默认返回true,表示自己消耗这件事情,这个和我们上面说的默认返回时false是冲突的,这个是因为他后面补了一个条件,这它默认是不点击的,在我们上面的例子中,我们用的自定义的ViewTest,这个在没有引入讨论onClick的时候,他就是默认不可点击的,所以是false,当他可点击之后,我们在xml中设立了clickable属性,他的onTouchEvent
则表示会消耗事件,返回是true,也可以理解嘛,设立了他可点击,就是想让他处理onclick
事件。所以,一旦有一个Clickable或者longClickable为true,那么他的onTouchEvent就返回true。
第十条,onClick触发的前提是view点击,并且收到了down和up事件,这个上面也验证过了。
总结
这十条说完,发现很多东西,我们上文已经提到过,只是归纳的没有他好。他这十条,总体说来,就是
默认的事件传递会到底层的view,根据view的onTouchEvent
方法中对DOWN事件的处理不同,会有不同的结果,如果返回了true,表示处理好了,则后续的MOVE,UP事件还会交给他来做,如果此外还设置了Clickable,则表示这个view也会响应onClick,而如果MOVE和UP返回了false,则会调用父View的onTouchEvent,如果返回了false,表示,这个view不堪重任,后续的MOVE和UP也不会给他做。
如果我们什么都不做,不去主动的更改onTouchEvent的返回结果,则会因为这个View是否能被点击来决定他的事件处理是否向上传递,如果能点击,则不传递,他自己来处理,如果不能点击,则返回false,向上传递。
至此,View事件的分发就到此结束了。后续可能会有根据源码去分析View分发的过程,这个就看事件啦。