View的事件传递源码详细解析

来源:互联网 发布:mac ifconfig 编辑:程序博客网 时间:2024/06/05 19:06

View的事件传递源码解析


View是Android中最小的基本的的UI的控件,当触摸事件传递到它的时候基本上就是最后一个处理事件的控件;它不同于ViewGroup,它没有其他的子类;并且View的事件传递的源代码是最好掌握的,所以在这里认真分析源代码,学习记录一下;源代码使用android2.2的源码,因为这个版本的View事件源代码最简单,最直接;很利于学习,而5.0的看了半天晕晕的,android从2.2到5.0的源代码逻辑机构不能变化太大,只能是重构优化很多了;所以从2.2开始了~~


dispatchTouchEvent

一个触摸事件传递到一个UI控件的时候,都是先传递到这个的UI控件的dispatchTouchEvent方法,进入到这个方法的源代码

    /**     * 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 (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&                mOnTouchListener.onTouch(this, event)) {            return true;        }        return onTouchEvent(event);    }

上面的源码注释是说:往下传递触摸屏幕事件到目标View,或者是传给这个View如果这个View是最终目标的话;

参数意思是要被分发下去的事件行为(就是这个触摸事件行为)

返回 如果这个事件被View处理了返回true ,否则返回false

第9行是一个if条件判断里面有三个条件,并且是并集,要全部成立就进入括号里面返回的是true;不会再往下执行代码,否则继续往下执行;

第13行是执行onTouchEvent方法:

先看看if的三个条件:

1:第一个条件 mOnTouchListener != null

mOnTouchListener是什么对象?它是触摸监听OnTouchListener的某个实现类的对象(OnTouchListener是抽象接口),是开发者我们自己注入的,也可从源代码里找到;

    /**     * Register a callback to be invoked when a touch event is sent to this view.     * @param l the touch listener to attach to this view     */    public void setOnTouchListener(OnTouchListener l) {        mOnTouchListener = l;    }
从这里,我们可以看到mOnTouchListener被赋值了,如果我们没有设置setOnTouchListener那么mOnTouchListener就是null了,if条件就不成立了

我们设置setOnTouchListener之后,就会就行第二个条件的判断 

2:(mViewFlags & ENABLED_MASK) == ENABLED

这个条件的意思是当前这个View是不是可用的,能够使用;

单独看每一个位运算符,也是心里不是很踏实,到底是不是,其实从源码里也是可以确认的:

    /**     * Returns the enabled status for this view. The interpretation of the     * enabled state varies by subclass.     *     * @return True if this view is enabled, false otherwise.     */    @ViewDebug.ExportedProperty    public boolean isEnabled() {        return (mViewFlags & ENABLED_MASK) == ENABLED;    }
这个方法使View判断View是不是可用的,里面的条件就是(mViewFlags & ENABLED_MASK) == ENABLED,可用返回true,不可用返回false

3:mOnTouchListener.onTouch(this, event) 

这个条件就是子类实现类调用子类实现的接口方法,虽然引用还是父类OnTouchListener(这里是多态的小运用)

我们看看OnTouchListener的接口,里面只有一个onTouch方法,

  /**     * Interface definition for a callback to be invoked when a touch event is     * dispatched to this view. The callback will be invoked before the touch     * event is given to the view.     */    public interface OnTouchListener {        /**         * Called when a touch event is dispatched to a view. This allows listeners to         * get a chance to respond before the target view.         *         * @param v The view the touch event has been dispatched to.         * @param event The MotionEvent object containing full information about         *        the event.         * @return True if the listener has consumed the event, false otherwise.         */        boolean onTouch(View v, MotionEvent event);    }
现在想想,如果子类实现了这个接口,不就得实现接口里面的方法吗?,而恰好这个方法返回的是一个布尔类型的变量值;

如果我们实现了OnTouchListener接口,并且方法返回值为true,那么这个条件就成立了;

总结一个 这个if的小条件 成立有三个条件:

第一:设置了setOnTouchListener ,那么mOnTouchListener就不为null

第二:这个View本身是可以使用的

第三:实现的mOnTouchListener对象中的onTouch方法返回true ,

否则 执行下面的 onTouchEvent方法

onTouchEvent

下面在逐步分析onTouchEvent方法的内部执行:

这个方法比较长,这样也好,就不用我们找来找去了^-^,贴一下代码

 /**     * Implement this method to handle touch screen motion events.     *     * @param event The motion event.     * @return True if the event was handled, false otherwise.     */    public boolean onTouchEvent(MotionEvent event) {        final int viewFlags = mViewFlags;        if ((viewFlags & ENABLED_MASK) == DISABLED) {            // A disabled view that is clickable still consumes the touch            // events, it just doesn't respond to them.            return (((viewFlags & CLICKABLE) == CLICKABLE ||                    (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));        }        if (mTouchDelegate != null) {            if (mTouchDelegate.onTouchEvent(event)) {                return true;            }        }        if (((viewFlags & CLICKABLE) == CLICKABLE ||                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {            switch (event.getAction()) {                case MotionEvent.ACTION_UP:                    boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;                    if ((mPrivateFlags & 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 (!mHasPerformedLongPress) {                            // 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)) {                                    performClick();                                }                            }                        }                        if (mUnsetPressedState == null) {                            mUnsetPressedState = new UnsetPressedState();                        }                        if (prepressed) {                            mPrivateFlags |= PRESSED;                            refreshDrawableState();                            postDelayed(mUnsetPressedState,                                    ViewConfiguration.getPressedStateDuration());                        } else if (!post(mUnsetPressedState)) {                            // If the post failed, unpress right now                            mUnsetPressedState.run();                        }                        removeTapCallback();                    }                    break;                case MotionEvent.ACTION_DOWN:                    if (mPendingCheckForTap == null) {                        mPendingCheckForTap = new CheckForTap();                    }                    mPrivateFlags |= PREPRESSED;                    mHasPerformedLongPress = false;                    postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());                    break;                case MotionEvent.ACTION_CANCEL:                    mPrivateFlags &= ~PRESSED;                    refreshDrawableState();                    removeTapCallback();                    break;                case MotionEvent.ACTION_MOVE:                    final int x = (int) event.getX();                    final int y = (int) event.getY();                    // Be lenient about moving outside of buttons                    int slop = mTouchSlop;                    if ((x < 0 - slop) || (x >= getWidth() + slop) ||                            (y < 0 - slop) || (y >= getHeight() + slop)) {                        // Outside button                        removeTapCallback();                        if ((mPrivateFlags & PRESSED) != 0) {                            // Remove any future long press/tap checks                            removeLongPressCallback();                            // Need to switch from pressed to not pressed                            mPrivateFlags &= ~PRESSED;                            refreshDrawableState();                        }                    }                    break;            }            return true;        }        return false;    }

(viewFlags & ENABLED_MASK) == DISABLED)

这段代码我们按照执行步骤一段段来分析吧,先看下面这段

        final int viewFlags = mViewFlags;        if ((viewFlags & ENABLED_MASK) == DISABLED) {            // A disabled view that is clickable still consumes the touch            // events, it just doesn't respond to them.            return (((viewFlags & CLICKABLE) == CLICKABLE ||                    (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));        }

对于这段代码 主要看if中的条件 (viewFlags & ENABLED_MASK) == DISABLED 这句意思是判断这个View是不是可用的 ,首先源代码里面 

/** * This view is disabled. Intrepretation varies by subclass. Use with * ENABLED_MASK when calling setFlags. {@hide} */static final int DISABLED = 0x00000020;/** * Mask for use with setFlags indicating bits used for indicating whether * this view is enabled {@hide} */static final int ENABLED_MASK = 0x00000020;

DISABLED和ENABLED_MASK都是不可变的final型int值;这里使用了位运算& ,转为二进制相同位置都是1的时候才是1

DISABLED 从16进制转化为2进制 是 0000 0000 0000 0000 0000 0000 0010 0000

ENABLED_MASK 转化为2进制 是     0000 0000 0000 0000 0000 0000 0010 0000

其实16进制到2进制 转化很简单 16进制的每一位自己可以转化成2进制 ,如果你还是很蒙,看看这篇介绍就懂了  二进制位运算积累备忘

上面viewFlags & ENABLED_MASK的位运算之后,只有2个结果 要么是0x00000020 就是 DISABLED ,要么是 0x0000000 就是 ENABLED,

 

/** * This view is enabled. Intrepretation varies by subclass. Use with * ENABLED_MASK when calling setFlags. {@hide} */static final int ENABLED = 0x00000000;

如果不可用的话,if条件成立 会返回 (((viewFlags & CLICKABLE) == CLICKABLE ||
                    (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));

这的意思可以点击或可以长按点击都会返回true ;至于二进制的位运算 和上面分析可不可用是一样一样的;另外这两个条件分别用于判断这个View是不是可点击和是不是可长点击 在源码中也是可以找到的,再贴一下源代码,这样看的更明白了:

  /**     * Indicates whether this view reacts to click events or not.     *     * @return true if the view is clickable, false otherwise     *     * @see #setClickable(boolean)     * @attr ref android.R.styleable#View_clickable     */    @ViewDebug.ExportedProperty    public boolean isClickable() {        return (mViewFlags & CLICKABLE) == CLICKABLE;    }    /**     * Indicates whether this view reacts to long click events or not.     *     * @return true if the view is long clickable, false otherwise     *     * @see #setLongClickable(boolean)     * @attr ref android.R.styleable#View_longClickable     */    public boolean isLongClickable() {        return (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE;    }

这里小结一下:如果一个View是不可用,但是只要它的可以点击或者是可长按点击的,它都返回true,也就是消耗了这次事件;

mTouchDelegate != null

接着再往下看 下一段源代码

        if (mTouchDelegate != null) {            if (mTouchDelegate.onTouchEvent(event)) {                return true;            }        }

这就源代码的意思是,这个View有没有设置触摸代理事件 ,如果mTouchDelegate不为null的话,就会进入这个条件内部

mTouchDelegate是怎么来的,还是 从源代码里面找

    /**     * Sets the TouchDelegate for this View.     */    public void setTouchDelegate(TouchDelegate delegate) {        mTouchDelegate = delegate;    }

使使用者在这里注入的,没有注入的话mTouchDeleagte这个对象就是null,上面的条件不成立了

public class TouchDelegate {

我们可以重写TouchDelegate里面的onTouchEvent方法,也可以不重写,因为这个类本身自己有实现onTouchEvent方法,
这个TouchDelegate唯一的构造如下:

    /**     * Constructor     *      * @param bounds Bounds in local coordinates of the containing view that should be mapped to     *        the delegate view     * @param delegateView The view that should receive motion events     */    public TouchDelegate(Rect bounds, View delegateView) {        mBounds = bounds;        mSlop = ViewConfiguration.get(delegateView.getContext()).getScaledTouchSlop();        mSlopBounds = new Rect(bounds);        mSlopBounds.inset(-mSlop, -mSlop);        mDelegateView = delegateView;    }

看注释的意思是:包含这个View的当前坐标的边界范围(尺寸)应该被映射到这个delegateView,,这个delegateView应该受到这个触摸事件,说白了,就是把当前View的触摸事件叫另一个View来处理,这里我们明白就可以了;不再往下深究了,不过View的子类中好像很少设置这个代理的,我们私下可以自己弄个代理看看效果;

((viewFlags & CLICKABLE) == CLICKABLE ||(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)

再往下看下一段源代码,又是一个大长段的 if条件语句,里面包含了一个switch语句,也就说,这要是可点击或者是可长按点击,这个结果返回true,事件一定是这个View消耗处理的,为了方便浏览,再贴一下源代码吧

        if (((viewFlags & CLICKABLE) == CLICKABLE ||                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {            switch (event.getAction()) {                case MotionEvent.ACTION_UP:                    boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;                    if ((mPrivateFlags & 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 (!mHasPerformedLongPress) {                            // 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)) {                                    performClick();                                }                            }                        }                        if (mUnsetPressedState == null) {                            mUnsetPressedState = new UnsetPressedState();                        }                        if (prepressed) {                            mPrivateFlags |= PRESSED;                            refreshDrawableState();                            postDelayed(mUnsetPressedState,                                    ViewConfiguration.getPressedStateDuration());                        } else if (!post(mUnsetPressedState)) {                            // If the post failed, unpress right now                            mUnsetPressedState.run();                        }                        removeTapCallback();                    }                    break;                case MotionEvent.ACTION_DOWN:                    if (mPendingCheckForTap == null) {                        mPendingCheckForTap = new CheckForTap();                    }                    mPrivateFlags |= PREPRESSED;                    mHasPerformedLongPress = false;                    postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());                    break;                case MotionEvent.ACTION_CANCEL:                    mPrivateFlags &= ~PRESSED;                    refreshDrawableState();                    removeTapCallback();                    break;                case MotionEvent.ACTION_MOVE:                    final int x = (int) event.getX();                    final int y = (int) event.getY();                    // Be lenient about moving outside of buttons                    int slop = mTouchSlop;                    if ((x < 0 - slop) || (x >= getWidth() + slop) ||                            (y < 0 - slop) || (y >= getHeight() + slop)) {                        // Outside button                        removeTapCallback();                        if ((mPrivateFlags & PRESSED) != 0) {                            // Remove any future long press/tap checks                            removeLongPressCallback();                            // Need to switch from pressed to not pressed                            mPrivateFlags &= ~PRESSED;                            refreshDrawableState();                        }                    }                    break;            }            return true;        }
可以看到return true; 不管你是哪一个行为 只要进入这个if,统统返回true;代表事件被这个View消费了;

现在已经是onTouchEvent方法的最后了,对于switch情形中的各种case ,我们一个一个啃,逐步分析,就按照

按下(MotionEvent.ACTION_DOWN) ,

移动(MotionEvent.ACTION_MOVE),

取消了(MotionEvent.ACTION_CANCEL),

抬起(MotionEvent.ACTION_UP)  这个事件的逻辑进行

MotionEvent.ACTION_DOWN

首先看按下:

                case MotionEvent.ACTION_DOWN:                    if (mPendingCheckForTap == null) {                        mPendingCheckForTap = new CheckForTap();                    }                    mPrivateFlags |= PREPRESSED;                    mHasPerformedLongPress = false;                    postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());                    break;

代码第2行 mPengdingCheckForTap是一个Runnable类型的对象,如果是null

代码第3行就会创建一个对象

代码第4行 是一个私有的flag标记变量mPrivateFlags ,mPrivateFlags |= PREPRESSED;这句的意思是标记为点击按下了的意思

代码第5行 是 mHasPerformedLongPress是一个布尔类型的变量,值为false,表示 还没有执行长按;

代码第6行是将任务mPengingCheckForTap加入到消息队列里面延迟执行 ,至于延迟多久 ViewConfiguration.getTapTimeout()获取的时间,源码中是115毫秒,这里是单击的时间,超过了115毫秒单击时间限定之后就执行里面的Runnable任务;

对于第4行代码,可能有疑惑到底是干嘛用的,这里我解释一下 ,View中有很多状态 ,比如 可不可以单击 可不可以双击 ,可以可以长按, 可不可以由焦点;其实每一种这些状态都对应2种 要么可以,要么不可以 反应到代码中控制的是2进制的某一个为是1和0, 1表示true 0表示不可以,说白了是通过二进制位运算来控制,因为2进制每一位是0或1,这样说可能还是有一点不清楚,下面举个例子演示一下 :

比如  mPrivateFlags |= PREPRESSED; 

 假设 mPrivateFlags 是任意一个值 转化成二进制以后 是  如下  :aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa

而从源码中可以知道PREPRESSED是一个常量值0x02000000    :0000 0010 0000 0000 0000 0000 0000 0000

mPrivateFlags |= PREPRESSED; 的结果是 :                            :aaaa aa1a aaaa  aaaa aaaa aaaa aaaa aaaa 

可以看到mPrivateFlags的结果中除了1的那个位置其他位置都是保持不变的 ,这里变成了1 ,可以理解为 点击按下了的意思

~PREPRESSED                                                                    :1111 1101 1111 1111 1111 1111 1111 1111  

mPrivateFlags &= ~PREPRESSED;                                          :aaaa aa0a aaaa aaaa aaaa aaaa aaaa aaaa
可以看到mPrivateFlags的结果中除了0的那个位置其他位置都是保持不变的 ,这里变成了10,可以理解为取消了点击按下了的意思

变量 mPrivateFlags 当 |PREPRESSED之后相应为值的值置为1,其他位置都是不变的,其实也不不用关心,因为只要这个位置是1,我们就知道它是的按下条件成立了

变量 mPrivateFlags 当 &~PREPRESSED之后相应为值的值置为0,其他位置都是不变的,其实也不不用关心,因为只要这个位置是0,我们就知道它是的按下条件不成立了

接下来,我们在看看mPendingCheckForTap中实现的run方法:

    private final class CheckForTap implements Runnable {        public void run() {            mPrivateFlags &= ~PREPRESSED;            mPrivateFlags |= PRESSED;            refreshDrawableState();            if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {                postCheckForLongClick(ViewConfiguration.getTapTimeout());            }        }    }

第3行取消了点击按下行为标记(上面已经解释了) ,因为按下事件超过了115毫秒,之后就不能再当单击按下行为来对待了;

第4行有标记成了长按下的行为;PRESSED也是一个final型的变量 它的存在的意义就是为了判断是不是长按下,基本原理也是通过二进制的某一位是0还是1来的

第5行更新背景

第6行,(mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE判断是不是可长按,这个行为如果我们在代码里面设置长按监听 或者是可以长按的话 就是可以的,没有的

话,这个条件就不成立;postCheckForLongClick的源代码如下

    private void postCheckForLongClick(int delayOffset) {        mHasPerformedLongPress = false;        if (mPendingCheckForLongPress == null) {            mPendingCheckForLongPress = new CheckForLongPress();        }        mPendingCheckForLongPress.rememberWindowAttachCount();        postDelayed(mPendingCheckForLongPress,                ViewConfiguration.getLongPressTimeout() - delayOffset);    }
这里其实最主要的就是 mPendingCheckForLongPress ,而ViewConfiguration.getLongPressTimeout()的值源码中是500毫秒,超过了500毫秒就会检查是不是长按了

    class CheckForLongPress implements Runnable {        private int mOriginalWindowAttachCount;        public void run() {            if (isPressed() && (mParent != null)                    && mOriginalWindowAttachCount == mWindowAttachCount) {                if (performLongClick()) {                    mHasPerformedLongPress = true;                }            }        }        public void rememberWindowAttachCount() {            mOriginalWindowAttachCount = mWindowAttachCount;        }    }
从 06行开始,isPressed()源码如下:

    public boolean isPressed() {        return (mPrivateFlags & PRESSED) == PRESSED;    }
看了吗?这个条件此时一定是true 

mParent!=null也一定是成立的 在任何View通过setContentView方法加入到Activity中的时候,一定有父View,

最后一个条件 mOriginalWindowAttachCount == mWindowAttachCount  看看上一个源码片段的 rememberWindowAttachCount方法就知道也一定是true

如果都成立那就执行performLongClick方法了

   public boolean performLongClick() {        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);        boolean handled = false;        if (mOnLongClickListener != null) {            handled = mOnLongClickListener.onLongClick(View.this);        }        if (!handled) {            handled = showContextMenu();        }        if (handled) {            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);        }        return handled;    }
看看第6行代码 我们自己实现的长按点击事件中onLongClick方法如果返回true 最后这个方法就返回true,如果返回false,这个方法最后就返回false

对于返回true,通过看上面的源代码很好理解,如果返回的是false ,这里我们也要分析一下为什么最终这个方法也返回的是false

 handled = showContextMenu();
主要是这行代码

    public boolean showContextMenu() {        return getParent().showContextMenuForChild(this);    }
getParent()一定是一个ViewGruop吧?那么去到ViewGruop中去看看这个方法

    public boolean showContextMenuForChild(View originalView) {        return mParent != null && mParent.showContextMenuForChild(originalView);    }
翻遍源码ViewGroup的所有子类都没有Overdide这个方法,那么 mParent!=null最后一定会是false,因为顶级的DecoerView没有父View;是不是!

这里长安监听的方法返回treu还是false对后面有什么影响?这里先保留,继续往后看源代码吧

MotionEvent.ACTION_MOVE

移动的时候的源代码

                case MotionEvent.ACTION_MOVE:                    final int x = (int) event.getX();                    final int y = (int) event.getY();                    // Be lenient about moving outside of buttons                    int slop = mTouchSlop;                    if ((x < 0 - slop) || (x >= getWidth() + slop) ||                            (y < 0 - slop) || (y >= getHeight() + slop)) {                        // Outside button                        removeTapCallback();                        if ((mPrivateFlags & PRESSED) != 0) {                            // Remove any future long press/tap checks                            removeLongPressCallback();                            // Need to switch from pressed to not pressed                            mPrivateFlags &= ~PRESSED;                            refreshDrawableState();                        }                    }                    break;
首先第6行mTouchSlop是系统所能识别的最小滑动距离,他是从哪里来的 在初始化的时候View获取的

    public View(Context context) {        mContext = context;        mResources = context != null ? context.getResources() : null;        mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED;        // Used for debug only        //++sInstanceCount;        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();    }

   /**     * @return Distance a touch can wander before we think the user is scrolling in pixels     */    public int getScaledTouchSlop() {        return mTouchSlop;    }
在我们认定用户是在滑动像素之后,一个触摸要移动的距离,就是被当做滑动的最小移动距离

再看那么if条件 意思是不是 触摸超过了View宽高的+系统所能识别的最小滑动距离,也就是被认为滑动到了View的外面去了,这个时候就会执行里面的取消单击,取消长按按下行为:

    /**     * Remove the tap detection timer.     */    private void removeTapCallback() {        if (mPendingCheckForTap != null) {            mPrivateFlags &= ~PREPRESSED;            removeCallbacks(mPendingCheckForTap);        }    }

上面是取消单击行为和任务,代码之后都有分析,不难看懂

    private void removeLongPressCallback() {        if (mPendingCheckForLongPress != null) {          removeCallbacks(mPendingCheckForLongPress);        }    }

取消长按任务,这个之后后面跟着取消了长按的标记

 MotionEvent.ACTION_CANCEL

取消的源代码

                case MotionEvent.ACTION_CANCEL:                    mPrivateFlags &= ~PRESSED;                    refreshDrawableState();                    removeTapCallback();                    break;

首先,我们要知道什么什么会收到这个事件MotionEvent.ACTION_CANCEL,上一个动作被父容器拦截的时候,子View会收到这样一个事件,比如用户一直在Moving,当某一个移动事件被父View拦截的时候,这个子View就会收到这个取消的事件;

分析代码 

第2行代码是取消长按按下的标记

第3行是刷新背景

第4行是取消单击任务和标记

这个很简单是吧;

MotionEvent.ACTION_UP

最后我们再来看一个抬起事件

                case MotionEvent.ACTION_UP:                    boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;                    if ((mPrivateFlags & 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 (!mHasPerformedLongPress) {                            // 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)) {                                    performClick();                                }                            }                        }                        if (mUnsetPressedState == null) {                            mUnsetPressedState = new UnsetPressedState();                        }                        if (prepressed) {                            mPrivateFlags |= PRESSED;                            refreshDrawableState();                            postDelayed(mUnsetPressedState,                                    ViewConfiguration.getPressedStateDuration());                        } else if (!post(mUnsetPressedState)) {                            // If the post failed, unpress right now                            mUnsetPressedState.run();                        }                        removeTapCallback();                    }                    break;

首先,如果用户滑动的时候事件没有被取消或者没有移动到View之外的区域,那么第3行的这个条件一定会成立;

7~9行代码 if条件的意思是 是否可获取焦点 是否触摸可以获取焦点 是否已经获取了焦点 

对于View本什么什么来说,这三个条件都是不成立的,这点可以在View初始化的时候通过获取的attr来看到,View的子类中

对于EditText来说这三个条件会成立,手指点击EditText的离开的时候,取得了焦点第三个条件就会成立;

成立了的话,通过requstFouse返回值就是true,否则的话,这个地方focusTaken 会一直是false;

11行代码mHasPerformedLongPress的这个值只有长按监听里面的 onLongClick方法返回的是true,这个值才是true;否则的话这个值就是false;

true的话这个条件不执行,用户的onLongClick方法执行了且返回了false,那么后续还是会执行以下单击监听事件,~~~

16行 会进入这个if条件 除非是EditText,

20~24行代码是 要执行点击监听事件

    private final class PerformClick implements Runnable {        public void run() {            performClick();        }    }
看23行如果这个任务呗投放到消息队列里面没有被执行,会返回值false,通过本地再执行一次,如果返回了true,那么24行代码就不会执行了;

29行以后的代码 主要就是刷新状态 和背景的内容,前面都有分析和解释,也是很容易看懂的;

好了,Veiw的事件传递的代码,就分析了一遍,希望对每一个看了这个源代码的人都有帮助;














0 0
原创粉丝点击