今天在学习Android 4.3的Laucher2的源码,在研究Widget拖拽至Workspace过程中,一直找不着onTouchEvent的处理流程的门道。

上网找了一些资料参考,发现如下总结还是写得比较到位。

(一)

http://www.cnblogs.com/xiaoQLu/archive/2011/03/26/1996344.html

老实说,这两个小东东(onInterceptTouchEvent、onTouchEvent)实在是太麻烦了,很不好懂,我自己看api文档都头晕,在网上找到很多资料,才知道是怎么回事,这里总结一下,记住这个原则就会很清楚了:

1、onInterceptTouchEvent()是用于处理事件(类似于预处理,当然也可以不处理)并改变事件的传递方向,也就是决定是否允许Touch事件继续向下(子控件)传递,一但返回True(代表事件在当前的viewGroup中会被处理),则向下传递之路被截断(所有子控件将没有机会参与Touch事件),同时把事件传递给当前的控件的onTouchEvent()处理;返回false,则把事件交给子控件的onInterceptTouchEvent()

2、onTouchEvent()用于处理事件,返回值决定当前控件是否消费(consume)了这个事件,也就是说在当前控件在处理完Touch事件后,是否还允许Touch事件继续向上(父控件)传递,一但返回True,则父控件不用操心自己来处理Touch事件。返回true,则向上传递给父控件(注:可能你会觉得是否消费了有关系吗,反正我已经针对事件编写了处理代码?答案是有区别!比如ACTION_MOVE或者ACTION_UP发生的前提是一定曾经发生了ACTION_DOWN,如果你没有消费ACTION_DOWN,那么系统会认为ACTION_DOWN没有发生过,所以ACTION_MOVE或者ACTION_UP就不能被捕获。)

(二)

总则:

1.onInterceptTouchEvent中有个Intercept,这是什么意思呢?她叫拦截,你大概知道她作用了吧,她就是你的亲亲小秘书,有什么事,先找秘书,秘书来决定这个事要不要你亲自处理,既然是秘书,是不是每个人都能有呢?当然不是,这个秘书只是viewgroup的,所以只有继承viewgroup的控件才有,那些textview啊button啊什么的,肯定是没有的,为什么?因为他级别不够(继承自view),是不能有秘书滴,注意,如果本级领导决定去的话,那后面的通知就不经过这一级的小秘了,直接发领导。

2.事件的传递顺序,依次发生的是ACTION_DOWN从父控件传向子控件,然后是ACTION_MOVE(可能出现,也可能不出现),最后是ACTION_UP注意是按顺序从父控件传向子控件,手机上ACTION_MOVE和ACTION_UP基本是都会出现的,是因为手机的传感器很敏感,但是模拟器就不一样,如果没有滑动,是不会出现action_MOVE的,所有的事件,ACTION_DOWN、ACTION_MOVE和ACTION_UP都首先发自根控件(布局文件中最外面一层)的onInterceptTouchEvent中,也就是说,每一个控件都首先会收到onInterceptTouchEvent事件(当然你必须有这个能力收到,原因看上面)

3.有小秘了,有事件了,那就该有领导了,onTouchEvent就是领导了,是处理具体的事件的,领导会首先收到小秘发的ACTION_DOWN事件,领导一看,(1)哇,小秘发的来的,赶紧瞅瞅,晚上是不是有什么活动,一看,还真有活动,又可以带小秘一起出去high了,过瘾,告诉小秘(return true),参加晚上的活动,然后上级小秘会接着把活动的具体安排(ACTION_MOVE和ACTION_UP)也发过来,都由这个领导处理(注意,具体活动是上级小秘直接通知下级领导(OnTouchEvent)的,这里不再需要下级小秘过滤了,因为小秘打扮去了,晚上要陪领导活动呢),其他领导呢,只能望洋兴叹了

(2)如果领导看了,发现不是出去happy的事,这心情郁闷的,又不能跟小秘一起了,这尼玛的,还去个毛啊!不去了!!!告诉小秘呗(return false)不处理,然后小秘就会向上级(父控件)汇报,然后就由上级领导来处理。神马?上级领导不处理,靠,那好吧,都交给他们的祖宗(View)去,查看view源码,你会发现,一样的,如果接受事件,就返回true,接着处理ACTION_MOVE和ACTION_UP,没处理,就返回false,到祖宗这一级了,如果都没人处理,那没办法,这个事件就此消失了,准备处理下一个

<?xml version="1.0" encoding="utf-8"?><com.touchstudy.LayoutView1 xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical"    android:layout_width="fill_parent"    android:layout_height="fill_parent" >    <com.touchstudy.LayoutView2        android:orientation="vertical"        android:layout_width="fill_parent"        android:layout_height="fill_parent"        android:gravity="center">       <com.touchstudy.MyTextView             android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:id="@+id/tv"            android:text="AB"            android:textSize="40sp"            android:textStyle="bold"            android:background="#FFFFFF"            android:textColor="#0000FF"/>   </com.touchstudy.LayoutView2></com.touchstudy.LayoutView1>


分析一

就上面布局,分析一下,如果不干扰他们各自的方法的返回值,首先是根控件LayoutView1的秘书1(onInterceptTouchEvent)收到通知(ACTION_down),她在想,啊,要不要通知根控件的领导1(onTouchEvent)呢,恩,算了吧,这么重要的领导1,太忙了,日理万机啊,先往下通知吧,看下面有没有人响应,然后直接发给LayoutView2的秘书2,秘书2想这几天陪领导2太累了,想休息下,不告诉他吧,然后又直接下发MyTextView领导3,领导3级别不够啊,没有小秘啊,咋办,自己收通知呗,然后处理,他想啊想,要不要去呢?真纠结,他给自己两条路:

  1.自己去,return true,好,这一级级的返回结果,都知道他要去了,这就省事了,活动安排(ACTION_MOVE和ACTION_UP)来了,秘书1直接通知秘书2(为什么不直接通知领导3呢,因为他闲领导3太丑,哈哈,开玩笑滴,领导绝对是英俊潇洒,风流倜傥……,是因为不够级别让自己去通知啊),秘书2收到通知,再直接下发给领导3 ,然后领导3就happy去了,所以说啊,这管理是很严滴,一级一级往下分发,你下级的事,上面都知道,要小心喽 ……

  2.自己不去,return false,唉,懒得去,向上级汇报,自己病了,秘书2一听,唉,没办法,这只有通知自家领导2了,叫醒领导2,问他去不去,领导2正好休息好,心情不错,大笑一挥,去,好,向上面汇报,秘书1的通知来了, 有人问这里还要不要秘书2过一遍呢,答案是否,因为秘书2只过滤一遍,就是决定要不要通知领导(看来领导必须跟小秘搞好关系啊,不然关键时刻给你来一下子,你就完蛋了),一旦领导收到通知,秘书就不管了,秘书干嘛去了,秘书打扮去了啊,晚上要陪领导啊,还不打扮的漂亮点,这不是给领导丢人么?

请看图

分析二

如果把LayoutView2的onInterceptTouchEVent返回true会有神马情况?

前面分析一样,这里只分析一点,既然领导2的小秘通知了领导,领导也决定去,那就没你领导3什么事了,领导3什么都收不到,网上说会收到ACTION_CANCEL,我这里没测到,我觉得应该是什么都收不到,因为ACTION_CACEL前提条件是你收到了ACTION_DOWN,并且在之后的时间,上级领导突然又要插手这件事了,(在onInterceptTouchEVent的ACTION_MOVE/UP的时候返回true,截断)这个时候,就会通知下级取消这次事件,什么时候会遇到这种情况呢,在listview滑动的时候,按住屏幕往上滑动的时候,如果用程序模拟的话,就是在上一级控件的onInterceptTouchEVent中ACTION_DOWN的时候返回false(向下级传递),然后在ACTION_UP的时候返回tue,截断后续消息,这种情况会收到ACTION_CANCEL

好吧,到此结束,该去洗澡了,关于具体的屏幕点击事件的处理机制以及流程在以后的博文中整理!谢谢!

最后附上源码地址 : http://files.cnblogs.com/xiaoQLu/MotionEventTest.rar

(三)

声明:原创作品,转载请说明出处,来自 http://www.cnblogs.com/xiaoQLu/archive/2013/04/02/2994030.html

ACTION_CANCEL事件,官方文档讲的是当前手势被释放,你将不会接收到其他的事件,应该向ACTION_UP一样对待它。

那到底什么情况会触发这个事件呢?当 当前控件(子控件,儿子)收到前驱事件(ACTION_MOVE或者ACTION_MOVE)后,它的父控件(老爸)突然插手,截断事件的传递,这时,当前控件就会收到ACTION_CANCEL,收到此事件后,不管子控件此时返回true或者false,都认为这一个动作已完成,不会再回传到父控件的OnTouchEvent中处理,同时后续事件,会通过dispatchEvent方法直接传送到父控件这里来处理。这和之前的结论有点相悖,之前说过如果子控件的OnTouchEvent返回false,表明事件未被处理,是回传到父控件去处理的,这里纠正一下,只有ACTION_DOWN事件才可以被回传,ACTION_MOVE和ACTION_UP事件会跟随ACTION_DOWN事件,即ACTION_DOWN是哪个控件处理的,后续事件都传递到这里,不会上抛到父控件,ACTION_CANCEL也不能回传。还有一点,触摸区域的范围问题,如果触摸区域在子控件内,同时父控件没有截断事件传递,刚不管子控件是否拦截此事件,都会传递到子控件的OnTouchEvent中处理,可以看成一种责任吧,因为我点的就是你,你父亲没有拦截,说明他不想处理,那到你这里了,不管你拦不拦截,都得你来处理。

结论:ACTION_CANCEL事件是收到前驱事件后,后续事件被父控件拦截的情况下产生,onTouchEvent的事件回传到父控件只会发生在ACTION_DOWN事件中

实例说明:在下面的实例中,LinearView1是父控件,LayoutView2是子控件,点击区域是LayoutView2,可以看到父控件的onInterceptTouchEvent方法在ACTION_DOWN的时候没有拦截事件,但是在ACTION_MOVE的时候突然插手,拦截掉事件,这时候,子控件就会收到ACTION_CANCEL。

public class LayoutView1 extends LinearLayout {    private final String TAG = "LayoutView1";    public LayoutView1(Context context, AttributeSet attrs) {        super(context, attrs);        Log.d(TAG, TAG);    }    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        int action = ev.getAction();        switch (action) {        case MotionEvent.ACTION_DOWN:            Log.d(TAG, "1:onInterceptTouchEvent action:ACTION_DOWN");            //return true;            break;        case MotionEvent.ACTION_MOVE:            Log.d(TAG, "1:onInterceptTouchEvent action:ACTION_MOVE");            return true;            //break;        case MotionEvent.ACTION_UP:            Log.d(TAG, "1:onInterceptTouchEvent action:ACTION_UP");            //return true;            break;        case MotionEvent.ACTION_CANCEL:            Log.d(TAG, "1:onInterceptTouchEvent action:ACTION_CANCEL");            break;        }        return false;    }    @Override    public boolean onTouchEvent(MotionEvent ev) {        int action = ev.getAction();        switch (action) {        case MotionEvent.ACTION_DOWN:            Log.d(TAG, "1:onTouchEvent action:ACTION_DOWN");            break;        case MotionEvent.ACTION_MOVE:            Log.d(TAG, "1:onTouchEvent action:ACTION_MOVE");            break;        case MotionEvent.ACTION_UP:            Log.d(TAG, "1:onTouchEvent action:ACTION_UP");            break;        case MotionEvent.ACTION_CANCEL:            Log.d(TAG, "1:onTouchEvent action:ACTION_CANCEL");            break;        }        return true;    }}


public class LayoutView2 extends LinearLayout {    private final String TAG = "LayoutView2";       public LayoutView2(Context context, AttributeSet attrs) {       super(context, attrs);       Log.d(TAG,TAG);    }    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {       int action = ev.getAction();       switch(action){       case MotionEvent.ACTION_DOWN:           Log.d(TAG,"2:onInterceptTouchEvent action:ACTION_DOWN");           break;           //return true;       case MotionEvent.ACTION_MOVE:           Log.d(TAG,"2:onInterceptTouchEvent action:ACTION_MOVE");           break;           //return true;       case MotionEvent.ACTION_UP:           Log.d(TAG,"2:onInterceptTouchEvent action:ACTION_UP");           break;       case MotionEvent.ACTION_CANCEL:           Log.d(TAG,"2:onInterceptTouchEvent action:ACTION_CANCEL");           break;       }         return false;}     @Override    public boolean onTouchEvent(MotionEvent ev) {       int action = ev.getAction();       switch(action){       case MotionEvent.ACTION_DOWN:           Log.d(TAG,"2:onTouchEvent action:ACTION_DOWN");           //return false;           break;       case MotionEvent.ACTION_MOVE:           Log.d(TAG,"2:onTouchEvent action:ACTION_MOVE");           return false;           //break;       case MotionEvent.ACTION_UP:           Log.d(TAG,"2:onTouchEvent action:ACTION_UP");           break;       case MotionEvent.ACTION_CANCEL:           Log.d(TAG,"2:onTouchEvent action:ACTION_CANCEL");           break;       }            return true;    } }


有不明白的下载源码运行看看结果

源码下载:http://files.cnblogs.com/xiaoQLu/MotionEventTest.rar

更多相关文章

  1. Android滑动组件----RecyclerView并且实现点击事件(2)
  2. android 仿iphone主题之主菜单
  3. 移动架构39_RxAndroid二(变换调用:map、flatMap、lift、compose)
  4. android 软件盘相关
  5. android 自动查找控件id
  6. android —— 自定义控件 利用 ViewPage 实现滑动屏
  7. 屏幕触点
  8. Android(安卓)Banner轮播控件
  9. MeasureSpec之详细分析

随机推荐

  1. Android存储Json到本地,和读取本地Json
  2. Android获取窗体信息的Util方法
  3. Android仿“知乎”隐藏标题栏、回答详情
  4. android phoneGap 静态页面中简单的数据
  5. android 8 sdk源码编译时报错Try increas
  6. 新思路_Android同时显示多个跑马灯
  7. 转载:Android uses-permission大全
  8. 十二个android编程技巧
  9. Android 官方文档 Google Services
  10. Android SDK 版本的简称