为了很好的理解事件分发机制,我们用一个自定义的RelativeLayout,里面装一个自定义的Button,然后分别MyRelativelayout重写里面的dispatchTouchEvent、onInterceptTouchEvent以及onTouchEvent方法,重写MyButton的onTouchEvent以及dispatchTouchEvent。(这里说明一下,因为RelativeLayout是继承ViewGroup的,所以它有拦截机制(onInterceptTouchEvent),但Button是继承是View的,所以没有这个方法),为了方便事件机制理解,这里demo只看ACTION_DOWN事件。
首先都是返回父类的方法,看看返回结果:
MyRelativeLayout.java
public class MyRelativeLayout extends RelativeLayout {
public MyRelativeLayout(Context context) {
super(context);
}
public MyRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public MyRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_DOWN)
Log.d(MyConstants.VIEW_TAG,MyRelativeLayout.class.getSimpleName()+"===onTouchEvent");
return super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(ev.getAction()==MotionEvent.ACTION_DOWN)
Log.d(MyConstants.VIEW_TAG,MyRelativeLayout.class.getSimpleName()+"===onInterceptTouchEvent");
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(ev.getAction()==MotionEvent.ACTION_DOWN)
Log.d(MyConstants.VIEW_TAG,MyRelativeLayout.class.getSimpleName()+"===dispatchTouchEvent");
return super.dispatchTouchEvent(ev);
}
}
MyButton.java
public class MyButton extends Button {
public MyButton(Context context) {
super(context);
}
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public MyButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_DOWN)
Log.d(MyConstants.VIEW_TAG,MyButton.class.getSimpleName()+"===onTouchEvent");
return super.onTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(ev.getAction()==MotionEvent.ACTION_DOWN)
Log.d(MyConstants.VIEW_TAG,MyButton.class.getSimpleName()+"===dispatchTouchEvent");
return super.dispatchTouchEvent(ev);
}
}
MainActivity调用如下:
private void testButton() {
MyRelativeLayout myRelativeLayout = findViewById(R.id.my_relative);
myRelativeLayout.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_DOWN)
Log.d(MyConstants.VIEW_TAG,"testButton myRelativeLayout====onTouch");
return false;
}
});
MyButton myButton = findViewById(R.id.my_button);
myButton.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_DOWN)
Log.d(MyConstants.VIEW_TAG,"testButton myButton====onTouch");
return false;
}
});
}
结果如下:
2021-04-05 10:27:35.095 8281-8281/com.jxd.studyone D/VIEW_TAG: MyRelativeLayout===dispatchTouchEvent
2021-04-05 10:27:35.095 8281-8281/com.jxd.studyone D/VIEW_TAG: MyRelativeLayout===onInterceptTouchEvent
2021-04-05 10:27:35.095 8281-8281/com.jxd.studyone D/VIEW_TAG: MyButton===dispatchTouchEvent
2021-04-05 10:27:35.095 8281-8281/com.jxd.studyone D/VIEW_TAG: testButton myButton====onTouch
2021-04-05 10:27:35.095 8281-8281/com.jxd.studyone D/VIEW_TAG: MyButton===onTouchEvent
从这里可以分析出来,如果走父类默认的事件分发机制,则先走MyRelativeLayout的分发,拦截,然后给子类消费。
假设我们将上述MainActivity调用改为:
MyButton myButton = findViewById(R.id.my_button);
myButton.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_DOWN)
Log.d(MyConstants.VIEW_TAG,"testButton myButton====onTouch");
return true;
}
});
则会返回结果如下:
2021-04-05 10:41:11.399 8686-8686/com.jxd.studyone D/VIEW_TAG: MyRelativeLayout===dispatchTouchEvent
2021-04-05 10:41:11.399 8686-8686/com.jxd.studyone D/VIEW_TAG: MyRelativeLayout===onInterceptTouchEvent
2021-04-05 10:41:11.399 8686-8686/com.jxd.studyone D/VIEW_TAG: MyButton===dispatchTouchEvent
2021-04-05 10:41:11.399 8686-8686/com.jxd.studyone D/VIEW_TAG: testButton myButton====onTouch
事件不再回到MyButton的onTouch
假设我们在MyRelativeLayout拦截这个事件:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(ev.getAction()==MotionEvent.ACTION_DOWN)
Log.d(MyConstants.VIEW_TAG,MyRelativeLayout.class.getSimpleName()+"===onInterceptTouchEvent");
return true;//super.onInterceptTouchEvent(ev)
}
则会返回如下结果:
2021-04-05 10:51:57.360 8825-8825/com.jxd.studyone D/VIEW_TAG: MyRelativeLayout===dispatchTouchEvent
2021-04-05 10:51:57.360 8825-8825/com.jxd.studyone D/VIEW_TAG: MyRelativeLayout===onInterceptTouchEvent
2021-04-05 10:51:57.360 8825-8825/com.jxd.studyone D/VIEW_TAG: testButton myRelativeLayout====onTouch
2021-04-05 10:51:57.360 8825-8825/com.jxd.studyone D/VIEW_TAG: MyRelativeLayout===onTouchEvent
我们可以看到MyButton不会有任何事件响应,事件被我们MyRelativeLayout消费掉了。
我们再假设在MyRelativeLayout不再往下分发这个事件:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(ev.getAction()==MotionEvent.ACTION_DOWN)
Log.d(MyConstants.VIEW_TAG,MyRelativeLayout.class.getSimpleName()+"===dispatchTouchEvent");
return false;//super.dispatchTouchEvent(ev)
}
则会返回如下结果:
2021-04-05 10:54:08.848 8922-8922/com.jxd.studyone D/VIEW_TAG: MyRelativeLayout===dispatchTouchEvent
我们发现事件到dispathTouchEvent之后,就不再放下传递了。
同理,我们如果放开MyRelativeLayout的事件分发和拦截,然后修改MyButton的事件分发为false:
则会出现如下结果:
2021-04-05 10:56:03.519 9061-9061/com.jxd.studyone D/VIEW_TAG: MyRelativeLayout===dispatchTouchEvent
2021-04-05 10:56:03.519 9061-9061/com.jxd.studyone D/VIEW_TAG: MyRelativeLayout===onInterceptTouchEvent
2021-04-05 10:56:03.519 9061-9061/com.jxd.studyone D/VIEW_TAG: MyButton===dispatchTouchEvent
总结:
1.事件分发是从外层向内层分发
2.从Activity向ViewGroup,再向view进行分发。
3.事件如果不分发,或者被拦截,则子view不会再接收到相应的事件。
至此,我相信各位安卓开发的同学已经知道事件分发机制的来龙去脉了。