通过demo分析View的事件分发
我们定义三个类,一层层嵌套,依次为Activity、ViewGroup、View
关心他们关于事件分发的三个方法,即dispatchTouchEvent(分发)、onInterceptTouchEvent(是否拦截)、onTouchEvent(消费事件)。View和Activity不存在分发,所以没有拦截的方法。
demo结构:
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
默认情况(所有事件交给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,事件流失。
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: ---------- trueD/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: ---------- true
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事件传到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事件,将没有机会接受其他事件)。