ViewGroup和View以及TouchDelegate的触摸事件源码简析

来源:互联网 发布:台湾中视直播软件 编辑:程序博客网 时间:2024/05/23 14:48

参考:

浅尝安卓事件分发机制

http://blog.csdn.net/zhaizu/article/details/50489398

深入Android开发之--理解View#onTouchEvent
https://my.oschina.net/banxi/blog/187267?p=1

Android 触摸消息处理

http://blog.csdn.net/siobhan/article/details/8257334

public boolean dispatchTouchEvent(MotionEvent ev) {    ......    final int action = ev.getAction();    final int actionMasked = action & MotionEvent.ACTION_MASK;    //down事件来时,清理之前的各种状态    if (actionMasked == MotionEvent.ACTION_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();    }    //ViewGroup节点是否要处理本次触摸事件,down事件或者mFirstTouchTarget不为空,则要检查子节点是否不允许父节点打断时间流程    final boolean intercepted;    if (actionMasked == MotionEvent.ACTION_DOWN            || mFirstTouchTarget != null) {        final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;        //子节点未设置不允许父节点打断流程,则调用onInterceptTouchEvent询问父节点是否要自己处理该事件        if (!disallowIntercept) {            intercepted = onInterceptTouchEvent(ev);            ev.setAction(action); // restore action in case it was changed        } else {            intercepted = false;        }    } else {        //当前不是down事件,并且在之前的down事件中,没有子节点来处理事件,所以move和up都由当前viewgroup来处理        intercepted = true;    }    //当前ViewGroup不处理该事件,且为down事件,则尝试找一个子节点处理    if (!canceled && !intercepted) {        if (actionMasked == MotionEvent.ACTION_DOWN) {            final int childrenCount = mChildrenCount;            if (newTouchTarget == null && childrenCount != 0) {                //找到一个触摸位置在该chile里的节点                final View[] children = mChildren;                for (int i = childrenCount - 1; i >= 0; i--) {                    final View child = children[i];                    if(dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)){                        mFirstTouchTarget = target;                        alreadyDispatchedToNewTouchTarget = true;                    }                }            }        }    }    //找不到处理该消息的子节点,则由当前节点处理    if (mFirstTouchTarget == null) {        // 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.        //由mFirstTouchTarget继续处理剩余事件        TouchTarget predecessor = null;        TouchTarget target = mFirstTouchTarget;        if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {            handled = true;        } else {            if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) {                handled = true;            }        }    }    return handled;}
public boolean onInterceptTouchEvent(MotionEvent ev) {    //默认都为false    return false;}//如果child为空,则由当前的ViewGroup调用它作为View的dispatchTouchEvent,否则调用child的dispatchTouchEventprivate boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,        View child, int desiredPointerIdBits) {    final boolean handled;    final MotionEvent transformedEvent = MotionEvent.obtain(event);    // Perform any necessary transformations and dispatch.    if (child == null) {        handled = super.dispatchTouchEvent(transformedEvent);    } else {        //坐标变换到child的空间        final float offsetX = mScrollX - child.mLeft;        final float offsetY = mScrollY - child.mTop;        transformedEvent.offsetLocation(offsetX, offsetY);        handled = child.dispatchTouchEvent(transformedEvent);    }    // Done.    transformedEvent.recycle();    return handled;}//是否可见,并且是否没有在播放动画private static boolean canViewReceivePointerEvents(@NonNull View child) {    return (child.mViewFlags & VISIBILITY_MASK) == VISIBLE            || child.getAnimation() != null;}protected boolean isTransformedTouchPointInView(float x, float y, View child,        PointF outLocalPoint) {    final float[] point = getTempPoint();    point[0] = x;    point[1] = y;    transformPointToViewLocal(point, child);    final boolean isInView = child.pointInView(point[0], point[1]);    if (isInView && outLocalPoint != null) {        outLocalPoint.set(point[0], point[1]);    }    return isInView;}public void transformPointToViewLocal(float[] point, View child) {    point[0] += mScrollX - child.mLeft;    point[1] += mScrollY - child.mTop;    if (!child.hasIdentityMatrix()) {        child.getInverseMatrix().mapPoints(point);    }}

//View的dispatchTouchEvent的主要作用就是调用mOnTouchListener的onTouch和onTouchEvent
public boolean dispatchTouchEvent(MotionEvent event) {    boolean result = false;    final int actionMasked = event.getActionMasked();    ListenerInfo li = mListenerInfo;    if (li != null && li.mOnTouchListener != null            && (mViewFlags & ENABLED_MASK) == ENABLED            && li.mOnTouchListener.onTouch(this, event)) {        result = true;    }    if (!result && onTouchEvent(event)) {        result = true;    }    return result;}//view自身处理事件,将点击事件的回调通过post来执行,保证了先看到UI的变化,再执行逻辑public boolean onTouchEvent(MotionEvent event) {    final float x = event.getX();    final float y = event.getY();    final int viewFlags = mViewFlags;    final int action = event.getAction();    if ((viewFlags & ENABLED_MASK) == DISABLED) {        if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {            setPressed(false);        }        // A disabled view that is clickable still consumes the touch        // events, it just doesn't respond to them.        //disabled的View如果可点击,依然会吞噬事件        return (((viewFlags & CLICKABLE) == CLICKABLE                || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)                || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);    }    //如果有view设置了代理,则由代理view处理事件    if (mTouchDelegate != null) {        if (mTouchDelegate.onTouchEvent(event)) {            return true;        }    }    if (((viewFlags & CLICKABLE) == CLICKABLE ||            (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||            (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {        switch (action) {            case MotionEvent.ACTION_UP:                boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;                if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {                    // 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)) {                        performClick();                    }                    removeTapCallback();                }                mIgnoreNextUpEvent = false;                break;            case MotionEvent.ACTION_DOWN:                mHasPerformedLongPress = false;                setPressed(true, x, y);                checkForLongClick(0, x, y);                break;            case MotionEvent.ACTION_CANCEL:                setPressed(false);                ......                break;            case MotionEvent.ACTION_MOVE:                drawableHotspotChanged(x, y);                // Be lenient about moving outside of buttons                //是否移出了View之外                if (!pointInView(x, y, mTouchSlop)) {                    // Outside button                    removeTapCallback();                    if ((mPrivateFlags & PFLAG_PRESSED) != 0) {                        // Remove any future long press/tap checks                        removeLongPressCallback();                        setPressed(false);                    }                }                break;        }        return true;    }    return false;}public boolean pointInView(float localX, float localY, float slop) {    return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&            localY < ((mBottom - mTop) + slop);}private final class PerformClick implements Runnable {    @Override    public void run() {        performClick();    }}public boolean performClick() {    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;    }    return result;}

//TouchDelegate类可以让一个view的点击区域变大,以方便点击//具体用法是,在当前view的父节点中设置TouchDelegate,传入当前view以及rect//rect的求法是view.getHitRect(rect),表示view在父节点中的范围,再根据需要手动扩大rectpublic class TouchDelegate {    //需要点击区域变大的那个view    private View mDelegateView;        //变大后的点击区域    private Rect mBounds;        //在mBounds区域的基础上又考虑了slop    private Rect mSlopBounds;        /**     * True if the delegate had been targeted on a down event (intersected mBounds).     */    //down事件是否在mSlopBounds区域内,表示可以传递给mDelegateView    private boolean mDelegateTargeted;    private int mSlop;    //bounds一般为delegateView.getHitRect(bounds)在扩大一部分,delegateView为最终接收点击事件的view,一般为当前节点的子节点    public TouchDelegate(Rect bounds, View delegateView) {        mBounds = bounds;        mSlop = ViewConfiguration.get(delegateView.getContext()).getScaledTouchSlop();        mSlopBounds = new Rect(bounds);        mSlopBounds.inset(-mSlop, -mSlop);        mDelegateView = delegateView;    }    //event为当前节点收到的事件,判断其是否在mBounds范围内,已决定是否传递给子节点delegateView    public boolean onTouchEvent(MotionEvent event) {        int x = (int)event.getX();        int y = (int)event.getY();        boolean sendToDelegate = false;        boolean hit = true;        boolean handled = false;        switch (event.getAction()) {        case MotionEvent.ACTION_DOWN:            Rect bounds = mBounds;            //点击位置在mBounds范围内,准备传递给子节点            if (bounds.contains(x, y)) {                mDelegateTargeted = true;                sendToDelegate = true;            }            break;        case MotionEvent.ACTION_UP:        case MotionEvent.ACTION_MOVE:            sendToDelegate = mDelegateTargeted;            if (sendToDelegate) {                Rect slopBounds = mSlopBounds;                if (!slopBounds.contains(x, y)) {                    hit = false;                }            }            break;        case MotionEvent.ACTION_CANCEL:            sendToDelegate = mDelegateTargeted;            mDelegateTargeted = false;            break;        }        if (sendToDelegate) {            final View delegateView = mDelegateView;                        if (hit) {                // Offset event coordinates to be inside the target view                //如果判定为在mBounds或mSlopBounds范围内,则设置点击位置为delegateView的中心                event.setLocation(delegateView.getWidth() / 2, delegateView.getHeight() / 2);            } else {                // Offset event coordinates to be outside the target view (in case it does                // something like tracking pressed state)                //未在范围内,则设定为一个负值                int slop = mSlop;                event.setLocation(-(slop * 2), -(slop * 2));            }            handled = delegateView.dispatchTouchEvent(event);        }        return handled;    }}
参考:
Android TouchDelegate   http://blog.csdn.net/a220315410/article/details/9141265
http://blog.csdn.net/tongcpp/article/details/23450975

                                             
0 0
原创粉丝点击