简单例子了解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分发的过程,这个就看事件啦。
简单例子了解View的事件分发的更多相关文章
- 从源码的角度解析View的事件分发
有好多朋友问过我各种问题,比如:onTouch和onTouchEvent有什么区别,又该如何使用?为什么给ListView引入了一个滑动菜单的功能,ListView就不能滚动了?为什么图片轮播器里的图 ...
- Android View的事件分发机制和滑动冲突解决方案
这篇文章会先讲Android中View的事件分发机制,然后再介绍Android滑动冲突的形成原因并给出解决方案.因水平有限,讲的不会太过深入,只希望各位看了之后对事件分发机制的流程有个大概的概念,并且 ...
- Android事件分发机制详解(1)----探究View的事件分发
探究View的事件分发 在Activity中,只有一个按钮,注册一个点击事件 [java] view plaincopy button.setOnClickListener(new OnClickLi ...
- Android View的事件分发机制
准备了一阵子,一直想写一篇事件分发的文章总结一下.这个知识点实在是太重要了. 一个应用的布局是丰富的,有TextView,ImageView,Button等.这些子View的外层还有ViewGroup ...
- View的事件分发机制解析
引言 Android事件构成 在Android中,事件主要包含点按.长按.拖拽.滑动等,点按又包含单击和双击,另外还包含单指操作和多指操作.全部这些都构成了Android中的事件响应.总的来说.全部的 ...
- 【Android - 自定义View】之View的事件分发机制
参考资料: View事件分发:http://blog.csdn.net/pi9nc/article/details/9281829 ViewGroup事件分发:http://blog.csdn.net ...
- Android View的事件分发
如果接触android开发时间足够长的话,或多或少都会遇到各种各样事件冲突的问题,要想解决这类问题,对深入理解事件分发机制是很有必要的,接下来几天都会尽自己所能尽可能将这方面讲清楚. View的事件 ...
- Android View 的事件分发原理解析
作为一名 Android 开发者,每天接触最多的就是 View 了.Android View 虽然不是四大组件,但其并不比四大组件的地位低.而 View 的核心知识点事件分发机制则是不少刚入门同学的拦 ...
- Android View体系(五)从源码解析View的事件分发机制
1.处理点击事件的方法 View的层级 我们知道View的结构是树形的结构,View可以放在ViewGroup中,这个ViewGroup也可以放到另一个ViewGroup中,这样层层的嵌套就组成了Vi ...
随机推荐
- 旺财速啃H5框架之Bootstrap(四)
上一篇<<旺财速啃H5框架之Bootstrap(三)>>已经把导航做了,接下来搭建内容框架.... 对于不规整的网页,要做成自适应就有点玩大了.... 例如下面这种版式的页面. ...
- FFmpeg学习6:视音频同步
在上一篇文章中,视频和音频是各自独立播放的,并不同步.本文主要描述了如何以音频的播放时长为基准,将视频同步到音频上以实现视音频的同步播放的.主要有以下几个方面的内容 视音频同步的简单介绍 DTS 和 ...
- 为什么 NaN 不等于自身?
NaN 即Not a Number , 不是一个数字, 那么NaN到底是什么呢? 话说在JavaScript中,有6大数据类型,分别包括string,number,boolean,undefined, ...
- Asp.net Boilerplate源码中NotNullAttribute的用处
看Asp.net Boilerplate 1.1.3.0源码时发现有一个NotNullAttribute的定义和27处的引用,就是不知道它的作用,当然顾名思义是可以的,就是不知道它是怎么判断的,在哪里 ...
- js报错: Uncaught RangeError: Invalid string length
在ajax请求后得到的json数据,遍历的时候chrome控制台报这个错误:Uncaught RangeError: Invalid string length,在stackoverflow查找答案时 ...
- 用django创建一个项目
首先你得安装好python和django,然后配置好环境变量,安装python就不说了,从配置环境变量开始 1.配置环境变量 在我的电脑处点击右键,或者打开 控制面板\系统和安全\系统 -> 左 ...
- 使用Microsoft的IoC框架:Unity来对.NET应用进行解耦
1.IoC/DI简介 IoC 即 Inversion of Control,DI 即 Dependency Injection,前一个中文含义为控制反转,后一个译为依赖注入,可以理解成一种编程模式,详 ...
- 解决:SharePoint当中的STP网站列表模板没有办法导出到其它语言环境中使用
首在在你的英文版本上,导出列表或是网站的模板,这个文件可能是这样滴:template.stp 把这个文件 template.stp 命名为 template.cab 解压 这个 *.cab 文件 在解 ...
- 使用git进行源代码管理
git是一款非常流行的分布式版本控制系统,使用Local Repository追踪代码的修改,通过Push和Pull操作,将代码changes提交到Remote Repository,或从Remote ...
- 解决maven下载jar慢的问题(如何更换Maven下载源)
修改 配置文件 maven 安装 路径 F:\apache-maven-3.3.9\conf 修改 settings.xml 在 <mirrors> <!-- mirror | Sp ...