现在的位置: 首页 > 操作系统 > 正文

简单例子了解View的事件分发

2020年02月12日 操作系统 ⁄ 共 18819字 ⁄ 字号 评论关闭

什么是事件分发

我们在写自定义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 dispatchTouchEvent008-30 07:47:13.741 7574-7574/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent008-30 07:47:13.741 7574-7574/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent008-30 07:47:13.741 7574-7574/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent008-30 07:47:13.741 7574-7574/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent008-30 07:47:13.741 7574-7574/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent008-30 07:47:13.741 7574-7574/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onTouchEvent008-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 dispatchTouchEvent008-30 08:53:31.126 23810-23810/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent008-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 dispatchTouchEvent008-30 08:59:27.702 23810-23810/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent008-30 08:59:27.702 23810-23810/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent008-30 08:59:27.702 23810-23810/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent008-30 08:59:27.702 23810-23810/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onTouchEvent008-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 dispatchTouchEvent008-30 09:10:27.550 23810-23810/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent008-30 09:10:27.550 23810-23810/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent008-30 09:10:27.550 23810-23810/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent008-30 09:10:27.550 23810-23810/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent008-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 dispatchTouchEvent008-30 09:15:28.998 23810-23810/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent008-30 09:15:28.998 23810-23810/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent008-30 09:15:28.998 23810-23810/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent008-30 09:15:28.998 23810-23810/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent008-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 dispatchTouchEvent008-30 10:40:38.209 32359-32359/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent008-30 10:40:38.209 32359-32359/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent008-30 10:40:38.209 32359-32359/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent008-30 10:40:38.209 32359-32359/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent008-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 dispatchTouchEvent008-30 13:07:04.834 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent008-30 13:07:04.834 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent008-30 13:07:04.834 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent008-30 13:07:04.834 16083-16083/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent008-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 onTouchEvent008-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 dispatchTouchEvent108-30 13:07:04.931 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent108-30 13:07:04.931 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent108-30 13:07:04.931 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent108-30 13:07:04.931 16083-16083/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent108-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 onTouchEvent108-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 dispatchTouchEvent008-31 07:18:40.791 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent008-31 07:18:40.791 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent008-31 07:18:40.791 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent008-31 07:18:40.791 16083-16083/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent008-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 onTouchEvent008-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 onTouchEvent008-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 dispatchTouchEvent008-31 08:09:13.076 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent008-31 08:09:13.076 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent008-31 08:09:13.076 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent008-31 08:09:13.076 16083-16083/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent008-31 08:09:13.077 16083-16083/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent008-31 08:09:13.148 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent208-31 08:09:13.148 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent208-31 08:09:13.148 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent208-31 08:09:13.148 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent208-31 08:09:13.149 16083-16083/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent208-31 08:09:13.149 16083-16083/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent208-31 08:09:13.166 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent208-31 08:09:13.166 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent208-31 08:09:13.166 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent208-31 08:09:13.166 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent208-31 08:09:13.166 16083-16083/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent208-31 08:09:13.166 16083-16083/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent208-31 08:09:13.180 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent208-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent208-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent208-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent208-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent208-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewTest: View onTouchEvent208-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA dispatchTouchEvent108-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewGroupA: ViewGroupA onInterceptTouchEvent108-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB dispatchTouchEvent108-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewGroupB: ViewGroupB onInterceptTouchEvent108-31 08:09:13.181 16083-16083/com.byhieg.viewdipatch E/ViewTest: View dispatchTouchEvent108-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分发的过程,这个就看事件啦。

本文永久更新链接地址:http://www.xuebuyuan.com/Linux/2017-01/139406.htm

以上就上有关简单例子了解View的事件分发的相关介绍,要了解更多View的事件分发,简单例子了解View的事件分发,编程,Linux编程,Linux Shell,Android,Android教程,JAVA,C语言,Python,HTML5内容请登录学步园。

抱歉!评论已关闭.