AndroidTouch事件

2019-04-12  本文已影响0人  会飞的羽天羽

记录一下Touch事件的分析- -

什么是事件:当用户触摸屏幕时,将产生的触摸行为(Touch事件)

事件的类型:

正常情况下,一次手指触摸屏幕的行为会触发一系列点击事件

事件分发对象

Activity ——> ViewGroup——>View

主要方法

Activity

当一个事件发生时首先会调用Activity的dispatchTouchEvent()事件
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        onUserInteraction(); //这是一个空方法子类可以实现来获取到当前用户触摸屏幕的监听  
    }
    //getWindow 返回的是PhoneWindow 实际上就是调用PhoneWindow的superDispatchTouchEvent的方法
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    //如果没有View消费事件,就会执行Activity的onTouchEvent事件
    return onTouchEvent(ev);
}
然后在PhoneWindow中的superDispatchTouchEvent()方法
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
    //mDecor 是继承自FrameLayout的一个子类
    return mDecor.superDispatchTouchEvent(event);
}
然后再看一下DecorView的superDispatchTouchEvent()方法
public boolean superDispatchTouchEvent(MotionEvent event) {
    //它调用了父类的dispatchTouchEvent()方法
    return super.dispatchTouchEvent(event);
}
由于DecorView是FrameLayout的子类所以就到了ViewGroup的dispatchTouchEvent()方法

流程就是

image.png

Activity收到事件后会一层一层的往下派发到ViewGroup到了ViewGroup之后就涉及到了ViewGroup的dispatchTouchEvent方法。

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    if (mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
    }
    if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
        ev.setTargetAccessibilityFocus(false);
    }

    boolean handled = false;
    //mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0&& (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED!=0
    //就是检查上述两个标志上述两个标志均为true时onFilterTouchEventForSecurity会返回false 
    //mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0 是View有设置被遮挡时不处理触摸事件的flag
    //event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED!=0 检查受到该事件的窗口是被其它窗口遮挡
    //FILTER_TOUCHES_WHEN_OBSCURED可以通过在xml文件中的android:filterTouchesWhenObscured来设置,或者在Java中通过setFilterTouchesWhenObscured()来添加或移除
    //DecorView默认是没有这个标志位的,而其他View基本上默认都是有的
    if (onFilterTouchEventForSecurity(ev)) {
        final int action = ev.getAction();
        final int actionMasked = action & MotionEvent.ACTION_MASK;

        // Handle an initial down.
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            //当时down事件时,是一个新的事件的开始,会进行一系列的reset操作,对上一次的事件一些状态进行重置&mFirstTouchTarget设置成null
            //清除TouchTarget的缓存
            cancelAndClearTouchTargets(ev);
            //对mGroupFlags的标志进行重置为~FLAG_DISALLOW_INTERCEPT
            resetTouchState();
        }

        // Check for interception.
        //记录当前事件能否被拦截
        final boolean intercepted;
        //如果当前为down事件  或者mFirstTouchTarget为null
        if (actionMasked == MotionEvent.ACTION_DOWN
                || mFirstTouchTarget != null) {
            //FLAG_DISALLOW_INTERCEPT这个标志很重要 当子View调用requestDisallowInterceptTouchEvent(boolean)方法时就是设置的mGroupFlags的属性
            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
            //这个disallowIntercept就是判断是否允许拦截除了down之外的事件 为什么是down事件 是因为在上一个判断中对mGroupFlags的标志进行重置所以在down事件中disallowIntercept永远为false
            //所以就会执行onInterceptTouchEvent()方法
            if (!disallowIntercept) {
                intercepted = onInterceptTouchEvent(ev);
                //onInterceptTouchEvent这个方法默认为false就是viewgroup默认不会拦截事件 
                ev.setAction(action); // restore action in case it was changed
            } else {
                //如果子View调用getParent().requestDisallowInterceptTouchEvent(true)就会执行下边语句,不在拦截事件
                intercepted = false;
            }
        } else {
            //这里默认就是mFirstTouchTarget为null,可以理解为没有子View能够分发此事件 所以 intercepted标志就变为true,
            intercepted = true;
        }
        if (intercepted || mFirstTouchTarget != null) {
            ev.setTargetAccessibilityFocus(false);
        }

        // 标识本次事件需不需要取消
        final boolean canceled = resetCancelNextUpFlag(this)
                || actionMasked == MotionEvent.ACTION_CANCEL;

        // 检查父View是否支持多点触控,即将多个TouchEvent分发给子View,
        // 通过setMotionEventSplittingEnabled()可以修改这个值。
        // FLAG_SPLIT_MOTION_EVENTS在3.0是默认为true的,即支持多点触控的分发
        final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
        TouchTarget newTouchTarget = null;
        boolean alreadyDispatchedToNewTouchTarget = false;
        //默认不拦截    
        if (!canceled && !intercepted) {
            // 检查TouchEvent是否可以触发View获取焦点,如果可以,查找本View中有没有获得焦点的子View,
            // 有就获取它,没有就为null
            View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
                    ? findChildWithAccessibilityFocus() : null;
            //如果当前为down事件
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE    //鼠标移动) {
                // 获取当前触摸手指在多点触控中的排序
                // 这个值可能因为有手指发生Down或Up而发生改变
                final int actionIndex = ev.getActionIndex(); // always 0 for down
                // 标识当前是那一个点的触摸事件
                //ev.getPointerId()此时获取到手指的Id,这个值在Down到Up这个过程中是不会改变的
                final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)        
                        : TouchTarget.ALL_POINTER_IDS;
                // 清理之前触摸事件中的目标
                removePointersFromTouchTargets(idBitsToAssign);

                final int childrenCount = mChildrenCount;
                if (newTouchTarget == null && childrenCount != 0) {
                    final float x = ev.getX(actionIndex);
                    final float y = ev.getY(actionIndex);
                
                    //5.0加入的 将所有子View放到集合中,按照添加顺序排序,但是受到Z轴影响 
                    //只有子View数量大于1,并且其中至少有一个子View的Z轴不为0,其实就是elevation属性大于0,它才不为null
                    // 7.0中,View的elevation默认为0
                    final ArrayList<View> preorderedList = buildTouchDispatchChildList();
                    final boolean customOrder = preorderedList == null
                            && isChildrenDrawingOrderEnabled();
                    final View[] children = mChildren;
                    //对Viewgroup的所有子View进行倒序遍历(为什么是倒序  是因为 默认最后添加的View在最上层,应该最优先得到事件)
                    for (int i = childrenCount - 1; i >= 0; i--) {
                        final int childIndex = getAndVerifyPreorderedIndex(
                                childrenCount, i, customOrder);
                        final View child = getAndVerifyPreorderedView(
                                preorderedList, children, childIndex);
                        // 如果当前已经有View获得焦点了,找到它。后面的触摸事件会优先传给它。
                        // 应该主要影响后面触摸点的Down事件
                        if (childWithAccessibilityFocus != null) {
                            if (childWithAccessibilityFocus != child) {
                                continue;
                            }
                            childWithAccessibilityFocus = null;
                            //// 找到后i设为最后一个View,强制结束for循环不再继续查找
                            i = childrenCount - 1;
                        }
                        //此方法判断子View是否可见&没有在执行动画
                        if (!canViewReceivePointerEvents(child)
                                    //判断触摸区域是否在View中
                                || !isTransformedTouchPointInView(x, y, child, null)) {
                            ev.setTargetAccessibilityFocus(false);
                            continue;
                        }
                        //找到了可以分发的View
                        //根据当前child查找是否已经记录在mFirstTouchTarget这个单链表中
                        newTouchTarget = getTouchTarget(child);
                        if (newTouchTarget != null) {
                            newTouchTarget.pointerIdBits |= idBitsToAssign;
                            break;
                        }
                        // 再次重置View
                        resetCancelNextUpFlag(child);
                        //将child传递到dispatchTransformedTouchEvent方法中如果传入的child为null会调用super.dispatchTouchEvent
                        // 否则会对想X,Y计算根据当前的View进行偏移然后调用child.dispatchTouchEvent方法将事件传递到child中
                        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                            // 记录这次TouchEvent按下的时间
                            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();
                            //将mFirstTouchTarget指向消费该事件的View
                            newTouchTarget = addTouchTarget(child, idBitsToAssign);
                            // 标记已经把事件分发给了newTouchTarget,退出子View遍历
                            alreadyDispatchedToNewTouchTarget = true;
                            break;
                        }
                        ev.setTargetAccessibilityFocus(false);
                    }
                    if (preorderedList != null) preorderedList.clear();
                }
                //这个只有在多点触控才会执行
                // newTouchTarget在不是Down事件,或者没有找到处理事件的View时是null
                // mFirstTouchTarget在Down事件时,如果找到了处理的View就不为null
                if (newTouchTarget == null && mFirstTouchTarget != null) {
                    // 直接让上次处理的View继续处理
                    newTouchTarget = mFirstTouchTarget;
                    while (newTouchTarget.next != null) {
                        newTouchTarget = newTouchTarget.next;
                    }
                    newTouchTarget.pointerIdBits |= idBitsToAssign;
                }
            //到这里 ACTION_DOWN的事件处理完毕
            }
        }

        //mFirstTouchTarget == null 表示没有能相应该事件的child,那么就调用父类(也就是View)的dispatchTouchEvent,如果在down事件中intercepted为true,则newTouchTarget也为null也会执行此方法
        if (mFirstTouchTarget == null) {
            handled = dispatchTransformedTouchEvent(ev, canceled, null,
                    TouchTarget.ALL_POINTER_IDS);
        } else {
            TouchTarget predecessor = null;
            TouchTarget target = mFirstTouchTarget;
            while (target != null) {
                final TouchTarget next = target.next;
                //表示在Down事件处理中,已经将这个事件交给newTouchTarget处理过了,就不重复处理了
                if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                    handled = true;
                //对Move Up事件的处理
                } else {
                    //cancelChild 为true 如果事件被父View拦截了,或者child原本被打上了暂时不可接收TouchEvent的标记PFLAG_CANCEL_NEXT_UP_EVENT
                    // 就给他给它发送取消事件
                    final boolean cancelChild = resetCancelNextUpFlag(target.child)
                            || intercepted;
                    //将Move Up等事件的分发给子View
                    if (dispatchTransformedTouchEvent(ev, cancelChild,
                            target.child, target.pointerIdBits)) {
                        handled = true;
                    }
                    //cancelChild为true 就会清空事件队列,这样后续事件就会被ViewGroup本身拦截掉
                    if (cancelChild) {
                        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) {
            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;
}

因为代码注释太多了,仔细分析可以看注释即可,简单分析就是

public boolean dispatchTouchEvent(MotionEvent ev){
    boolean concume = fasle ;
    //判断是否要拦截,
    if(onInterceptTouchEvent(ev)){
        //如果拦截就调用自身的onTouchEvent()事件。 ViewGroup的onTouchEvent是用的父类View的,自身并没有实现这个方法
        consume = onTouchEvent(ev)
     }else{
        //如果不拦截就调用child.dispatchTouchEvent再对事件分发给子View
        Consume = child.dispatchTouchEvent(ev);
    }
    return consume;
}

还需要注意一下下边的代码

            //FLAG_DISALLOW_INTERCEPT这个标志很重要 当子View调用requestDisallowInterceptTouchEvent(boolean)方法时就是设置的mGroupFlags的属性
            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
            //这个disallowIntercept就是判断是否允许拦截除了down之外的事件 为什么是down事件 是因为在上一个判断中对mGroupFlags的标志进行重置所以在down事件中disallowIntercept永远为false
            //所以就会执行onInterceptTouchEvent()方法
            if (!disallowIntercept) {
                intercepted = onInterceptTouchEvent(ev);
                //onInterceptTouchEvent这个方法默认为false就是viewgroup默认不会拦截事件 
                ev.setAction(action); // restore action in case it was changed
            } else {
                //如果子View调用getParent().requestDisallowInterceptTouchEvent(true)就会执行下边语句,不在拦截事件
                intercepted = false;
            }

再看一下dispatchTransformedTouchEvent这个方法

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
        View child, int desiredPointerIdBits) {
    final boolean handled;
     // 先记录原本的Action
    //如果cancel为true就会发送cancel事件,实际传递进来的事件就会被覆盖掉
    final int oldAction = event.getAction();
    if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
        // 可能过来的事件没有ACTION_CANCEL,如果希望取消的话,那么为事件添加取消标志。
        event.setAction(MotionEvent.ACTION_CANCEL);
        if (child == null) {
            // 如果没有子View了,调用View中的dispatchTouchEvent
            // 进而调用View的onTouch或者onTouchEvent方法,触发ACTION_CANCEL逻辑
            handled = super.dispatchTouchEvent(event);
        } else {
            // 如果有子View,将这个取消事件传递给子View
            handled = child.dispatchTouchEvent(event);
        }
        // 在设置回原本的Action
        // 此时TouchEvent的行为相当于没变
        // 但是却把该ViewGroup的
        event.setAction(oldAction);
        return handled;
    }
    // 获取触摸事件的触摸点id
    final int oldPointerIdBits = event.getPointerIdBits();
    // 看和期望的触摸点是不是一个
    final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
       if (newPointerIdBits == 0) {
        //不是
        return false;
    }
    final MotionEvent transformedEvent;
    if (newPointerIdBits == oldPointerIdBits) {
        if (child == null || child.hasIdentityMatrix()) {
            //这里边如果child == null 就会调用自身的super.dispatchTouchEvent方法
            if (child == null) {
                handled = super.dispatchTouchEvent(event);
            } else {
                //否则就会计算偏移量然后调用 就会调用child.dispatchTouchEvent方法进行分发
                final float offsetX = mScrollX - child.mLeft;
                final float offsetY = mScrollY - child.mTop;
                event.offsetLocation(offsetX, offsetY);
                handled = child.dispatchTouchEvent(event);
   
             // 恢复TouchEvent坐标到原来位置,避免影响后面的流
                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 handle;
}

再记录一下cancelAndClearTouchTargets这个方法

private void cancelAndClearTouchTargets(MotionEvent event) {
    // 如果触摸事件目标队列不为空才执行后面的逻辑
    if (mFirstTouchTarget != null) {
        boolean syntheticEvent = false;
        if (event == null) {
            final long now = SystemClock.uptimeMillis();
            //自己创建一个ACTION_CANCEL事件
            event = MotionEvent.obtain(now, now,
                    MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
            // 设置事件源类型为触摸屏幕
            event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
            // 标记一下,这是一个合成事件
            syntheticEvent = true;
        }
        // TouchTarget是一个链表结构,保存了事件传递的子一系列目标View
        for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
            // 检查View是否设置了暂时不在接收事件的标志位,如果有清除该标志位
            // 这样该View就能够接收下一次事件了。
            //这个标志位是PFLAG_CANCEL_NEXT_UP_EVENT 一个View如果有PFLAG_CANCEL_NEXT_UP_EVENT标志,表示它跟ViewGroup解除了绑定,通常会在调用ViewGroup#detachViewFromParent(View),很少用
            resetCancelNextUpFlag(target.child);
            // 将这个取消事件传给子View,给上一次接收事件流的子View发送模拟的ACTION_CANCEL事件,可以重置这些子View的触摸状态。比如取消它们的点击或者长按事件
            dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
        }
        // 清空触摸事件目标队列
        clearTouchTargets();
        if (syntheticEvent) {
            // 如果是合成事件,需要回收它
            event.recycle();
        }
    }
}

再学习一个类

TouchTarget是一个内部类 他是一个单向的链表,mFirstTouchTarget表示的就是头,他记录的就是能够处理child的TouchTarget。它内部有一个链表组成的TouchTarget对象池能够起到复用的机制
private static final class TouchTarget {
    //这个值表示最大个数,这个值也就决定了事件最多传递32层,所以当写一个layout的层级超过32的时候,子View就会收不到事件。
    private static final int MAX_RECYCLED = 32;
    private static final Object sRecycleLock = new Object[0];
    //这一个static的值。所有的ViewGroup对象共用一个这点很重要
    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() {
    }
    //获取一个可以复用的target
    public static TouchTarget obtain(@NonNull View child, int pointerIdBits) {
        if (child == null) {
            throw new IllegalArgumentException("child must be non-null");
        }

        final TouchTarget target;
        synchronized (sRecycleLock) {
            //如果第一次为null就会创建一个新的
            if (sRecycleBin == null) {
                target = new TouchTarget();
            } else {
                //这基本就是链表操作
                //会在sRecycleBin取一个出来,将对象传递出去
                target = sRecycleBin;
                sRecycleBin = target.next;
                //sRecycleBin 的count-1 ,因为移出去了一个
                 sRecycledCount--;
                target.next = null;
            }
        }
        target.child = child;
        target.pointerIdBits = pointerIdBits;
        return target;
}
    //对this进行回收操作,将它放进sRecycleBin的链表里
    public void recycle() {
        if (child == null) {
            throw new IllegalStateException("already recycled once");
        }
        //基本链表操作
        synchronized (sRecycleLock) {
            if (sRecycledCount < MAX_RECYCLED) {
                next = sRecycleBin;
                sRecycleBin = this;
                sRecycledCount += 1;
            } else {
                next = null;
            }
            child = null;
    }
}

总结:

public boolean dispatchTouchEvent(MotionEvent event) {
    if (event.isTargetAccessibilityFocus()) {
        if (!isAccessibilityFocusedViewOrHost()) {
            return false;
        }
        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
        //5.0内嵌滑动的处理
        stopNestedScroll();
    }
    //跟ViewGroup一样的判断
    if (onFilterTouchEventForSecurity(event)) {
        if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
            result = true;
        }
  
        ListenerInfo li = mListenerInfo;
        //优先执行li.mOnTouchListener.onTouch方法。
        if (li != null && li.mOnTouchListener != null
                && (mViewFlags & ENABLED_MASK) == ENABLED
                && li.mOnTouchListener.onTouch(this, event)) {
            result = true;
        }
        //如果执行li.mOnTouchListener.onTouch方法返回为true就不会再次执行onTouchEvent了这个需要注意
        if (!result && onTouchEvent(event)) {
            result = true;
        }
    }

    if (!result && mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
    }
    if (actionMasked == MotionEvent.ACTION_UP ||
            actionMasked == MotionEvent.ACTION_CANCEL ||
            (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
        stopNestedScroll();
    }

    return result;
}

可以看到

public boolean onTouchEvent(MotionEvent event) {
    final float x = event.getX();
    final float y = event.getY();
    final int viewFlags = mViewFlags;
    final int action = event.getAction();

    final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
            || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
            || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
    //如果ENABLED为false 则直接return
    if ((viewFlags & ENABLED_MASK) == DISABLED) {
        if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
            setPressed(false);
        }
        mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
        // A disabled view that is clickable still consumes the touch
        // events, it just doesn't respond to them.
        //如果ENABLED为false 则直接return 不在继续后续操作
        return clickable;
    }
    if (mTouchDelegate != null) {
        //如果有TouchDelegate则直接返回true
        if (mTouchDelegate.onTouchEvent(event)) {
            return true;
        }
    }

    if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
        switch (action) {
            case MotionEvent.ACTION_UP:
                mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                if ((viewFlags & TOOLTIP) == TOOLTIP) {
                    handleTooltipUp();
                }
                if (!clickable) {
                    removeTapCallback();
                    removeLongPressCallback();
                    mInContextButtonPress = false;
                    mHasPerformedLongPress = false;
                    mIgnoreNextUpEvent = false;
                    break;
                }
                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;
                    if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                        focusTaken = requestFocus();
                    }

                    if (prepressed) {
                     
                        setPressed(true, x, y);
                    }
                    //mHasPerformedLongPress 会有1中情况会true  只有当触摸了超过500ms并且onLongClick回调返回了true mHasPerformedLongPress就会true 可查看源码比较清晰,
                    //这种情况下就不会出发点击事件
                    //所以就会又2种情况 低于500ms会移除长按事件,并触发点击事件,超过500ms之后并且设置了长按监听会优先处理长按事件然后根据返回值来处理是否执行下边代码
                    if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                        // This is a tap, so remove the longpress check
                        //移除长按事件
                        removeLongPressCallback();

                        if (!focusTaken) {
                             //点击事件
                            if (mPerformClick == null) {
                                mPerformClick = new PerformClick();
                            }
                            if (!post(mPerformClick)) {
                                performClick();
                            }
                        }
                    }

                   // 省去若干代码

                    removeTapCallback();
                }
                mIgnoreNextUpEvent = false;
                break;

            case MotionEvent.ACTION_DOWN:
                if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN) {
                    mPrivateFlags3 |= PFLAG3_FINGER_DOWN;
                }
                //mHasPerformedLongPress 改成false
                mHasPerformedLongPress = false;

                if (!clickable) {
                    checkForLongClick(0, x, y);
                    break;
                }

                if (performButtonActionOnTouchDown(event)) {
                    break;
                }

                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) {
                    mPrivateFlags |= PFLAG_PREPRESSED;
                    if (mPendingCheckForTap == null) {
                        mPendingCheckForTap = new CheckForTap();
                    }
                    mPendingCheckForTap.x = event.getX();
                    mPendingCheckForTap.y = event.getY();
                    //发送一个100ms的点击任务
                    postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                    //mPendingCheckForTap 里边会发送一个500ms的长按事件的延迟任务 是一个CheckForLongPress的类
                } else {
                    // Not inside a scrolling container, so show the feedback right away
                    setPressed(true, x, y);
                    checkForLongClick(0, x, y);
                }
                break;

            case MotionEvent.ACTION_CANCEL:
                if (clickable) {
                    setPressed(false);
                }
                removeTapCallback();
                removeLongPressCallback();
                mInContextButtonPress = false;
                mHasPerformedLongPress = false;
                mIgnoreNextUpEvent = false;
                mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                break;

            case MotionEvent.ACTION_MOVE:
                if (clickable) {
                    drawableHotspotChanged(x, y);
                }

              ////省去若干代码
                break;
        }

        return true;
    }

    return false;
}

总结

上一篇下一篇

猜你喜欢

热点阅读