- 事件分發(fā)
事件從Activity開始分發(fā),首先Activity 實現(xiàn)了Window.Callback接口,并實現(xiàn)了接口里面的dispatchTouchEvent()方法
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, // Activity 實現(xiàn)了 Window.Callback接口 并實現(xiàn)了接口里面的dispatchTouchEvent()方法
KeyEvent.Callback,OnCreateContextMenuListener, ComponentCallbacks2,Window.OnWindowDismissedCallback, WindowControllerCallback, AutofillManager.AutofillClient {
//開始處理事件
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
//如果收到 ACTION_DOWN事件, 調(diào)用onUserInteraction(), 里面是一個空實現(xiàn),等用戶自己實現(xiàn)一個交互
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) { //getWindow()即是PhoneWindow 把事件從Activity傳入到PhoneWindow中,若PhoneWindow返回true,代表消費了此事件
return true; //事件被getWindow().superDispatchTouchEvent(ev)里面消費了膳叨,有可能是里面的ViewGroup或View
}
//若事件在以上都沒用被消費,則執(zhí)行Activity的onTouchEvent()方法去處理該事件
return onTouchEvent(ev);
}
public boolean onTouchEvent(MotionEvent event) {
if (mWindow.shouldCloseOnTouch(this, event)) { //查看window是否應該關(guān)閉OnTouch事件 如果關(guān)閉痘系,則finish()當前Activity
finish();
return true;
}
return false;
}
}
在Activity中調(diào)用getWindow().superDispatchTouchEvent(ev) 菲嘴, 事件從Activity派發(fā)到PhoneWindow在把事件從PhoneWindow派發(fā)到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中又把事件派發(fā)到 mDecor處理
return mDecor.superDispatchTouchEvent(event);
}
}
// DecorView.java
// mDecor.superDispatchTouchEvent(event)
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
public boolean superDispatchTouchEvent(MotionEvent event) {
//在此處又調(diào)用了super.dispatchTouchEvent(event) ,即調(diào)用父類的dispatchTouchEvent(event) 方法去處理事件
return super.dispatchTouchEvent(event);
}
}
因為FrameLayout extends ViewGroup 汰翠, 即在DecorView中又默認調(diào)用了ViewGroup的dispatchTouchEvent()方法去處理事件
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
/**
* 事件分發(fā)到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) {
// 調(diào)用onInterceptTouchEvent(ev)方法查看事件是否被攔截,一般情況下默認是不攔截复唤。自定義ViewGroup時可根據(jù)實際開發(fā)情況重寫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數(shù)量
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健田, 分發(fā)事件
for (int i = childrenCount - 1; i >= 0; i--) {
//省略部分代碼......
//開始把事件派發(fā)給子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中處理佛纫,重寫該方法妓局,根據(jù)實際條件去攔截事件分發(fā))
*/
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;
}
/**
* 事件派發(fā)給子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為空,則默認調(diào)用super.dispatchTouchEvent(event)呈宇,即View的dispatchTouchEvent(event)方法
handled = super.dispatchTouchEvent(event);
} else {
// 調(diào)用子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();
//返回事件處理結(jié)果
return handled;
}
}
ViewGroup遍歷子view好爬,把事件派發(fā)給View處理,即調(diào)用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);
}
//處理結(jié)果(是否消費了該事件)
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設(shè)置了setOnTouchListener存炮,并且在OnTouchListener接口中的onTouch方法中返回了true,即消費了該事件
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
//以上if判斷都滿足時,則消費了該事件(即處理了此次的事件分發(fā))
result = true;
}
//如果沒有設(shè)置setOnTouchListener或者在在OnTouchListener接口中的onTouch方法中返回false,則執(zhí)行下面的的代碼
//result為false時型豁,執(zhí)行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是否可點擊的,即是否設(shè)置了setClickable() ,Button是默認設(shè)置了setClickable()尚蝌,TextView默認沒有設(shè)置
final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
//省略部分代碼......
//如果該view是可點擊的迎变,即設(shè)置了setClickable(),或者在xml中設(shè)置了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中設(shè)置了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;
}
}
事件分發(fā)大致流程圖:
image.png