当我们点击屏幕,就会产生了点击事件,这个事件封装成了一个类:MotionEvent
当MotionEvent产生后,那么系统就会讲MotionEvent传递给View的层级,MotionEvent在View中的层级传递过程就是点击事件分发。他有3个重要的方法
- dispatchTouchEvent(MotionEvent event) -用来进行事件的分发
- onInterceptTouchEvent(MotionEvent event) - 用来进行事件的拦截,在dispatchTouchEvent()中调用,注意的是View没有提供该方法
- onTouchEvent(MotionEvent event) - 用来处理点击事件,在dispatchTouchEvent()方法中进行调用
1.事件分发机制传递过程
当MotionEvent产生后->Activity.dispatchTouchEvent()->PhoneWindow->DecorView->ViewGroup
从Activity的dispatchTouchEvent()开始分析:
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
// 当所有view的onTouchEvent都返回ture ,整个事件结束,否则返回Activity的onTouchEvent()
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
在看getWindow().superDispatchTouchEvent(ev)看看getWindow是什么?
public Window getWindow() {
return mWindow;
}
继续看Window里面,发现Window是个抽象类,superDispatchTouchEvent这也是个抽象方法,找他实现类PhoneWindow。
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
重大发现来了一个DecorView,有人会问DecorView是什么?https://www.jianshu.com/p/324603bb5791
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
DecorView继承FrameLayout,FrameLayout又是继承ViewGroup,所有最终事件调用顶层View(ViewGroup)的dispatchTouchEvent方法。
2.View的事件分发源码解析
先看一段代码TouchView.class
public class TouchView extends View {
...
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("TAG","onTouchEvent---->"+event.getAction());
return super.onTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
}
MainActivity.class
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View view = findViewById(R.id.touch_view);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("TAG","onClick");
}
});
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.e("TAG","onTouch------>"+event.getAction());
return false;
}
});
}
}
打印结果:
03-11 20:06:58.562 3105-3105/com.lorenzogao.view E/TAG: onTouch------>0
03-11 20:06:58.562 3105-3105/com.lorenzogao.view E/TAG: onTouchEvent---->0
03-11 20:06:58.629 3105-3105/com.lorenzogao.view E/TAG: onTouch------>1
03-11 20:06:58.629 3105-3105/com.lorenzogao.view E/TAG: onTouchEvent---->1
03-11 20:06:58.632 3105-3105/com.lorenzogao.view E/TAG: onClick
场景一: 这个方法执行顺序是 OnTouchListener()->onTouchEvent()->OnClickListener();
如果OnTouchListener的onTouch()返回true时
打印结果
03-11 20:12:14.425 3105-3105/com.lorenzogao.view E/TAG: onTouch------>0
03-11 20:12:14.491 3105-3105/com.lorenzogao.view E/TAG: onTouch------>1
场景二:当OnTouchListener的onTouch()返回true时,只执行OnTouchListener,其他都不执行了。
场景三:当onTouchEvent的返回true时,不会执行onClick
onClick在View里面执行,没调用super.onTouchEvent所以调不到PerformClick()
- dispatchTouchEvent() 用来处理事件分发的
public boolean dispatchTouchEvent(MotionEvent event) {
...
boolean result = false;
//ListenerInfo 存放了关于View的所有listener,如 OnClickListener、OnLongClickListener等。
ListenerInfo li = mListenerInfo;
//如果mOnTouchListener不为null并且onTouch方法返回true,则表示事件被消费了,就不会往下执行了。
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED//是否是ENABLED 可用的
&& li.mOnTouchListener.onTouch(this, event)) {//如果是false result = false 则反之到这里验证了场景1
result = true;
}
//如果result=false 就会执行onTouchEvent事件,则反之,到这里验证了场景2
if (!result && onTouchEvent(event)) {
result = true;
}
}
return result;
}
目前还没看到点击事件
- onTouchEvent() 用来处理点击事件
public boolean onTouchEvent(MotionEvent event) {
...
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
...
if (!focusTaken) {
//PerformClick 点击事件,验证了场景1
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClickInternal();
}
}
...
return false;
}
3.ViewGroup事件分发机制源码分析
3.1.调用方法顺序, 前提是子view设置了setOnTouchListener要不然就不会调用onTouch,设置onClick要不然不会调用onClick。
- 第一次DOWN
ViewGroup.dispatchTouchEvent->ViewGroup.onInterceptTouchEvent-> View.dispatchTouchEvent->View.onTouch->View.onTouchEvent- 第二次MOVE
ViewGroup.dispatchTouchEvent->ViewGroup.onInterceptTouchEvent-> View.dispatchTouchEvent->View.onTouch->View.onTouchEvent- 第三次UP
ViewGroup.dispatchTouchEvent->ViewGroup.onInterceptTouchEvent-> View.dispatchTouchEvent->View.onTouch->View.onTouchEvent->View.onClick
3.2. 去掉onClick事件(没有的一个地方消费事件,没有一个地方返回true,所以走了一遍)
- UP事件
ViewGroup.dispatchTouchEvent->ViewGroup.onInterceptTouchEvent-> View.dispatchTouchEvent->View.onTouch->View.onTouchEvent->ViewGroup.onTouchEvent
3.3 在View的onTouchEvent方法返回true
- 第一次DOWN
ViewGroup.dispatchTouchEvent->ViewGroup.onInterceptTouchEvent-> View.dispatchTouchEvent->View.onTouch->View.onTouchEvent- 第二次MOVE
ViewGroup.dispatchTouchEvent->ViewGroup.onInterceptTouchEvent-> View.dispatchTouchEvent->View.onTouch->View.onTouchEvent- 第三次UP
ViewGroup.dispatchTouchEvent->ViewGroup.onInterceptTouchEvent-> View.dispatchTouchEvent->View.onTouch->View.onTouchEvent
3.4 在ViewGroup的onInterceptTouchEvent方法返回true
ViewGroup.dispatchTouchEvent->ViewGroup.onInterceptTouchEvent-> ViewGroup.onTouchEvent
dispatchTouchEvent方法分析
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
...
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
...
if (actionMasked == MotionEvent.ACTION_DOWN) {
//清除所有的mFirstTouchTarget
cancelAndClearTouchTargets(ev);
resetTouchState();
}
//是否拦截
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
//正常情况下会调用onInterceptTouchEvent(ev)
intercepted = onInterceptTouchEvent(ev);//默认情况下返回false
ev.setAction(action);
} else {
intercepted = false;
}
} else {
intercepted = true;
}
TouchTarget newTouchTarget = null;
// 默认情况下可以执行
if (!canceled && !intercepted) {
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
if (newTouchTarget == null && childrenCount != 0) {
···
final View[] children = mChildren;
//反序列for循环,因为RelativeLayout布局可以叠加
for (int i = childrenCount - 1; i >= 0; i--) {
...
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
resetCancelNextUpFlag(child);
//获取子View
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
...
//如果返回true进来,添加一个target,addTouchTarget方法主要给mFirstTouchTarget 赋值
newTouchTarget = addTouchTarget(child, idBitsToAssign);
break;
}
}
}
}
if (mFirstTouchTarget == null) {
//dispatchTransformedTouchEvent方法 child是个null,所有会调用super.dispatchTouchEvent(event)。
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
...
}
return handled;
}
cancelAndClearTouchTargets方法
private void cancelAndClearTouchTargets(MotionEvent event) {
if (mFirstTouchTarget != null) {
...
//清除所有的mFirstTouchTarget
clearTouchTargets();
}
}
clearTouchTargets方法 清除所有的mFirstTouchTarget
private void clearTouchTargets() {
TouchTarget target = mFirstTouchTarget;
if (target != null) {
do {
TouchTarget next = target.next;
target.recycle();
target = next;
} while (target != null);
mFirstTouchTarget = null;
}
}
dispatchTransformedTouchEvent方法
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
//child==null 会调用自己super.dispatchTouchEvent(event)
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
//child!=null 会调用自己child.dispatchTouchEvent (event)
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}
···
return handled;
}
在ViewGroup里子View没有一个地方消费事件(返回true),只会进来一次只响应DOWN事件,如果你响应MOVE 、UP事件,必须找个地方返回true,对于ViewGroup想拦截子View的触摸事件,覆写onInterceptTouchEvent方法返回true,如果onInterceptTouchEvent方法返回true,执行自己的onTouchEvent(),在ViewGroup里子View没消费事件也会调用自己onTouchEven()。
网友评论