View事件分发机制

来源:互联网 发布:生化危机7ps4淘宝 编辑:程序博客网 时间:2024/04/29 16:14

在开发过程中,我们经常遇到事件冲突现象,但是很多开发者不知道如何处理。在这里我认为只有认识View 事件的分发机制后,才会懂得如何下手,并且可以处理任何的事件冲突的现象。
一、三个方法
dispatchTouchEvent(MotionEvent ev)
该方法用于进行事件分发。如果事件能够传递给当前View,那么此方法一定会被调用,返回结果受当前View的onTouchEvent和下级的View的dispatchTouchEvent方法的影响,表示是否消耗当前事件
onInterceptTouchEvent(MotionEvent event)
该方法用于拦截事件。如果当前的View成功拦截来某个事件,那么在同一个事件序列当中,此方法将不会再次调用,返回结果表示是否拦截当前事件
onTouchEvent(MotionEvent event)
该方法用于处理事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前View无法再次接收事件。
二、三个方法联系

public boolean dispatchTouchEvent(MotionEvent ev){    boolean consume=false;    if(onInterceptToucEvent(ev)){        consume=onTouchEvent(ev);    }else{        consume=child.dispatchTouchEvent(ev);    }    return consume;}

对于一个根ViewGroup来说,点击事件产生后,首先会传递给它,这是它的dispatchTouchEvent方法会被调用,如果这个ViewGroup的onInterceptTouchEvent方法返回true就表示它要拦截当前事件,接着这个事件交给这个ViewGroup处理,即它的onTouchEvent方法会被调用,并且这个序列事件都会交给它处理,onInterceptTouchEvent方法不再调用,成功拦截只调用一次。如果这个ViewGroup的onInterceptTouchEvent方法返回false,就表示它不拦截当前事件,这个当前事件就会继续传递给它的子元素,接着子元素的dispatchTouchEvent方法就会被调用,此法反复直到事件被最终处理
三、分析
当一个View需要处理事件时,如果它设置来OnTouchListener,那么OnTouchListener中的onTouch方法会被回调,这时事件如何处理还要看onTouch方法的返回值,如果返回false,则当前View的onTouchEvent方法,如果返回true,那么onTouchEvent方法将不会再调用,由此可见,给View设置onTouchListener,其优先级比onTouchEvent方法高。在onTouchEvent方法中,如果当前设置了OnClickListener方法,那么它的onClick方法将被调用,由此可见,OnClickListener方法在三个优先级中表现为最低。

当一个点击事件产生后,它的传递过程顺序如下:Activity–>Window–>View,所以最先收到事件的是Activity,Activity再传输给Window,最后Window再传输给顶级View,也就是DecorView,顶级View收到事件后,就会按照事件分发机制去分发事件。如果在分发过程中,有个View消耗掉来该事件,那么该View的父容器不再调用调用onTouchEvent方法,除非父容器实现来事件的成功拦截。

四、详细分析

  1. 同一个事件序列是指从手指接触屏幕的那一刻起,到手指离开屏幕的那一刻结束,在这个过程中所产生的一系列事件,这个事件序列以down事件开始,中间含有数量不定的move事件,最终以up事件结束。
  2. 正常情况下,一个事件序列只能被一个view拦截且消耗。因为一旦一个元素拦截了某个事件后,那么同一个事件序列内的所有事件都会直接交给它处理,因此同一个事件序列中的事件不能分别由两个View同时处理,但是通过某个特殊手段可以做到,比如一个View将本该自己处理的事件通过onTouchEvent强行传递给其他View处理
  3. 某个View一旦决定拦截,那么这一个事件序列都只能由它来处理(如果事件序列能够传递给它的话)并且它的onInterceptTouchEvent不会再次调用,也就是说当一个View决定拦截一个事件后,那么系统会把同一个事件序列内的其他方法都直接交给它来处理,因此就不用调用这个View的onInterceptTouchEvent去询问它是否要拦截了。
  4. 某个View一旦开始处理事件,如果它不消耗ACTION_DOWN事件,即onTouchEvent方法返回false,那么同一事件序列中的其他事件都不会交给它来处理,并且事件将重新交给它的父元素去处理,即父元素的onTouchEvent方法将被调用。也就是说事件一旦交给一个view处理,那么它就鼻息消耗掉,否则同一个事件序列中剩下的事件就不再交给它处理了。
  5. 如果View不消耗ACTION_DOWN以外的其他事件,那么这个点击事件会消失,此时父元素的onTouchEvent并不会被调用,并且当前的View可以持续收到后续的事件,最终这些消失的点击事件传递给Activity来处理
  6. ViewGroup默认不拦截任何事件,即onInterceptTouchEvent的返回值的是false
  7. View没有onInterceptTouchEvent方法,一旦有点击事件,那么它的onTouchEvent方法会被调用
  8. View的onTouchEvent默认都会消耗事件(返回true),除非它是不可点击(clickable和longclickable
    同时为false),View的longclickable的属性默认为false,clickable属性要分情况,比如Button的clickable属性默认为true,而TextView的clickable属性默认值为false
  9. View的enable属性不影响onTouchEvent的默认返回值。只要它的clickable或则longclickable有一个为true,那么它的onTouchEvent就返回true
  10. onClick会发生的前提是当前的View是可点击的,并且它收到down和up事件
  11. 事件传递过程是由内到外,即事件总是先传递给父元素,然后在由父元素分发给子元素View,通过requestDisallowInterceptTouchEvent方法可以在子元素中干预父元素的事件分发过程,但是ACTION_DOWN事件除外。

先来看看单一的View类对touch事件的处理

/**     * Pass the touch screen motion event down to the target view, or this     * view if it is the target.     *     * @param event The motion event to be dispatched.     * @return True if the event was handled by the view, false otherwise.     */    public boolean dispatchTouchEvent(MotionEvent event) {        if (mInputEventConsistencyVerifier != null) {            mInputEventConsistencyVerifier.onTouchEvent(event, 0);        }        if (onFilterTouchEventForSecurity(event)) { // 一般都成立            //noinspection SimplifiableIfStatement            ListenerInfo li = mListenerInfo;            if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED                    && li.mOnTouchListener.onTouch(this, event)) { // 先在ENABLED状态下尝试调用onTouch方法                return true; // 如果被onTouch处理了,则直接返回true            }            // 从这里我们可以看出,当你既设置了OnTouchListener又设置了OnClickListener,那么当前者返回true的时候,            // onTouchEvent没机会被调用,当然你的OnClickListener也就不会被触发;另外还有个区别就是onTouch里可以            // 收到每次touch事件,而onClickListener只是在up事件到来时触发。            if (onTouchEvent(event)) {                return true;            }        }        if (mInputEventConsistencyVerifier != null) {            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);        }        return false; // 上面的都没处理,则返回false    }    /**     * Implement this method to handle touch screen motion events.     * <p>     * If this method is used to detect click actions, it is recommended that     * the actions be performed by implementing and calling     * {@link #performClick()}. This will ensure consistent system behavior,     * including:     * <ul>     * <li>obeying click sound preferences     * <li>dispatching OnClickListener calls     * <li>handling {@link AccessibilityNodeInfo#ACTION_CLICK ACTION_CLICK} when     * accessibility features are enabled     * </ul>     *     * @param event The motion event.     * @return True if the event was handled, false otherwise.     */    public boolean onTouchEvent(MotionEvent event) { // View对touch事件的默认处理逻辑        final int viewFlags = mViewFlags;        if ((viewFlags & ENABLED_MASK) == DISABLED) { // DISABLED的状态下            if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {                setPressed(false); // 复原,如果之前是PRESSED状态            }            // A disabled view that is clickable still consumes the touch            // events, it just doesn't respond to them.            return (((viewFlags & CLICKABLE) == CLICKABLE || // CLICKABLE或LONG_CLICKABLE的view标记为对事件处理了,                    (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)); // 只不过是以do nothing的方式处理了。        }        if (mTouchDelegate != null) {            if (mTouchDelegate.onTouchEvent(event)) { // 如果有TouchDelegate的话,优先交给它处理                return true; // 处理了返回true,否则接着往下走            }        }        if (((viewFlags & CLICKABLE) == CLICKABLE || // View能对touch事件响应的前提要么是CLICKABLE要么是LONG_CLICKABLE                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {            switch (event.getAction()) {                case MotionEvent.ACTION_UP: // UP事件                    // 如果外围有可以滚动的parent的话,当按下时会设置这个标志位                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) { // 按下了或者预按下了                        // take focus if we don't have it already and we should in                        // touch mode.                        boolean focusTaken = false;                        // 这行代码就是我们上篇博客中说的,设置了FocusableInTouchMode后,View在点击的时候就会                        // 尝试requestFocus(),并将focusToken设置为true                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {                            focusTaken = requestFocus(); // 能进来这个if,一般都会返回true                        }                        if (prepressed) {                            // The button is being released before we actually                            // showed it as pressed.  Make it show the pressed                            // state now (before scheduling the click) to ensure                            // the user sees it.                            // 在前面down事件的时候我们延迟显示view的pressed状态                            setPressed(true); // 直到up事件到来的时候才显示pressed状态                       }                        if (!mHasPerformedLongPress) { // 如果没有长按发生的话                            // This is a tap, so remove the longpress check                            removeLongPressCallback(); // 移除长按callback                            // Only perform take click actions if we were in the pressed state                            if (!focusTaken) { // 看到没,focusTaken是false才会进入下面的if语句                                // Use a Runnable and post this rather than calling                                // performClick directly. This lets other visual state                                // of the view update before click actions start.                        // 也就是说在touch mode下,不take focus的view第一次点击的时候才会触发onClick事件                                if (mPerformClick == null) {                                    mPerformClick = new PerformClick();                                }                                if (!post(mPerformClick)) { // 如果post失败了,则直接调用performClick()方法                                    performClick(); // 这2行代码会触发onClickListener                                }                            }                        }                        if (mUnsetPressedState == null) {                            mUnsetPressedState = new UnsetPressedState(); // unset按下状态的                        }                        if (prepressed) {                             postDelayed(mUnsetPressedState,                                    ViewConfiguration.getPressedStateDuration());                        } else if (!post(mUnsetPressedState)) {                            // If the post failed, unpress right now                            mUnsetPressedState.run();                        }                        removeTapCallback();                    }                    break;                case MotionEvent.ACTION_DOWN: // DOWN事件                    mHasPerformedLongPress = false;                    if (performButtonActionOnTouchDown(event)) {                        break;                    }                    // Walk up the hierarchy to determine if we're inside a scrolling container.                    boolean isInScrollingContainer = isInScrollingContainer();                    // For views inside a scrolling container, delay the pressed feedback for                    // a short period in case this is a scroll.                    if (isInScrollingContainer) { // 如果是在可以滚动的container里面的话                        mPrivateFlags |= PFLAG_PREPRESSED; // 设置PREPRESSED标志位                        if (mPendingCheckForTap == null) {                            mPendingCheckForTap = new CheckForTap();                        } // 延迟pressed feedback                        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());                    } else {                        // Not inside a scrolling container, so show the feedback right away                        setPressed(true); // 否则直接显示pressed feedback                        checkForLongClick(0); // 并启动长按监测                    }                    break;                case MotionEvent.ACTION_CANCEL: // 针对CANCEL事件的话,恢复各种状态,移除各种callback                    setPressed(false);                    removeTapCallback();                    removeLongPressCallback();                    break;                case MotionEvent.ACTION_MOVE: // MOVE事件                    final int x = (int) event.getX();                    final int y = (int) event.getY();                    // Be lenient about moving outside of buttons                    if (!pointInView(x, y, mTouchSlop)) { // 如果移动到view的边界之外了,                        // Outside button                        removeTapCallback(); // 则取消Tap callback,这样当你松手的时候onClick不会被触发                        if ((mPrivateFlags & PFLAG_PRESSED) != 0) { // 当已经是按下状态的话                            // Remove any future long press/tap checks                            removeLongPressCallback(); // 移除长按callback                            setPressed(false); // 恢复按下状态                        }                    }                    break;            }            return true; // 最后返回true,表示对touch事件处理过了,消费了        }        return false; // 既不能单击也不能长按的View,返回false,表示不处理touch事件    }

在开始介绍ViewGroup对touch事件的处理之前,我们还得先看看ViewGroup的一个内部类TouchTarget,因为它描述的就是被

touch的view和touch的手指相关的信息,代码如下:

/* Describes a touched view and the ids of the pointers that it has captured.     *     * This code assumes that pointer ids are always in the range 0..31 such that     * it can use a bitfield to track which pointer ids are present.     * As it happens, the lower layers of the input dispatch pipeline also use the     * same trick so the assumption should be safe here...     */    private static final class TouchTarget {        private static final int MAX_RECYCLED = 32;        private static final Object sRecycleLock = new Object[0];        private static TouchTarget sRecycleBin; // 回收再利用的链表头        private static int sRecycledCount;        public static final int ALL_POINTER_IDS = -1; // all ones        // The touched child view.        public View child;        // The combined bit mask of pointer ids for all pointers captured by the target.        public int pointerIdBits;        // The next target in the target list.        public TouchTarget next;        private TouchTarget() {        }        // 看到这个有没有很眼熟?是的Message里也有类似的实现,我们在之前介绍Message的文章里详细地分析过        public static TouchTarget obtain(View child, int pointerIdBits) {            final TouchTarget target;            synchronized (sRecycleLock) {                if (sRecycleBin == null) { // 没有可以回收的目标,则new一个返回                    target = new TouchTarget();                 } else {                    target = sRecycleBin; // 重用当前的sRecycleBin                    sRecycleBin = target.next; // 更新sRecycleBin指向下一个                     sRecycledCount--; // 重用了一个,可回收的减1                    target.next = null; // 切断next指向                }            }            target.child = child; // 找到合适的target后,赋值            target.pointerIdBits = pointerIdBits;            return target;        }        public void recycle() { // 基本是obtain的反向过程            synchronized (sRecycleLock) {                if (sRecycledCount < MAX_RECYCLED) {                    next = sRecycleBin; // next指向旧的可回收的头                    sRecycleBin = this; // update旧的头指向this,表示它自己现在是可回收的target(第一个)                    sRecycledCount += 1; // 多了一个可回收的                } else {                    next = null; // 没有next了                }                child = null; // 清空child字段            }        }    }

现在开始看看ViewGroup的方法

@Override    public boolean dispatchTouchEvent(MotionEvent ev) {        if (mInputEventConsistencyVerifier != null) {            mInputEventConsistencyVerifier.onTouchEvent(ev, 1);        }        boolean handled = false;        if (onFilterTouchEventForSecurity(ev)) { // view没有被遮罩,一般都成立            final int action = ev.getAction();            final int actionMasked = action & MotionEvent.ACTION_MASK;            // Handle an initial down.            if (actionMasked == MotionEvent.ACTION_DOWN) { // 一堆touch事件(从按下到松手)中的第一个down事件                // Throw away all previous state when starting a new touch gesture.                // The framework may have dropped the up or cancel event for the previous gesture                // due to an app switch, ANR, or some other state change.                cancelAndClearTouchTargets(ev);                resetTouchState(); // 作为新一轮的开始,reset所有相关的状态            }            // Check for interception.            final boolean intercepted; // 检查是否要拦截            if (actionMasked == MotionEvent.ACTION_DOWN // down事件                    || mFirstTouchTarget != null) { // 或者之前的某次事件已经经由此ViewGroup派发给children后被处理掉了                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;                if (!disallowIntercept) { // 只有允许拦截才执行onInterceptTouchEvent方法                    intercepted = onInterceptTouchEvent(ev); // 默认返回false,不拦截                    ev.setAction(action); // restore action in case it was changed                } else {                    intercepted = false; // 不允许拦截的话,直接设为false                }            } else {                // There are no touch targets and this action is not an initial down                // so this view group continues to intercept touches.                // 在这种情况下,actionMasked != ACTION_DOWN && mFirstTouchTarget == null                // 第一次的down事件没有被此ViewGroup的children处理掉(要么是它们自己不处理,要么是ViewGroup从一                // 开始的down事件就开始拦截),则接下来的所有事件                // 也没它们的份,即不处理down事件的话,那表示你对后面接下来的事件也不感兴趣                intercepted = true; // 这种情况下设置ViewGroup拦截接下来的事件            }            // Check for cancelation.            final boolean canceled = resetCancelNextUpFlag(this)                    || actionMasked == MotionEvent.ACTION_CANCEL; // 此touch事件是否取消了            // Update list of touch targets for pointer down, if needed.            // 是否拆分事件,3.0(包括)之后引入的,默认拆分            final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;            TouchTarget newTouchTarget = null; // 接下来ViewGroup判断要将此touch事件交给谁处理            boolean alreadyDispatchedToNewTouchTarget = false;            if (!canceled && !intercepted) { // 没取消也不拦截,即是个有效的touch事件                if (actionMasked == MotionEvent.ACTION_DOWN // 第一个手指down                        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) // 接下来的手指down                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {                    final int actionIndex = ev.getActionIndex(); // always 0 for down                    final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)                            : TouchTarget.ALL_POINTER_IDS;                    // Clean up earlier touch targets for this pointer id in case they                    // have become out of sync.                    removePointersFromTouchTargets(idBitsToAssign);                    final int childrenCount = mChildrenCount;                    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 View[] children = mChildren;                        final boolean customOrder = isChildrenDrawingOrderEnabled();                        // 从最后一个向第一个找                        for (int i = childrenCount - 1; i >= 0; i--) {                            final int childIndex = customOrder ?                                    getChildDrawingOrder(childrenCount, i) : i;                            final View child = children[childIndex];                            if (!canViewReceivePointerEvents(child)                                    || !isTransformedTouchPointInView(x, y, child, null)) {                                continue; // 不满足这2个条件直接跳过,看下一个child                            }                            // child view能receive touch事件而且touch坐标也在view边界内                            newTouchTarget = getTouchTarget(child);// 查找child对应的TouchTarget                            if (newTouchTarget != null) { // 比如在同一个child上按下了多跟手指                                // Child is already receiving touch within its bounds.                                // Give it the new pointer in addition to the ones it is handling.                                newTouchTarget.pointerIdBits |= idBitsToAssign;                                break; // newTouchTarget已经有了,跳出for循环                            }                            resetCancelNextUpFlag(child);                            // 将此事件交给child处理                            // 有这种情况,一个手指按在了child1上,另一个手指按在了child2上,以此类推                            // 这样TouchTarget的链就形成了                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {                                // Child wants to receive touch within its bounds.                                mLastTouchDownTime = ev.getDownTime();                                mLastTouchDownIndex = childIndex;                                mLastTouchDownX = ev.getX();                                mLastTouchDownY = ev.getY();                                // 如果处理掉了的话,将此child添加到touch链的头部                                // 注意这个方法内部会更新 mFirstTouchTarget                                newTouchTarget = addTouchTarget(child, idBitsToAssign);                                alreadyDispatchedToNewTouchTarget = true; // down或pointer_down事件已经被处理了                                break; // 可以退出for循环了。。。                            }                        }                    }                    // 本次没找到newTouchTarget但之前的mFirstTouchTarget已经有了                    if (newTouchTarget == null && mFirstTouchTarget != null) {                        // Did not find a child to receive the event.                        // Assign the pointer to the least recently added target.                        newTouchTarget = mFirstTouchTarget;                        while (newTouchTarget.next != null) {                            newTouchTarget = newTouchTarget.next;                        }                        // while结束后,newTouchTarget指向了最初的TouchTarget                        newTouchTarget.pointerIdBits |= idBitsToAssign;                    }                }            }            // 非down事件直接从这里开始处理,不会走上面的一大堆寻找TouchTarget的逻辑            // Dispatch to touch targets.            if (mFirstTouchTarget == null) {                // 没有children处理则派发给自己处理                // No touch targets so treat this as an ordinary view.                handled = dispatchTransformedTouchEvent(ev, canceled, null,                        TouchTarget.ALL_POINTER_IDS);            } else {                // Dispatch to touch targets, excluding the new touch target if we already                // dispatched to it.  Cancel touch targets if necessary.                TouchTarget predecessor = null;                TouchTarget target = mFirstTouchTarget;                while (target != null) { // 遍历TouchTarget形成的链表                    final TouchTarget next = target.next;                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {                        handled = true; // 已经处理过的不再让其处理事件                    } else {                        // 取消child标记                        final boolean cancelChild = resetCancelNextUpFlag(target.child)                                || intercepted;                        // 如果ViewGroup从半路拦截了touch事件则给touch链上的child发送cancel事件                        // 如果cancelChild为true的话                        if (dispatchTransformedTouchEvent(ev, cancelChild,                                target.child, target.pointerIdBits)) {                            handled = true; // TouchTarget链中任意一个处理了则设置handled为true                        }                        if (cancelChild) { // 如果是cancelChild的话,则回收此target节点                            if (predecessor == null) {                                mFirstTouchTarget = next;                            } else {                                predecessor.next = next; // 相当于从链表中删除一个节点                            }                            target.recycle(); // 回收它                            target = next;                            continue;                        }                    }                    predecessor = target; // 访问下一个节点                    target = next;                }            }            // Update list of touch targets for pointer up or cancel, if needed.            if (canceled                    || actionMasked == MotionEvent.ACTION_UP                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {                // 取消或up事件时resetTouchState                resetTouchState();            } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {                // 当某个手指抬起时,将其相关的信息移除                final int actionIndex = ev.getActionIndex();                final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);                removePointersFromTouchTargets(idBitsToRemove);            }        }        if (!handled && mInputEventConsistencyVerifier != null) {            mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);        }        return handled; // 返回处理的结果    }

在看看ViewGroup的拦截方法

public boolean onInterceptTouchEvent(MotionEvent ev) {        return false; // 其默认直接返回false,表示不拦截    }

最后为了完整性,我这里把TouchTarget相关的方法都列一下以便大家参考:

/**     * Resets all touch state in preparation for a new cycle.     */    private void resetTouchState() {        clearTouchTargets();        resetCancelNextUpFlag(this);        mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;    }    /**     * Resets the cancel next up flag.     * Returns true if the flag was previously set.     */    private static boolean resetCancelNextUpFlag(View view) {        if ((view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0) {            view.mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;            return true;        }        return false;    }    /**     * Clears all touch targets.     */    private void clearTouchTargets() {        TouchTarget target = mFirstTouchTarget;        if (target != null) {            do {                TouchTarget next = target.next;                target.recycle();                target = next;            } while (target != null);            mFirstTouchTarget = null;        }    }    /**     * Cancels and clears all touch targets.     */    private void cancelAndClearTouchTargets(MotionEvent event) {        if (mFirstTouchTarget != null) {            boolean syntheticEvent = false;            if (event == null) {                final long now = SystemClock.uptimeMillis();                event = MotionEvent.obtain(now, now,                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);                event.setSource(InputDevice.SOURCE_TOUCHSCREEN);                syntheticEvent = true;            }            for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {                resetCancelNextUpFlag(target.child);                dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);            }            clearTouchTargets();            if (syntheticEvent) {                event.recycle();            }        }    }    /**     * Gets the touch target for specified child view.     * Returns null if not found.     */    private TouchTarget getTouchTarget(View child) {        for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {            if (target.child == child) {                return target;            }        }        return null;    }    /**     * Adds a touch target for specified child to the beginning of the list.     * Assumes the target child is not already present.     */    private TouchTarget addTouchTarget(View child, int pointerIdBits) {        TouchTarget target = TouchTarget.obtain(child, pointerIdBits);        target.next = mFirstTouchTarget;        mFirstTouchTarget = target;        return target;    }    /**     * Removes the pointer ids from consideration.     */    private void removePointersFromTouchTargets(int pointerIdBits) {        TouchTarget predecessor = null;        TouchTarget target = mFirstTouchTarget;        while (target != null) {            final TouchTarget next = target.next;            if ((target.pointerIdBits & pointerIdBits) != 0) {                target.pointerIdBits &= ~pointerIdBits;                if (target.pointerIdBits == 0) {                    if (predecessor == null) {                        mFirstTouchTarget = next;                    } else {                        predecessor.next = next;                    }                    target.recycle();                    target = next;                    continue;                }            }            predecessor = target;            target = next;        }    }    // 类似从链表中删除某个特定的节点    private void cancelTouchTarget(View view) {        TouchTarget predecessor = null;        TouchTarget target = mFirstTouchTarget;        while (target != null) {            final TouchTarget next = target.next;            if (target.child == view) {                if (predecessor == null) {                    mFirstTouchTarget = next;                } else {                    predecessor.next = next;                }                target.recycle();                final long now = SystemClock.uptimeMillis();                MotionEvent event = MotionEvent.obtain(now, now,                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);                event.setSource(InputDevice.SOURCE_TOUCHSCREEN);                view.dispatchTouchEvent(event);                event.recycle();                return;            }            predecessor = target;            target = next;        }    }

对于以上的理解,来自android开发艺术探索以及http://www.cnblogs.com/xiaoweiz/p/3838682.html这个链接的理解。

0 0
原创粉丝点击