什么是事件分发

我们在写自定义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开发艺术探索》,我们对这些规则进行讲解,验证

  1. 同一事件序列是指从手机接触屏幕的那一刻起,到手指离开屏幕的那一刻结束,在这个过程中所产生的一些列事件,这个事件序列以DOWN事件开始,中间含有数个MOVE事件,最终以UP事件结束
  2. 某个View一旦决定拦截,那么这一个事件序列都只能有他处理,并且它的onInterceptTouchEvent不会被调用
  3. 正常情况下,一个事件序列只能被一个View拦截且消耗。
  4. 某个View一旦开始处理事件,如果他不消耗ACTION_DOWN,那么同一事件序列中其他事件不会交给他处理,并且事件将重新交由它的父元素去处理,即父元素的onTouchEvent会被调用
  5. 如果View不消耗除了ACTION_DOWN以外的其他事件,那么这个点击事件会消失,此时父元素的onTouchEvent并不会被调用,并且当前View可以持续受到后续的事件,最终这些消失的点击事件会传递给Activity处理
  6. ViewGroup默认不拦截任何事件。
  7. View没有onInterceptTouchEvent方法,一旦有点击事件传递给他,那么他的onTouchEvent方法会被调用
  8. View的onTouchEvent默认会消耗事件,除非他是不可点击的。
  9. View的enable属性不影响onTouchEvent的默认返回值。哪怕一个View是disable状态,只要他的clickable或者longClickable有一个true,那么他的onTouchEvent返回true。
  10. 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分发的过程,这个就看事件啦。

简单例子了解View的事件分发的更多相关文章

  1. 从源码的角度解析View的事件分发

    有好多朋友问过我各种问题,比如:onTouch和onTouchEvent有什么区别,又该如何使用?为什么给ListView引入了一个滑动菜单的功能,ListView就不能滚动了?为什么图片轮播器里的图 ...

  2. Android View的事件分发机制和滑动冲突解决方案

    这篇文章会先讲Android中View的事件分发机制,然后再介绍Android滑动冲突的形成原因并给出解决方案.因水平有限,讲的不会太过深入,只希望各位看了之后对事件分发机制的流程有个大概的概念,并且 ...

  3. Android事件分发机制详解(1)----探究View的事件分发

    探究View的事件分发 在Activity中,只有一个按钮,注册一个点击事件 [java] view plaincopy button.setOnClickListener(new OnClickLi ...

  4. Android View的事件分发机制

    准备了一阵子,一直想写一篇事件分发的文章总结一下.这个知识点实在是太重要了. 一个应用的布局是丰富的,有TextView,ImageView,Button等.这些子View的外层还有ViewGroup ...

  5. View的事件分发机制解析

    引言 Android事件构成 在Android中,事件主要包含点按.长按.拖拽.滑动等,点按又包含单击和双击,另外还包含单指操作和多指操作.全部这些都构成了Android中的事件响应.总的来说.全部的 ...

  6. 【Android - 自定义View】之View的事件分发机制

    参考资料: View事件分发:http://blog.csdn.net/pi9nc/article/details/9281829 ViewGroup事件分发:http://blog.csdn.net ...

  7. Android View的事件分发

    如果接触android开发时间足够长的话,或多或少都会遇到各种各样事件冲突的问题,要想解决这类问题,对深入理解事件分发机制是很有必要的,接下来几天都会尽自己所能尽可能将这方面讲清楚.  View的事件 ...

  8. Android View 的事件分发原理解析

    作为一名 Android 开发者,每天接触最多的就是 View 了.Android View 虽然不是四大组件,但其并不比四大组件的地位低.而 View 的核心知识点事件分发机制则是不少刚入门同学的拦 ...

  9. Android View体系(五)从源码解析View的事件分发机制

    1.处理点击事件的方法 View的层级 我们知道View的结构是树形的结构,View可以放在ViewGroup中,这个ViewGroup也可以放到另一个ViewGroup中,这样层层的嵌套就组成了Vi ...

随机推荐

  1. 支持 .NET Core 的 Memcached 客户端 EnyimMemcachedCore

    1. 介绍 EnyimMemcachedCore 是一个支持 .NET Core 的 Memcached 客户端,是从 EnyimMemcached 迁移至 .NET Core的,源代码托管在 Git ...

  2. .Net中的AOP系列之构建一个汽车租赁应用

    返回<.Net中的AOP>系列学习总目录 本篇目录 开始一个新项目 没有AOP的生活 变更的代价 使用AOP重构 本系列的源码本人已托管于Coding上:点击查看. 本系列的实验环境:VS ...

  3. JavaScript 字符串实用常操纪要

    JavaScript 字符串用于存储和处理文本.因此在编写 JS 代码之时她总如影随形,在你处理用户的输入数据的时候,在读取或设置 DOM 对象的属性时,在操作 Cookie 时,在转换各种不同 Da ...

  4. 探索ASP.NET MVC5系列之~~~5.缓存篇(页面缓存+二级缓存)

    其实任何资料里面的任何知识点都无所谓,都是不重要的,重要的是学习方法,自行摸索的过程(不妥之处欢迎指正) 汇总:http://www.cnblogs.com/dunitian/p/4822808.ht ...

  5. .NET平台开源项目速览(14)最快的对象映射组件Tiny Mapper

    好久没有写文章,工作甚忙,但每日还是关注.NET领域的开源项目.五一休息,放松了一下之后,今天就给大家介绍一个轻量级的对象映射工具Tiny Mapper:号称是.NET平台最快的对象映射组件.那就一起 ...

  6. zookeeper源码分析之二客户端启动

    ZooKeeper Client Library提供了丰富直观的API供用户程序使用,下面是一些常用的API: create(path, data, flags): 创建一个ZNode, path是其 ...

  7. java单向加密算法小结(2)--MD5哈希算法

    上一篇文章整理了Base64算法的相关知识,严格来说,Base64只能算是一种编码方式而非加密算法,这一篇要说的MD5,其实也不算是加密算法,而是一种哈希算法,即将目标文本转化为固定长度,不可逆的字符 ...

  8. pdo的使用

    PHP 数据对象 (PDO) 扩展为PHP访问数据库定义了一个轻量级的一致接口. PDO 提供了一个数据访问抽象层,这意味着,不管使用哪种数据库,都可以用相同的函数(方法)来查询和获取数据. PDO随 ...

  9. php注册审核

    通过注册审核,判断刚创建的账户是否可以使用. 后台管理员审核通过后,账号可以使用. 通过session 设置只能通过登录入口进入网页. 原理:通过数据库设置账号的一个字段状态,例: isok:1, i ...

  10. .NET应用和AEAI CAS集成详解

    1 概述 数通畅联某综合SOA集成项目的统一身份认证工作,需要第三方系统配合进行单点登录的配置改造,在项目中有需要进行单点登录配置的.NET应用系统,本文专门记录.NET应用和AEAI CAS的集成过 ...