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

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

VieGroup触摸事件概述

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

ViewGroup触摸事件调用原则

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

  • ViewGroup能够在onInterceptTouchEvent()中拦截事件,onInterceptTouchEvent()在以后执行,若是返回true,则会执行自身的onTouchEvent()方法,但后续onInterceptTouchEvent()onTouchEvent()将不会调用。ide

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

  • ViewGroup若是返回在dispatchTouchEvent()中返回false,那么后续全部的触摸操做都不会再执行!spa

  • ViewGroup在dispatchTouchEvent()return truereturn super.dispatchTouchEvent(ev)会致使触摸结果不同!code

ViewGroup拦截触摸事件示例

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

一、正常状态(子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) > 结束

二、正常状态(子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) > 结束

三、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) > 结束

四、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) >

五、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) > 结束

总结

一、若是ViewGroup要消费触摸事件的话,必定要在onInterceptTouchEvent()返回true,不然它是否调用onTouchEvent()方法取决于子View是否消费此事件!
二、ViewGroup的dispatchTouchEvent()中尽可能不要直接返回truefalse,由于这样致使ViewGroup不会再去分发任何事件!最好返回super.dispatchTouchEvent(ev)
三、子View能够经过调用getParent().requestDisallowInterceptTouchEvent(true)方法来强制消费事件(有限制条件)。