通过demo分析View的事件分发

我们定义三个类,一层层嵌套,依次为Activity、ViewGroup、View

Android view分发 android view的事件分发_ide

关心他们关于事件分发的三个方法,即dispatchTouchEvent(分发)、onInterceptTouchEvent(是否拦截)、onTouchEvent(消费事件)。View和Activity不存在分发,所以没有拦截的方法。

 

demo结构:

Android view分发 android view的事件分发_Android view分发_02

EventViewA.java
public class EventViewA extends View {
    private String TAG = "event_event_EventViewA";

    public EventViewA(Context context) {
        super(context);
    }

    public EventViewA(Context context, AttributeSet attrs) {
        super(context, attrs);
    }


    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.d(TAG, "dispatchTouchEvent: ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.d(TAG, "dispatchTouchEvent: ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.d(TAG, "dispatchTouchEvent: ACTION_UP");
                break;
        }
        boolean b = super.dispatchTouchEvent(event);
        Log.d(TAG, "dispatchTouchEvent: ----------  : " + b);
        return b;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.d(TAG, "onTouchEvent: ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.d(TAG, "onTouchEvent: ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.d(TAG, "onTouchEvent: ACTION_UP");
                break;
        }
        boolean b = super.onTouchEvent(event);
        Log.d(TAG, "onTouchEvent: &&&&&&&&&&&&&&&&&&&&   " + b);
        return b;
    }
}
 EventViewGroupA.java
public class EventViewGroupA extends LinearLayout {

    private String TAG = "event_event_EventViewGroupA";

    public EventViewGroupA(Context context) {
        super(context);
    }

    public EventViewGroupA(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.d(TAG, "dispatchTouchEvent: ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.d(TAG, "dispatchTouchEvent: ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.d(TAG, "dispatchTouchEvent: ACTION_UP");
                break;
        }
        boolean b = super.dispatchTouchEvent(ev);
        Log.d(TAG, "dispatchTouchEvent: ----------  " + b);
        return b;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.d(TAG, "onInterceptTouchEvent: ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.d(TAG, "onInterceptTouchEvent: ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.d(TAG, "onInterceptTouchEvent: ACTION_UP");
                break;
        }
        boolean b = super.onInterceptTouchEvent(ev);
        Log.d(TAG, "onInterceptTouchEvent: +++++++++++  " + b);
        return b;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.d(TAG, "onTouchEvent: ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.d(TAG, "onTouchEvent: ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.d(TAG, "onTouchEvent: ACTION_UP");
                break;
        }
        boolean b = super.onTouchEvent(event);
        Log.d(TAG, "onTouchEvent: &&&&&&&&&&&&&&&&&&&&  " + b);
        return b;
    }
}
 MainActivity.java
public class MainActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    private String TAG = "event_event_MainActivity";

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.d(TAG, "dispatchTouchEvent: ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.d(TAG, "dispatchTouchEvent: ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.d(TAG, "dispatchTouchEvent: ACTION_UP");
                break;
        }
        boolean b = super.dispatchTouchEvent(ev);
        Log.d(TAG, "dispatchTouchEvent: ----------  " + b);
        return b;
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.d(TAG, "onTouchEvent: ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.d(TAG, "onTouchEvent: ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.d(TAG, "onTouchEvent: ACTION_UP");
                break;
        }
        boolean b = super.onTouchEvent(event);
        Log.d(TAG, "onTouchEvent: &&&&&&&&&&&&&&&&&&&&  " + b);
        return b;
    }
}
activity_main.xml


Android view分发 android view的事件分发_ide_03

默认情况(所有事件交给super去处理)下的事件分发

1.最顶层View不可点击(即:EventViewA extends View)

log:

D/event_event_MainActivity: dispatchTouchEvent: ACTION_DOWN
 D/event_event_EventViewGroupA: dispatchTouchEvent: ACTION_DOWN
 D/event_event_EventViewGroupA: onInterceptTouchEvent: ACTION_DOWN
 D/event_event_EventViewGroupA: onInterceptTouchEvent: +++++++++++  false
 D/event_event_EventViewA: dispatchTouchEvent: ACTION_DOWN
 D/event_event_EventViewA: onTouchEvent: ACTION_DOWN
 D/event_event_EventViewA: onTouchEvent: &&&&&&&&&&&&&&&&&&&&   false
 D/event_event_EventViewA: dispatchTouchEvent: ----------  : false
 D/event_event_EventViewGroupA: onTouchEvent: ACTION_DOWN
 D/event_event_EventViewGroupA: onTouchEvent: &&&&&&&&&&&&&&&&&&&&  false
 D/event_event_EventViewGroupA: dispatchTouchEvent: ----------  false
 D/event_event_MainActivity: onTouchEvent: ACTION_DOWN
 D/event_event_MainActivity: onTouchEvent: &&&&&&&&&&&&&&&&&&&&  false
 D/event_event_MainActivity: dispatchTouchEvent: ----------  false

 D/event_event_MainActivity: dispatchTouchEvent: ACTION_MOVE
 D/event_event_MainActivity: onTouchEvent: ACTION_MOVE
 D/event_event_MainActivity: onTouchEvent: &&&&&&&&&&&&&&&&&&&&  false
 D/event_event_MainActivity: dispatchTouchEvent: ----------  false

 D/event_event_MainActivity: dispatchTouchEvent: ACTION_UP
 D/event_event_MainActivity: onTouchEvent: ACTION_UP
 D/event_event_MainActivity: onTouchEvent: &&&&&&&&&&&&&&&&&&&&  false
 D/event_event_MainActivity: dispatchTouchEvent: ----------  false

 

整个事件传递的流程(先处理DOWN事件,再处理MOVE事件【多次】,最后是UP事件)

DOWN事件:

D/event_event_MainActivity: dispatchTouchEvent: ACTION_DOWN
首先会传到 MainActivity.dispatchTouchEvent(event) 中,事件交给super( 即Activity.dispatchTouchEvent(event)   )去分发的。super将事件向下层View(即EventViewGroupA .java)传递。
 D/event_event_EventViewGroupA: dispatchTouchEvent: ACTION_DOWN
  D/event_event_EventViewGroupA: onInterceptTouchEvent: ACTION_DOWN
  D/event_event_EventViewGroupA: onInterceptTouchEvent: +++++++++++  false事件传到 EventViewGroupA.dispatchTouchEvent(event) 中,事件会交给super(即ViewGroup.dispatchTouchEvent(event) )去分发,super将事件传递到ViewGroup拦截的方法 EventViewGroupA.onInterceptTouchEvent(event) 中,我们依然是交给super(即ViewGroup.onInterceptTouchEvent(event) )来处理,super默认不拦截返回false,并将事件向下层View(即EventViewA.java)传递。
D/event_event_EventViewA: dispatchTouchEvent: ACTION_DOWN
 D/event_event_EventViewA: onTouchEvent: ACTION_DOWN
 D/event_event_EventViewA: onTouchEvent: &&&&&&&&&&&&&&&&&&&&   false
 D/event_event_EventViewA: dispatchTouchEvent: ----------  : false事件传到最底层的View的 EventViewA.dispatchTouchEvent(event)  中 ,事件会交给super(即ViewGroup.dispatchTouchEvent(event)   去处理,此时事件已经传到最底层的View,super会走到EventViewA.onTouchEvent(event)  进行消费,事件会交给super(即View.onTouchEvent(event) )去消费,View不消费事件返回了false。由于View自身没有消费事件,并且事件已经传递到最底层不会再有子View消费事件,View.dispatchTouchEvent(event) 返回了 false,并将事件向上传递给上层View(即EventViewGroupA)。
 D/event_event_EventViewGroupA: onTouchEvent: ACTION_DOWN
  D/event_event_EventViewGroupA: onTouchEvent: &&&&&&&&&&&&&&&&&&&&  false
  D/event_event_EventViewGroupA: dispatchTouchEvent: ----------  false事件将传到EventViewGroupA.onTouchEvent(event)进行消费,我们将事件交给super(即ViewGroup.onTouchEvent(event) )去消费,ViewGroup不消费事件返回了false。由于ViewGroup(即EventViewGroupA)自身没有消费事件,并且子View(即EventViewA)也没有消费事件,ViewGroup.dispatchTouchEvent(event) 返回了 false,并将事件向上传递给上层View(即MainActivity)。
 D/event_event_MainActivity: onTouchEvent: ACTION_DOWN
  D/event_event_MainActivity: onTouchEvent: &&&&&&&&&&&&&&&&&&&&  false
  D/event_event_MainActivity: dispatchTouchEvent: ----------  false事件将传到MainActivity.onTouchEvent(event)进行消费,我们将事件交给super(即Activity.onTouchEvent(event) )去消费,Activity不消费事件返回了false。由于Activity(即MainActivity)自身没有消费事件,并且子View(即EventViewGroupA和EventViewA)也没有消费事件,Activity.dispatchTouchEvent(event) 返回了 false,DOWN事件流失。

MOVE事件(UP事件):

 D/event_event_MainActivity: dispatchTouchEvent: ACTION_MOVE
 D/event_event_MainActivity: onTouchEvent: ACTION_MOVE
 D/event_event_MainActivity: onTouchEvent: &&&&&&&&&&&&&&&&&&&&  false
 D/event_event_MainActivity: dispatchTouchEvent: ----------  false

依然是从Activity的dispatchTouchEvent开始分发,我们将事件交给super(即Activity.dispatchTouchEvent(event) )去分发,super将事件传到MainActivity.onTouchEvent(event)进行消费,我们将事件交给super(即Activity.onTouchEvent(event) )去消费,Activity不消费事件返回了false。由于Activity自身没有消费事件,Activity.dispatchTouchEvent(event) 返回了 false,事件流失。

Android view分发 android view的事件分发_java_04

2.最顶层View可点击(即:clickable或longClickable 为 true)

<com.ymm.view.ev.EventViewA android:clickable="true" ... />

log:

D/event_event_MainActivity: dispatchTouchEvent: ACTION_DOWN
  D/event_event_EventViewGroupA: dispatchTouchEvent: ACTION_DOWN
  D/event_event_EventViewGroupA: onInterceptTouchEvent: ACTION_DOWN
  D/event_event_EventViewGroupA: onInterceptTouchEvent: +++++++++++  false
  D/event_event_EventViewA: dispatchTouchEvent: ACTION_DOWN
  D/event_event_EventViewA: onTouchEvent: ACTION_DOWN
  D/event_event_EventViewA: onTouchEvent: &&&&&&&&&&&&&&&&&&&&   true
  D/event_event_EventViewA: dispatchTouchEvent: ----------  : true
  D/event_event_EventViewGroupA: dispatchTouchEvent: ----------  true
  D/event_event_MainActivity: dispatchTouchEvent: ----------  trueD/event_event_MainActivity: dispatchTouchEvent: ACTION_MOVE
 D/event_event_EventViewGroupA: dispatchTouchEvent: ACTION_MOVE
 D/event_event_EventViewGroupA: onInterceptTouchEvent: ACTION_MOVE
 D/event_event_EventViewGroupA: onInterceptTouchEvent: +++++++++++  fal
 D/event_event_EventViewA: dispatchTouchEvent: ACTION_MOVE
 D/event_event_EventViewA: onTouchEvent: ACTION_MOVE
 D/event_event_EventViewA: onTouchEvent: &&&&&&&&&&&&&&&&&&&&   true
 D/event_event_EventViewA: dispatchTouchEvent: ----------  : true
 D/event_event_EventViewGroupA: dispatchTouchEvent: ----------  true
 D/event_event_MainActivity: dispatchTouchEvent: ----------  true D/event_event_MainActivity: dispatchTouchEvent: ACTION_UP
  D/event_event_EventViewGroupA: dispatchTouchEvent: ACTION_UP
  D/event_event_EventViewGroupA: onInterceptTouchEvent: ACTION_UP
  D/event_event_EventViewGroupA: onInterceptTouchEvent: +++++++++++  false
  D/event_event_EventViewA: dispatchTouchEvent: ACTION_UP
  D/event_event_EventViewA: onTouchEvent: ACTION_UP
  D/event_event_EventViewA: onTouchEvent: &&&&&&&&&&&&&&&&&&&&   true
  D/event_event_EventViewA: dispatchTouchEvent: ----------  : true
  D/event_event_EventViewGroupA: dispatchTouchEvent: ----------  true
  D/event_event_MainActivity: dispatchTouchEvent: ----------  trueD/event_event_MainActivity: dispatchTouchEvent: ACTION_DOWN
  D/event_event_EventViewGroupA: dispatchTouchEvent: ACTION_DOWN
  D/event_event_EventViewGroupA: onInterceptTouchEvent: ACTION_DOWN
  D/event_event_EventViewGroupA: onInterceptTouchEvent: +++++++++++  false事件传到EventViewA之前的分发流程没变
 D/event_event_EventViewA: dispatchTouchEvent: ACTION_DOWN
  D/event_event_EventViewA: onTouchEvent: ACTION_DOWN
  D/event_event_EventViewA: onTouchEvent: &&&&&&&&&&&&&&&&&&&&   true
  D/event_event_EventViewA: dispatchTouchEvent: ----------  : true传到EventViewA之后,事件被消耗 onTouchEvent 返回true,dispatchTouchEvent 返回true
D/event_event_EventViewGroupA: dispatchTouchEvent: ----------  true
  D/event_event_MainActivity: dispatchTouchEvent: ----------  true子View(即:EventViewA)消耗了事件( EventViewA.onTouchEvent 返回true),父View的 dispatchTouchEvent 也会返回true并且不会再走消费事件的方法。

总结

ViewGroup事件分发的流程能整理成如下伪代码:

public boolean dispatchTouchEvent(MotionEvent ev) { boolean consume = false;//事件是否被消费 if (onInterceptTouchEvent(ev)){//调用onInterceptTouchEvent判断是否拦截事件 consume = onTouchEvent(ev);//如果拦截则调用自身的onTouchEvent方法 }else{ consume = child.dispatchTouchEvent(ev);//不拦截调用子View的dispatchTouchEvent方法 } return consume;//返回值表示事件是否被消费,true事件终止,false调用父View的onTouchEvent方法 }

事件产生后,最先会调用根ViewGroup的 dispatchTouchEvent 方法,在此方法内部根据 onInterceptTouchEvent 的返回值判断是否要拦截当前事件,若拦截这个事件交给 onTouchEvent ,不拦截事件会传给子元素的 dispatchTouchEvent ,根据onTouchEvent 和 child.dispatchTouchEvent 的返回值决定分发的结果

Avtivity和View没有 拦截的能力,故没有onInterceptTouchEvent 的方法。View不需要进行拦截的判断就要调 onTouchEvent  去消费事件,当所有View和ViewGroup都没有消费事件,事件会返回给Activity 的onTouchEvent  进行消费处理。

结论:

1.同一事件序列从down事件开始,经历数据不定的move事件,以up事件结束;同一事件从外向内传递的,即从Activity --> ViewGroup --> View 。

2.事件的分发与拦截是由外向内的(即:父元素的dispatchTouchEvent 和onInterceptTouchEvent 先被调用),而事件的处理是由内向外的(即:事件能传递到子元素,子元素先处理)【Activity的 dispatchTouchEvent  最先被调用,最后得到返回值;最里层View的 onTouchEvent 最先调用】。

3.同一事件的传递规则是,分发的方法 dispatchTouchEvent  最先被调用,在dispatchTouchEvent  内部其他方法被调用。事件没有被自己和内层的View消费 dispatchTouchEvent 会返回false。

5.某个View一旦处理了事件(即:View的onTouchEvent返回了true ),那么它上层的View将没有机会处理处理这个事件(即:上层View的onTouchEvent不会被调用)。

6.某个View一旦开始处理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent返回了false),那么同一事件序列中的其他事件都不会再交给它处理,事件将提交给它的父元素去处理 【①中move与up事件将直接交给Activity来处理,不会向下传】(即不消耗down事件,将没有机会接受其他事件)。