- 事件分发
事件从Activity开始分发,首先Activity 实现了Window.Callback接口,并实现了接口里面的dispatchTouchEvent()方法
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, // Activity 实现了 Window.Callback接口 并实现了接口里面的dispatchTouchEvent()方法
KeyEvent.Callback,OnCreateContextMenuListener, ComponentCallbacks2,Window.OnWindowDismissedCallback, WindowControllerCallback, AutofillManager.AutofillClient {
//开始处理事件
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
//如果收到 ACTION_DOWN事件, 调用onUserInteraction(), 里面是一个空实现,等用户自己实现一个交互
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) { //getWindow()即是PhoneWindow 把事件从Activity传入到PhoneWindow中,若PhoneWindow返回true,代表消费了此事件
return true; //事件被getWindow().superDispatchTouchEvent(ev)里面消费了,有可能是里面的ViewGroup或View
}
//若事件在以上都没用被消费,则执行Activity的onTouchEvent()方法去处理该事件
return onTouchEvent(ev);
}
public boolean onTouchEvent(MotionEvent event) {
if (mWindow.shouldCloseOnTouch(this, event)) { //查看window是否应该关闭OnTouch事件 如果关闭,则finish()当前Activity
finish();
return true;
}
return false;
}
}
在Activity中调用getWindow().superDispatchTouchEvent(ev) , 事件从Activity派发到PhoneWindow在把事件从PhoneWindow派发到DecorView中
// PhoneWindow.java
public class PhoneWindow extends Window implements MenuBuilder.Callback {
// DecorView extends FrameLayout
private DecorView mDecor; //DecorView也即是一个FrameLayout extends ViewGroup
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
//在PhoneWindow中又把事件派发到 mDecor处理
return mDecor.superDispatchTouchEvent(event);
}
}
// DecorView.java
// mDecor.superDispatchTouchEvent(event)
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
public boolean superDispatchTouchEvent(MotionEvent event) {
//在此处又调用了super.dispatchTouchEvent(event) ,即调用父类的dispatchTouchEvent(event) 方法去处理事件
return super.dispatchTouchEvent(event);
}
}
因为FrameLayout extends ViewGroup , 即在DecorView中又默认调用了ViewGroup的dispatchTouchEvent()方法去处理事件
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
/**
* 事件分发到ViewGroup中,在dispatchTouchEvent()方法处理
*/
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}
// If the event targets the accessibility focused view and this is it, start
// normal event dispatch. Maybe a descendant is what will handle the click.
//
if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
ev.setTargetAccessibilityFocus(false);
}
boolean handled = false; // 事件是否被处理
if (onFilterTouchEventForSecurity(ev)) {
//省略部分代码......
// Check for interception.
final boolean intercepted; // 该事件是否被拦截
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
// 调用onInterceptTouchEvent(ev)方法查看事件是否被拦截,一般情况下默认是不拦截。自定义ViewGroup时可根据实际开发情况重写onInterceptTouchEvent()方法
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}
//省略部分代码......
//如果事件没有取消并且没有被拦截
if (!canceled && !intercepted) {
//省略部分代码......
final int childrenCount = mChildrenCount; // 子view数量
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// Find a child that can receive the event.
// Scan children from front to back.
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
//遍历子所有view, 分发事件
for (int i = childrenCount - 1; i >= 0; i--) {
//省略部分代码......
//开始把事件派发给子view处理, 在dispatchTransformedTouchEvent()把事件真正交给子view处理
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// childIndex points into presorted list, find original index
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
// The accessibility focus didn't handle the event, so clear
// the flag and do a normal dispatch to all children.
ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null) preorderedList.clear();
}
//省略部分代码......
}
}
//省略部分代码......
}
if (!handled && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
}
return handled;
}
/**
* 判断事件是否被拦截(拦截事件是在ViewGroup中处理,重写该方法,根据实际条件去拦截事件分发)
*/
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
&& ev.getAction() == MotionEvent.ACTION_DOWN
&& ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
&& isOnScrollbarThumb(ev.getX(), ev.getY())) {
return true;
}
return false;
}
/**
* 事件派发给子view处理
*/
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
// Canceling motions is a special case. We don't need to perform any transformations
// or filtering. The important part is the action, not the contents.
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
//如果传入的view为空,则默认调用super.dispatchTouchEvent(event),即View的dispatchTouchEvent(event)方法
handled = super.dispatchTouchEvent(event);
} else {
// 调用子view自己的的dispatchTouchEvent()
handled = child.dispatchTouchEvent(event); //
}
event.setAction(oldAction);
return handled;
}
//省略部分代码......
// Perform any necessary transformations and dispatch.
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
handled = child.dispatchTouchEvent(transformedEvent);
}
// Done.
transformedEvent.recycle();
//返回事件处理结果
return handled;
}
}
ViewGroup遍历子view,把事件派发给View处理,即调用View中的dispatchTouchEvent()方法处理事件
public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
/**
*事件最后传入到View中处理
*/
public boolean dispatchTouchEvent(MotionEvent event) {
// If the event should be handled by accessibility focus first.
//该view是否是focus
if (event.isTargetAccessibilityFocus()) {
// We don't have focus or no virtual descendant has it, do not handle the event.
if (!isAccessibilityFocusedViewOrHost()) {
return false;
}
// We have focus and got the event, then use normal event dispatch.
event.setTargetAccessibilityFocus(false);
}
//处理结果(是否消费了该事件)
boolean result = false;
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
final int actionMasked = event.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Defensive cleanup for new gesture
stopNestedScroll();
}
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
// 如果ListenerInfo不为空, 该View设置了setOnTouchListener,并且在OnTouchListener接口中的onTouch方法中返回了true,即消费了该事件
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
//以上if判断都满足时,则消费了该事件(即处理了此次的事件分发)
result = true;
}
//如果没有设置setOnTouchListener或者在在OnTouchListener接口中的onTouch方法中返回false,则执行下面的的代码
//result为false时,执行onTouchEvent方法
if (!result && onTouchEvent(event)) {
result = true;
}
}
//省略部分代码......
return result;
}
/**
* onTouchEvent方法
*/
public boolean onTouchEvent(MotionEvent event) {
final float x = event.getX();
final float y = event.getY();
final int viewFlags = mViewFlags;
final int action = event.getAction();
//判断view是否可点击的,即是否设置了setClickable() ,Button是默认设置了setClickable(),TextView默认没有设置
final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
//省略部分代码......
//如果该view是可点击的,即设置了setClickable(),或者在xml中设置了clickable = true
if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
switch (action) {
case MotionEvent.ACTION_UP:
//省略部分代码......
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
//省略部分代码......
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
//如果该view中设置了setOnClickListener()方法,则在ACTION_UP事件中会处理 点击事件
performClickInternal();
}
}
}
//省略部分代码......
break;
//省略部分代码......
return true;
}
return false;
}
/**
*处理点击事件
*/
private boolean performClickInternal() {
// Must notify autofill manager before performing the click actions to avoid scenarios where
// the app has a click listener that changes the state of views the autofill service might
// be interested on.
notifyAutofillManagerOnClick();
return performClick();
}
/**
* 处理view的点击事件并消费该事件
*/
public boolean performClick() {
// We still need to call this method to handle the cases where performClick() was called
// externally, instead of through performClickInternal()
notifyAutofillManagerOnClick();
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);
notifyEnterOrExitForAutoFillIfNeeded(true);
return result;
}
}
事件分发大致流程图:

网友评论