Android的事件分发的理论网络上已经有很多了,今天自己通过实例来深刻理解ViewGroup和View的事件分发,只有理解了其分发原理,就很容易处理平时遇到的滑动冲突的问题。
ViewGroup
首先看ViewGroup的事件分发:
自定义一个LinearLatout:
public class CustomLayout extends LinearLayout {
private static final String TAG = "CustomLayout";
public CustomLayout(Context context) {
super(context);
}
public CustomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
第一步:拦截事件 onInterceptTouchEvent的返回值为true,则会调用OnTouchEvent事件
public class CustomLayout extends LinearLayout {
private static final String TAG = "CustomLayout";
public CustomLayout(Context context) {
super(context);
}
public CustomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d(TAG, "dispatchTouchEvent: ");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.d(TAG, "onInterceptTouchEvent: ");
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "onTouchEvent: ");
return super.onTouchEvent(event);
}
}
Debug的结果:
CustomLayout: dispatchTouchEvent:
CustomLayout: onInterceptTouchEvent:
CustomLayout: onTouchEvent:
第二步:不拦截事件 onInterceptTouchEvent的返回值为false
public class CustomLayout extends LinearLayout {
private static final String TAG = "CustomLayout";
public CustomLayout(Context context) {
super(context);
}
public CustomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d(TAG, "dispatchTouchEvent: ");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.d(TAG, "onInterceptTouchEvent: ");
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "onTouchEvent: ");
return super.onTouchEvent(event);
}
}
Debug结果:
CustomLayout: dispatchTouchEvent:
CustomLayout: onInterceptTouchEvent:
View
view作为一个单一的对象,当事件分发给它时,他只有处理和不处理两种选择,所以没有拦截事件。
第一种:如果注册了OnTouchlistener()监听事件,首先调用的是它的OnTouchListener(),如果它的返回值为True,则事件就会被消费掉;
public class CustomView extends Button {
private static final String TAG = "CustomView";
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.d(TAG, "dispatchTouchEvent: ");
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "onTouchEvent: ");
return true;
}
}
View注册了OnTouchListener()事件:
mCustomView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.d(TAG, "onTouch: ");
return true;
}
});
Debug的结果:
CustomLayout: dispatchTouchEvent:
CustomLayout: onInterceptTouchEvent:
CustomView: dispatchTouchEvent:
DispatchEventActivity: onTouch:
从结果可以证明上面的结论,没有调用OnTouchEvent()。
第二种:如果没有注册OnTouchListener()监听事件,就会调用OnTouchEvent()方法,如果OnTouchEvent的返回值为True,则表示该View消费了该事件。
public class CustomView extends Button {
private static final String TAG = "CustomView";
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.d(TAG, "dispatchTouchEvent: ");
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "onTouchEvent: ");
return true;
}
}
Debug的结果:
CustomLayout: dispatchTouchEvent:
CustomLayout: onInterceptTouchEvent:
CustomView: dispatchTouchEvent:
CustomView: onTouchEvent:
结果可以证明直接回调用OnTouchEvent()方法。
第三种:如果注册了OnClickListener()监听事件,必须OnTouchEvent()返回值必须为父类的方法。
public class CustomView extends Button {
private static final String TAG = "CustomView";
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.d(TAG, "dispatchTouchEvent: ");
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "onTouchEvent: ");
return super.onTouchEvent(event);
}
}
mCustomView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "onClick: ");
}
});
Debug的结果:
CustomLayout: dispatchTouchEvent:
CustomLayout: onInterceptTouchEvent:
CustomView: dispatchTouchEvent:
DispatchEventActivity: onTouch:
CustomView: onTouchEvent:
DispatchEventActivity: onClick:
第四种:OnTouchEvent()消费了该事件:
public class CustomView extends Button {
private static final String TAG = "CustomView";
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.d(TAG, "dispatchTouchEvent: ");
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "onTouchEvent: ");
return true;
}
}
Debug结果:
CustomLayout: dispatchTouchEvent:
CustomLayout: onInterceptTouchEvent:
CustomView: dispatchTouchEvent:
DispatchEventActivity: onTouch:
CustomView: onTouchEvent:
结果证明没有调用OnClickListener()。
由此得出的结论可以如下:
对于一个单一的View而言,事件的优先级为OnTouchListener()–OnTouchEvent()—OnClickListener();