在写博客之前先感谢郭霖大神,之前拜读了好多次郭大神关于事件的文章,Android事件分发机制完全解析,带你从源码的角度彻底理解(上),Android事件分发机制完全解析,带你从源码的角度彻底理解(下),感谢郭大神。
郭老大已经对事件分析的很透彻了,小弟写这篇文章仅仅是加了一点自己的思考。
说到事件得先从view事件出发。首先自定义了一个Btn2继承Button,然后分别在onTouchEvent(),dispatchTouchEvent(),setOnTouchListener(),setOnClickListener方法中加了log,来分析其对应的流程。
public class Btn2 extends Button{
public Btn2(Context context, AttributeSet attrs) {
super(context, attrs);
this.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
System.out.println("Btn2---OnTouchListener onTouch---DOWN");
//return true;
break;
case MotionEvent.ACTION_MOVE:
System.out.println("Btn2---OnTouchListener onTouch---MOVE");
break;
case MotionEvent.ACTION_UP:
System.out.println("Btn2---OnTouchListener onTouch---UP");
break;
default:
break;
}
return false;
}
});
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Log.e("Btn2","---OnClickListener onClick---go on");
}
});
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
System.out.println("Btn2---onTouchEvent---DOWN");
break;
case MotionEvent.ACTION_MOVE:
System.out.println("Btn2---onTouchEvent---MOVE");
break;
case MotionEvent.ACTION_UP:
System.out.println("Btn2---onTouchEvent---UP");
break;
default:
break;
}
return super.onTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//getParent().requestDisallowInterceptTouchEvent(true);
System.out.println("Btn2---dispatchTouchEvent---DOWN");
break;
case MotionEvent.ACTION_MOVE:
System.out.println("Btn2---dispatchTouchEvent---MOVE");
break;
case MotionEvent.ACTION_UP:
System.out.println("Btn2---dispatchTouchEvent---UP");
break;
default:
break;
}
return super.dispatchTouchEvent(event);
}
}
然后点击btn2,下面是打印的log:
可以看到事件view的流程依次是dispatchTouchEvent-down -> OnTouchListener onTouch DOWN -> onTouchEvent DOWN ->dispatchTouchEvent UP -> OnTouchListener onTouch UP ->onTouchEvent UP -> OnClickListener onClick go on。可以看到dispatchTouchEvent分发,onTouch和onTouchEvent都走了2次,走完后最后走了一次OnClickListener。为什么会出现这样,下面我们来看看源码。
btn2继承Button,在Button中我们没有找到dispatchTouchEvent,然后继续像上找,找到view,看到dispatchTouchEvent方法中有这么一句话:
if (onFilterTouchEventForSecurity(event)) {
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
}
我们看到这句li.mOnTouchListener.onTouch(this, event),这里调用了onTouch方法,如果这里返回true(同时前面的2个参数都为true)时,result为true,然后下面的if取反,不在去调用onTouchEvent方法。ok,下面我们改一下btn2的代码,将onTouch方法的返回值直接改为true,看看打印的log情况。
看到结果是onTouchEvent和onClick方法都没有执行,很好奇为什么onClick也没有执行,后来发现在onTouchEvent的源码中调用了performClick方法,在这个方法里面执行了onclick,正好解答了刚才的疑问。
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
也就是我们常说的如果返回true,那么该事件就认为被消化掉,不再向下传递。
接着我们再看看ViewGroup的事件到底是怎么进行的。我在Btn2外面定义了一个MyFrameLayout继承FrameLayout,然后也对其几个关键的放大加了log,这里要注意在在ViewGroup中多了一个事件拦截的方法onInterceptTouchEvent。
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
System.out.println("MyFrameLayout---dispatchTouchEvent---DOWN");
break;
case MotionEvent.ACTION_MOVE:
System.out.println("MyFrameLayout---dispatchTouchEvent---MOVE");
break;
case MotionEvent.ACTION_UP:
System.out.println("MyFrameLayout---dispatchTouchEvent---UP");
break;
default:
break;
}
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
System.out.println("MyFrameLayout---onTouchEvent---DOWN");
break;
case MotionEvent.ACTION_MOVE:
System.out.println("MyFrameLayout---onTouchEvent---MOVE");
break;
case MotionEvent.ACTION_UP:
System.out.println("MyFrameLayout---onTouchEvent---UP");
break;
default:
break;
}
return super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e("MyFrameLayout", "---onInterceptTouchEvent---DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e("MyFrameLayout","---onInterceptTouchEvent---MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e("MyFrameLayout","---onInterceptTouchEvent---UP");
break;
default:
break;
}
return super.onInterceptTouchEvent(event);
}
<com.example.ontouchtext.MyFrameLayout
android:id="@+id/framelayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.example.ontouchtext.Btn2
android:id="@+id/btn2"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#aaaaaa"
android:text="btn2" />
</com.example.ontouchtext.MyFrameLayout>
接着我们再次点击一下log,注意一下我这里所有的onTouch默认返回都是false。
可以看到先走了btn2父类MyFrameLayout的分发dispatchTouchEvent的方法,接着走了拦截方法onInterceptTouchEvent,紧接着走的是btn2自己的分发dispatchTouchEvent方法,然后走了自己的onTouch,这里返回的是false,所有走了onTouchEvent,最后走了onClick。也就是说事件会先走父类,然后分发给自己,自己在进行一次分发。
说到这里基本上就把整个分发说的差不多了,下面来说一下两个有点小意思的东西。
1.如果在父类的onInterceptTouchEvent中的MotionEvent.ACTION_DOWN返回true,同时onTouchEvent的MotionEvent.ACTION_DOWN也返回true,说明父类从down就开始消化掉这个事件。我们来看看log,有啥有意思的地方:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
System.out.println("MyFrameLayout---onTouchEvent---DOWN");
return true;
//break;
case MotionEvent.ACTION_MOVE:
System.out.println("MyFrameLayout---onTouchEvent---MOVE");
break;
case MotionEvent.ACTION_UP:
System.out.println("MyFrameLayout---onTouchEvent---UP");
break;
default:
break;
}
return super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e("MyFrameLayout", "---onInterceptTouchEvent---DOWN");
return true;
// break;
case MotionEvent.ACTION_MOVE:
Log.e("MyFrameLayout","---onInterceptTouchEvent---MOVE");
// return true;
break;
case MotionEvent.ACTION_UP:
Log.e("MyFrameLayout","---onInterceptTouchEvent---UP");
break;
default:
break;
}
return true;
}
有发现了没,发现只要父类决定拦截,而且消化掉这个事件,那么以后的move,up将不再走父类的拦截,也不会再进行分发。
2.在子类(Btn2)中的onTouchEvent返回true,在父类MyFrameLayout中onInterceptTouchEvent的MotionEvent.ACTION_MOVE返回true,onTouchEvent中的MotionEvent.ACTION_MOVE也返回true。我们再来看看log。
Btn2:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
System.out.println("Btn2---onTouchEvent---DOWN");
return true;
//break;
case MotionEvent.ACTION_MOVE:
System.out.println("Btn2---onTouchEvent---MOVE");
break;
case MotionEvent.ACTION_UP:
System.out.println("Btn2---onTouchEvent---UP");
break;
default:
break;
}
return super.onTouchEvent(event);
}
MyFrameLayout:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
System.out.println("MyFrameLayout---onTouchEvent---DOWN");
break;
case MotionEvent.ACTION_MOVE:
System.out.println("MyFrameLayout---onTouchEvent---MOVE");
return true;
//break;
case MotionEvent.ACTION_UP:
System.out.println("MyFrameLayout---onTouchEvent---UP");
break;
default:
break;
}
return super.onTouchEvent(event);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e("MyFrameLayout", "---onInterceptTouchEvent---DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e("MyFrameLayout","---onInterceptTouchEvent---MOVE");
return true;
// break;
case MotionEvent.ACTION_UP:
Log.e("MyFrameLayout","---onInterceptTouchEvent---UP");
break;
default:
break;
}
return super.onInterceptTouchEvent(event);
}
哈哈,发现down父类没有拦截,然后就交给子类的onTouchEvent了,然而这里我子类的onTouchEvent中的down即使返回了true,到了move仍然走了父类的拦截方法,这时候我在父类中onInterceptTouchEvent得ACTION_MOVE返回了true,同时在父类中onTouchEvent的ACTION_MOVE返回了true,然后下面的move和up都被父类消化了,这也验证了上面第一点。
欢迎拍砖。 View事件解析(下)这里写链接内容