Android触摸滑动全解(二)——ViewGroup中触摸事件详解

Android触摸滑动全解(二)——ViewGroup中触摸事件详解

VieGroup触摸事件概述

ViewGroup继承自View,很多方法都和View中一样,不同的是它重载了dispatchTouchEvent()方法,并且它多了一个自身的方法onInterceptTouchEvent()

ViewGroup触摸事件调用原则

  • dispatchTouchEvent()中,会对子View进行递归,将dispatchTouchEvent()传递给它的每一个子View,如果某一个子View在ACTION_DOWN的时候没有消费或者拦截事件,那么这个子View后续也将无法收到ACTIION_MOVEACTION_UP等事件。

  • ViewGroup可以在onInterceptTouchEvent()中拦截事件,onInterceptTouchEvent()在之后执行,如果返回true,则会执行自身的onTouchEvent()方法,但后续onInterceptTouchEvent()onTouchEvent()将不会调用。

  • ViewGroup如果onInterceptTouchEvent()返回false,那么ViewGroup后续是否继续调用dispatchTouchEvent()取决于子View是否消费触摸事件。

  • ViewGroup如果返回在dispatchTouchEvent()中返回false,那么后续所有的触摸操作都不会再执行!

  • ViewGroup在dispatchTouchEvent()return truereturn super.dispatchTouchEvent(ev)会导致触摸结果不一样!

ViewGroup拦截触摸事件示例

自定义一个ViewGroup继承LinearLayout,展示ViewGroup对触摸事件的拦截。子View默认消费此事件。

1、正常状态(子View消费触摸事件)

此时:
ViewGroup:dispatchTouchEvent()super.dispatchTouchEvent(ev)onInterceptTouchEvent()falseOnTouchEvent()false
子View:dispatchTouchEvent()返回trueOnTouchEvent()返回true

Log打印:

zw: LinearLayout dispatchTouchEvent down
    LinearLayout onInterceptTouchEvent down
    View dispatchTouchEvent down
    View onTouchEvent down

zw: LinearLayout dispatchTouchEvent move
    LinearLayout onInterceptTouchEvent move
    View dispatchTouchEvent move
    View onTouchEvent move

zw: LinearLayout dispatchTouchEvent up
    LinearLayout onInterceptTouchEvent up
    View dispatchTouchEvent up
    View onTouchEvent up

可以看见,子View将事件消费掉了,因此ViewGroup没有调用OnTouchEvent()方法。

调用顺序:
ACTION_DOWN:
ViewGroup.dispatchTouchEvent(DOWN) > ViewGroup.onInterceptTouchEvent(DOWN) > View.dispatchTouchEvent(DOWN) > View.OnTouchEvent(DOWN) >
ACTION_MOVE:
ViewGroup.dispatchTouchEvent(MOVE) > ViewGroup.onInterceptTouchEvent(MOVE) > View.dispatchTouchEvent(MOVE) > View.OnTouchEvent(MOVE) >
ACTION_UP:
ViewGroup.dispatchTouchEvent(UP) > ViewGroup.onInterceptTouchEvent(UP) > View.dispatchTouchEvent(UP) > View.OnTouchEvent(UP) > 结束

2、正常状态(子View不消费触摸事件)

此时:
ViewGroup:dispatchTouchEvent()super.dispatchTouchEvent(ev)onInterceptTouchEvent()falseOnTouchEvent()false
子View:dispatchTouchEvent()返回falseOnTouchEvent()返回false

Log打印:

zw: LinearLayout dispatchTouchEvent down
    LinearLayout onInterceptTouchEvent down
    View dispatchTouchEvent down
    View onTouchEvent down
    LinearLayout onTouchEvent down

可以看见,子View没有将事件消费掉,因此ViewGroup调用OnTouchEvent()方法。

调用顺序:
ACTION_DOWN:
ViewGroup.dispatchTouchEvent(DOWN) > ViewGroup.onInterceptTouchEvent(DOWN) > View.dispatchTouchEvent(DOWN) > View.OnTouchEvent(DOWN) > ViewGroup.onTouchEvent(DOWN) > 结束

3、ViewGroup拦截但不消费触摸事件(子View消费触摸事件)

此时:
ViewGroup:dispatchTouchEvent()super.dispatchTouchEvent(ev)onInterceptTouchEvent()trueOnTouchEvent()false
子View:dispatchTouchEvent()返回trueOnTouchEvent()返回true

Log打印:

zw: LinearLayout dispatchTouchEvent down
    LinearLayout onInterceptTouchEvent down
    LinearLayout onTouchEvent down

可以看见,子View没有将事件消费掉,因为ViewGroup的onInterceptTouchEvent()返回了true,拦截了此次事件,但是它本身也没有消费,因此,ACTION_DOWN后面没有再继续执行了。

调用顺序:
ACTION_DOWN:
ViewGroup.dispatchTouchEvent(DOWN) > ViewGroup.onInterceptTouchEvent(DOWN) > ViewGroup.onTouchEvent(DOWN) > 结束

4、ViewGroup拦截并消费触摸事件(子View消费触摸事件)

此时:
ViewGroup:dispatchTouchEvent()super.dispatchTouchEvent(ev)onInterceptTouchEvent()trueOnTouchEvent()true
子View:dispatchTouchEvent()返回trueOnTouchEvent()返回true

Log打印:

zw: LinearLayout dispatchTouchEvent down
    LinearLayout onInterceptTouchEvent down
    LinearLayout onTouchEvent down

zw: LinearLayout dispatchTouchEvent move
    LinearLayout onTouchEvent move

zw: LinearLayout dispatchTouchEvent up
    LinearLayout onTouchEvent up

可以看见,子View没有将事件消费掉,因为ViewGroup的onInterceptTouchEvent()返回了true,拦截了此次事件,但是它本身消费了此次事件,因此,只有ViewGroup自身在消费本次触摸事件。

调用顺序:
ACTION_DOWN:
ViewGroup.dispatchTouchEvent(DOWN) > ViewGroup.onInterceptTouchEvent(DOWN) > ViewGroup.onTouchEvent(DOWN) >
ACTION_MOVE:
ViewGroup.dispatchTouchEvent(MOVE) > ViewGroup.onTouchEvent(MOVE) >
ACTION_UP:
ViewGroup.dispatchTouchEvent(UP) > ViewGroup.onTouchEvent(UP) >

5、ViewGroup没拦截但消费触摸事件(子View消费触摸事件)

此时:
ViewGroup:dispatchTouchEvent()super.dispatchTouchEvent(ev)onInterceptTouchEvent()falseOnTouchEvent()true
子View:dispatchTouchEvent()返回trueOnTouchEvent()返回true

Log打印:

zw: LinearLayout dispatchTouchEvent down
    LinearLayout onInterceptTouchEvent down
    View dispatchTouchEvent down
    View onTouchEvent down

zw: LinearLayout dispatchTouchEvent move
    LinearLayout onInterceptTouchEvent move
    View dispatchTouchEvent move
    View onTouchEvent move

zw: LinearLayout dispatchTouchEvent up
    LinearLayout onInterceptTouchEvent up
    View dispatchTouchEvent up
    View onTouchEvent up

可以看见,如果ViewGroup的onInterceptTouchEvent()没有返回truedispatchTouchEvent()返回true,那么即使OnTouchEvent()返回的是true,也不会调用OnTouchEvent()方法,需要根据子View的消费情况来判断!

调用顺序:
ACTION_DOWN:
ViewGroup.dispatchTouchEvent(DOWN) > ViewGroup.onInterceptTouchEvent(DOWN) > View.dispatchTouchEvent(DOWN) > View.onTouchEvent(DOWN) >
ACTION_MOVE:
ViewGroup.dispatchTouchEvent(MOVE) > ViewGroup.onInterceptTouchEvent(MOVE) > View.dispatchTouchEvent(MOVE) > View.onTouchEvent(MOVE) >
ACTION_UP:
ViewGroup.dispatchTouchEvent(UP) >ViewGroup.onInterceptTouchEvent(UP) > View.dispatchTouchEvent(UP) > View.onTouchEvent(UP) >

子View强制消费触摸事件的方法

当ViewGroup在onInterceptTouchEvent()返回true消费事件后,子View则无法消费此触摸事件,此时,子View如果一定要消费此触摸事件的话,可以使用getParent().requestDisallowInterceptTouchEvent(true)来让ViewGroup不拦截本次事件。
但是此方法需要满足一个条件,就是ViewGroup在onInterceptTouchEvent()的ACTION_DOWN时不要拦截,如果拦截了,那么此方法失效。

正常情况

此时:
ViewGroup:dispatchTouchEvent()trueonInterceptTouchEvent()在ACITON_DOWN时为false,其余返回trueOnTouchEvent()true
子View:dispatchTouchEvent()返回trueOnTouchEvent()返回true

Log打印:

zw: LinearLayout dispatchTouchEvent down
    LinearLayout onInterceptTouchEvent down
    View dispatchTouchEvent down
    View onTouchEvent down

zw: LinearLayout dispatchTouchEvent move
    LinearLayout onInterceptTouchEvent move
    LinearLayout dispatchTouchEvent move
    LinearLayout onTouchEvent move

zw: LinearLayout dispatchTouchEvent up
    LinearLayout onTouchEvent up

可以看见,子View只有dispatchTouchEvent(DOWN)onTouchEvent(DOWN)被调用了,其余的都被拦截了。(这就是ViewGroup的onInterceptTouchEvent()必须在ACTION_DOWN时返回false的原因,不然子View根本没有调用的机会)

调用顺序:
ACTION_DOWN:
ViewGroup.dispatchTouchEvent(DOWN) > ViewGroup.onInterceptTouchEvent(DOWN) > View.dispatchTouchEvent(DOWN) > View.onTouchEvent(DOWN)
ACTION_MOVE:
ViewGroup.dispatchTouchEvent(MOVE) > ViewGroup.onInterceptTouchEvent(MOVE) > ViewGroup.dispatchTouchEvent(MOVE) > ViewGroup.onTouchEvent(MOVE)
ACTION_UP:
ViewGroup.dispatchTouchEvent(UP) > ViewGroup.onTouchEvent(UP) > 结束

子View调用代码的情况

基本和上面一样,只是子View在onTouchEvent()的ACTION_DOWN中调用代码:

@Override
public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()){
        case MotionEvent.ACTION_DOWN:
            getParent().requestDisallowInterceptTouchEvent(true);
            Log.e("zw","onTouchEvent down");
            break;
        case MotionEvent.ACTION_MOVE:
            Log.e("zw","onTouchEvent move");
            break;
        case MotionEvent.ACTION_UP:
            Log.e("zw","onTouchEvent up");
            break;
    }
    return true;
}

Log打印:

zw: LinearLayout dispatchTouchEvent down
    LinearLayout onInterceptTouchEvent down
    View dispatchTouchEvent down
    View onTouchEvent down

zw: LinearLayout dispatchTouchEvent move
    View dispatchTouchEvent move
    View onTouchEvent move

zw: LinearLayout dispatchTouchEvent up
    View dispatchTouchEvent up
    View onTouchEvent up

可以看见,本次触摸事件被子View强制消费掉了。

调用顺序:
ACTION_DOWN:
ViewGroup.dispatchTouchEvent(DOWN) > ViewGroup.onInterceptTouchEvent(DOWN) > View.dispatchTouchEvent(DOWN) > View.onTouchEvent(DOWN)
ACTION_MOVE:
ViewGroup.dispatchTouchEvent(MOVE) > View.dispatchTouchEvent(MOVE) > View.onTouchEvent(MOVE)
ACTION_UP:
ViewGroup.dispatchTouchEvent(UP) > View.dispatchTouchEvent(UP) >View.onTouchEvent(UP) > 结束

总结

1、如果ViewGroup要消费触摸事件的话,一定要在onInterceptTouchEvent()返回true,否则它是否调用onTouchEvent()方法取决于子View是否消费此事件!
2、ViewGroup的dispatchTouchEvent()中尽量不要直接返回truefalse,因为这样导致ViewGroup不会再去分发任何事件!最好返回super.dispatchTouchEvent(ev)
3、子View可以通过调用getParent().requestDisallowInterceptTouchEvent(true)方法来强制消费事件(有限制条件)。

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

推荐阅读更多精彩内容