android基本功 - touch事件传递机制总结

android的touch事件传递机制是做自定义控件,分析一切与touch有关问题的理论基础,尽管google在support包里已经有了ViewDragHelper这样关于touch的比较完善的辅助类,但是了解touch事件分发的原理还是非常有必要的。本文将对android的touch事件机制做一个比较完整的阐述和总结,并结合一个实际例子,加深理解。

如果你是一个老司机,请直接飘过。如果你对这些还有点懵逼,希望看了这篇文章后,能够对touch传递机制有一个比较清晰的认识。

在阅读这篇文章之前,你肯定已经知道了,和touch事件传递有关的,主要有下面几个方法:

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev{
       
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
       
        return super.onTouchEvent(ev);
    }

首先,我们要知道,这几个方法,存在于哪些地方。这里列出一个表格:

位置 dispatchTouchEvent onInterceptTouchEvent onTouchEvent
Activity
ViewGroup
View

既然这几个方法和touch机制有关,那么我们首先要搞清楚下面两个问题:

1,这几个方法的含义分别是什么?
2,这几个方法的返回值代表什么意思?

在回答这几个问题之前,我们先看一下google官方对这几个方法的说明:

dispatchTouchEvent:

Activity中的官方注释是这样的:

 /**
   * Called to process touch screen events.  You can override this to
   * intercept all touch screen events before they are dispatched to the
   * window.  Be sure to call this implementation for touch screen events
   * that should be handled normally.
   *
   * @param ev The touch screen event.
   *
   * @return boolean Return true if this event was consumed.
   */

大概意思是:
这个方法是用来派发touch事件的,你可以重写这个方法,从而在事件往窗口派发之前,做你想做的事情。但是你如果你想事件正常地按套路传递下去,记得调用这个默认实现,也就是super.dispatchTouchEvent。返回值为true时,表示消费了事件。

ViewGroup中关于dispatchTouchEvent没有注释,我们看看View中的:

 /**
     * Pass the touch screen motion event down to the target view, or this
     * view if it is the target.
     *
     * @param event The motion event to be dispatched.
     * @return True if the event was handled by the view, false otherwise.
     */

意思是,这个方法是用来判断把事件传递给目标view还是自己。返回值为true时表示是否消费了这次事件?

怎么样,看了官方注释之后是不是豁然开朗了?

没有!!!,还是一脸懵逼。

怎么办?问题还是要解决啊!
RTFSC,古人诚不欺我。

但是我今天不打算在这里贴一大堆代码然后一行一行地分析讲解。因为这件事应该自己去做,不要指望别人帮你。

关于android的touch机制,总体上总结如下:

  • 一次完整的手势始于ACTION_DOWN(第一个手指按下),终于ACTION_UP(最后一个手指离开);
  • touch事件的传递顺序是Activity>ViewGroup>....>View;
  • dispatchTouchEvent是事件派发的直接通道,Activity的dispatchTouchEvent内部会调用ViewGroup(Dectorview)的dispatchTouchEvent方法,...,ViewGroup又调用View的dispatchTouchEvent方法;
  • onInterceptTouchEvent(如果有)和onTouchEvent是在当前对象的dispatchTouchEvent被调用的;
  • dispatchTouchEvent的返回值用来告诉它的上级,自己是否对此事件感兴趣。从ViewGroup开始,任何对象的dispatchTouchEvent如果在ACTION_DOWN时返回false,在本次手势结束前,后续任何事件它的上级都不会再派发给它;
  • 在不重写dispatchTouchEvent的情况下,View的dispatchTouchEvent返回值取决于它的touchListener的onTouch方法和自身的onTouchEvent方法的返回值,有任一方法返回true,则结果为true,简单地可以表达如下:
public boolean View.dispatchTouchEvent(MotionEvent ev) {

    if(this.touchListener.onTouch(ev)){
       return true;
    }
    if(this.onTouchEvent(ev)){
       return true;
    }
    return false;
}
  • 在不重写dispatchTouchEvent的情况下,ViewGroup的dispatchTouchEvent返回值除了和它的touchListener的onTouch方法以及自身的onTouchEvent方法的返回值有关外,还和其child的dispatchTouchEvent返回值有关,有任一处返回true,则结果为true;
  • ViewGroup的dispatchTouchEvent进行事件派发之前,首先会调用自己的onInterceptTouchEvent来决定是把事件交给自己处理还是往下派发给子View,onInterceptTouchEvent返回true表示拦截事件自己处理。可以简单地将ViewGroup的dispatchTouchEvent流程描述如下:
public boolean ViewGroup.dispatchTouchEvent(MotionEvent ev) {
    if(!onInterceptTouchEvent(ev)){
        for(View child : children){
            if(child.dispatchTouchEvent(ev)){
              return true;
            }
        }
    }
    return View.dispatchTouchEvent(ev);
}
  • 从ViewGroup开始,如果一个对象的dispatchTouchEvent在ACTION_DOWN时返回true,不管其他事件时返回true还是false,都将收到事件。不同的是,返回true时事件被消费,不能再被其他任何对象处理,返回false时事件还能被Activity的onTouchEvent接收处理,即Activity的dispatchTouchEvent处理流程是这样的:
public boolean Activity.dispatchTouchEvent(MotionEvent ev) {

    if(ViewGroup.dispatchTouchEvent(ev)){
        return true;
    }
    return this.onTouchEvent(ev);
}

关于ViewGroup的onInterceptTouchEvent,另外说明几点:

  1. 如果ACTION_DOWN时onInterceptTouchEvent返回true,同时ViewGroup 的onTouchEvent返回true,即消费了down事件,那么在这次手势过程中,后面的事件不会再经过onInterceptTouchEvent,而是直接交给ViewGroup自己处理;
  2. 子View可以调用父View的requestDisallowInterceptTouchEvent方法告诉父View:这事你别管,我来处理。禁用拦截只在当前手势过程中有效;
  3. 在一次手势过程中,如果前面的事件父View没有拦截,这次事件父View拦截了,那么子View会马上收到一个ACTION_CANCEL事件。

古人云,实践出真知,说了这么多,如果看了文章之后还不清楚的,建议用下面的例子实践一下。

实现如下图所示效果:

要求:
1,情形一,中间绿色view设置点击事件,可滑动
2,情形二,中间绿色view不设置点击事件,可滑动
3,操作中间绿色view之外的区域可正常操作ViewPager

demo源码已经放到github

类似探探左右滑动的控件:
StackCardsView源码,各位有兴趣可以看看,欢迎fork,star。
附效果图:

仿探探滑动控件

参考资料:Mastering the Android Touch System

转载请说明出处:http://www.jianshu.com/p/328ab7c84ca0

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容