前言
这里讲的事件是Android的点击事件。

什么是事件分发机制
事件分发中的事件是说当用户Touch屏幕时会有三种手势:ACTION_DOWN、ACTION_UP、ACTION_MOVE。而分发就是把这三种手势传递分发到某个具体的view中。

溯源
其实可以用一张图来表示点击事件分发机制的话很简单:

那么Activity又是如何获的点击事件呢?我们先从activity的dispatchTouchEvent源码下手:

public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}

从源码看是getWindow()在处理activity的事件分发

public Window getWindow() {
return mWindow;
}

window是抽象类,只能从PhoneWindow(window的全局唯一实现)中找superDispatchTouchEvent

@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}

PhoneWindow的superDispatchTouchEvent则调用了DecorView的superDispatchTouchEvent。

public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}

看到这里发现好像不知道如何传递点击事件了。别着急,DecorView作为ViewGroup的子类,此时super.dispatchTouchEvent(event)调用的是ViewGroup的dispatchTouchEvent。

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
        。。。
        resetCancelNextUpFlag(child);
        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 & lt; 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;
        }
    。。。
    }

viewGroup重置TouchState后调用dispatchTransformedTouchEvent()重新分发点击事件。

/**
 * Transforms a motion event into the coordinate space of a particular child view,
 * filters out irrelevant pointer ids, and overrides its action if necessary.
 * If child is null, assumes the MotionEvent will be sent to this ViewGroup instead.
 */
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
        View child, int desiredPointerIdBits) {
...
    // If the number of pointers is the same and we don't need to perform any fancy
    // irreversible transformations, then we can reuse the motion event for this
    // dispatch as long as we are careful to revert any changes we make.
    // Otherwise we need to make a copy.
    final MotionEvent transformedEvent;
    if (newPointerIdBits == oldPointerIdBits) {
        if (child == null || child.hasIdentityMatrix()) {
            if (child == null) {
                handled = super.dispatchTouchEvent(event);
            } else {
                final float offsetX = mScrollX - child.mLeft;
                final float offsetY = mScrollY - child.mTop;
                event.offsetLocation(offsetX, offsetY);
                handled = child.dispatchTouchEvent(event);
                event.offsetLocation(-offsetX, -offsetY);
            }
            return handled;
        }
        transformedEvent = MotionEvent.obtain(event);
    } else {
        transformedEvent = event.split(newPointerIdBits);
    }

    // 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;
}

调用子view的事件分发机制,流程如下图所示:

那activity的点击事件是怎么来的呢?别着急,这个细节我们在后面讲。

5 thoughts on “Android 事件分发机制”

发表评论

邮箱地址不会被公开。