初识Android点击事件
1. 用户对屏幕的操作的事件可以划分为3种最基础的事件:ACTION_DOWN、ACTION_MOVE、ACTION_UP。
2. 用户的ACTION_DOWN到ACTION_UP的操作可以称为一个事件序列,主要有以下两种组成:
一: ACTION_DOWN->ACTION_UP
二 :ACTION_DOWN->许多个ACTION_MOVE>ACTION_UP
3. Android 的事件分发机制大体可以分为三部分:事件生产、事件分发 、事件消费。事件的生产是由用户点击屏幕产生,我们这次着重分析事件的分发和消费,因为事件分发和处理联系的过于紧密,这篇文章将把事件的分发和消费放在一起分析。
在Activity上的事件分发和Activity PhoneView DecorView ViewGroup view 密不可分,它们的关系如下图表示:
View树的视图结构可以用下图概括:
事件分发主要有以下几个方法:
1. dispatchTouchEvent(event):用于进行点击事件的分发
2. onInterceptTouchEvent(event):用于进行点击事件的拦截 注:只有 ViewGroup才有
3. onTouchEvent(event):用于处理点击事件
事件的分发到处理过程大致可以用下图简单表示:
总结一下:
1. 所有的dispatchTouchEvent当为true的时候则直接消费(),为false的时候则传递给上一层的onTouchEvent方法,Activity例外false它也会消费掉,当为super方法的时候则向下一层的dispatchTouchEvent传递,注意:ViewGroup由于有onInterceptTouchEvent,他会先传递到onInterceptTouchEvent!!!
2.onInterceptTouchEvent(ViewGroup特有)为super/false时候可以向下一层传递到dispatchTouchEvent,当为true的时候,则表示要自己消化,则会传递到自身的onTouchEvent方法。
3.所有的onTouchEvent方法,当返回为true的时候表示自己要消费(结束),当为super/false的话则传递给上一层的onTouchEvent方法。
对ACTION_MOVE 和ACTION_UP事件的处理
对这两种事件的处理可以总结为下面这些内容ACTION_DOWN事件在哪个控件消费了(return true), 那么ACTION_MOVE和ACTION_UP就会从上往下(通过dispatchTouchEvent)做事件分发往下传,就只会传到这个控件,不会继续往下传,通俗理解就是ACTION_DOWN哪里结束,ACTION_MOVE 和ACTION_UP就传递到哪里结束。
View事件方法执行顺序
先看结论:onTouchListener > onTouchEvent > onLongClickListener > onClickListener
1. 从dispatchTouchEvent源代码中我们可以看到View会先判断是否设置了OnTouchListener,如果设置了OnTouchListener并且onTouch方法返回了true,那么onTouchEvent不会被调用。当没有设置OnTouchListener或者设置了OnTouchListener但是onTouch方法返回false则会调用View自己的onTouchEvent方法。
2.默认点击的时间大于了500ms算是LongClick,onTouchEvent中的ACTION_UP事件处理,也会先检查有没有长按事件的监听,再执行onLongClick
3. 在View的onTouchEvent中的case ACTION_UP中有对onClickListener的处理,感兴趣的小伙伴可以查看具体源码。
简单总结一下 onLongClickListener 和 onClickListener的处理过程:当ACTION_DOWN到达一个view后,他会想通过handler post一个500ms消息(我称之为长按消息)给主线程,这个消息里面封装着LongClickListener的处理任务,如果在500ms之内收到了ACTION_UP事件,则首先会移除掉主线程消息队列中的长按消息,然后去执行onClickListener对Click的执行逻辑,因为主线程中长按消息已经被移除,所以500ms后不会执行LongClick的任务。
ViewGroup分发中有一种特殊情况,那就是FLAG_ DISALLOW_ INTERCEPT 标记位,这个标记位是通过requestDisallowInterceptTouchEvent 方法来设置的,一般用于子 View中。FLAG_DISALLOW_INTERCEPT 一旦设置后,ViewGroup 将无法拦截除了 ACTION_ DOWN以外的其他点击事件。为什么说是除了ACTION_ DOWN以外的其他事 件呢?这是因为ViewGroup在分发事件时,如果是ACTION DOWN就会重置 FLAG_ DISALLOW_ INTERCEPT这个标记位,将导致子View中设置的这个标记位无效。 因此,当面对ACTION_DOWN事件时,ViewGroup总是会调用自己的onInterceptTouchEvent方法来询问自己是否要拦截事件,这一点从源码中也可以看出来。
注:有个TouchTarget类,观察TouchTarget的字段可以发现,TouchTarget里面存有消费事件的View,它的结构是一个链表结构。
TouchTarget.java
// 观察TouchTarget的字段可以发现吗,TouchTarget里面存有消费事件的View,
//pointerIdBits(和多点触碰有关),还有指向下一个节点的引用
private static final class TouchTarget {
private static final int MAX_RECYCLED = 32;
private static final Object sRecycleLock = new Object[0];
private static TouchTarget sRecycleBin;
private static int sRecycledCount;
public static final int ALL_POINTER_IDS = -1; // all ones
// The touched child view.
@UnsupportedAppUsage
public View child;
// The combined bit mask of pointer ids for all pointers captured by the target.
public int pointerIdBits;
// The next target in the target list.
public TouchTarget next;
写这个博客的时候参考很多优秀的博客和书籍,在这里向各位作者表示感谢!