美文网首页
view事件分发源代码详解

view事件分发源代码详解

作者: 刘佳阔 | 来源:发表于2017-07-26 22:09 被阅读0次

    view事件分发

    1,首先 viewgroup并没有重写onTouchEvent 方法,而viewgroup是view 的子类,所以view的onTouchEvent 方法也是veiwgroup的方法

    2, view 没有onInterceptTouchEvent,方法,因为他不需要拦截事件,

    首先看下 onDispatchTouchEvent方法源代码

    public boolean dispatchTouchEvent(MotionEvent event) {

    //记录最后的处理结果

    boolean result = false;

    //这是一个验证事件完整性的一个类,一会会看到在本方法结束的地方也有这个类出现

    //防止event事件在分发过程中不一致,这个类还有记录的功能

     if (mInputEventConsistencyVerifier != null) {

     mInputEventConsistencyVerifier.onTouchEvent(event, 0);

     }

    //活动事件种类

    final int actionMasked = event.getActionMasked();

    //如果是down 就停止

    if (actionMasked == MotionEvent.ACTION_DOWN) {

    // Defensive cleanup for new gesture

    stopNestedScroll(); //停止view中嵌套的滑动

    }

    //view 为 enable,并且事件可以被scroll bar 处理掉时,返回true

    //表示事件被滑动所消耗掉了

    if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {

    result = true;

    }

    //这个 li中保存了各种我们为view设置的listener

    ListenerInfo li = mListenerInfo;

    //先调用OnTouchListener的 onTouch ,返回true表示事件被消耗了

    if (li != null && li.mOnTouchListener != null

    && (mViewFlags & ENABLED_MASK) == ENABLED

    && li.mOnTouchListener.onTouch(this, event)) {

    result = true;

    }

    //看到这里就知道,如果 onTouchListener的onTouch返回true,消耗掉了事件,就不会在回调

    //onTouchEvent了

    if (!result && onTouchEvent(event)) {

    result = true;

    }

    //表示事件没被处理,和之前的事件校验器相对应

    if (!result && mInputEventConsistencyVerifier != null) {

    mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);

    }

    //如果抬起事件和取消事件,及down事件却没有消耗掉, 同样都要取消掉滑动

    if (actionMasked == MotionEvent.ACTION_UP ||

    actionMasked == MotionEvent.ACTION_CANCEL ||

    (actionMasked == MotionEvent.ACTION_DOWN && !result)) {

    stopNestedScroll();

    }

    //分发结束

    return result;

    }

    接下来看 ontouchEvent()

    这里有几种状态 : click press longclick longpass

    click 和 press 由up down 组成

    longpass 由 up down 组成,但是两个事件之间有延时,一定不能有move

    longclick 由 up down move 组成, move可有可无

    public boolean onTouchEvent(MotionEvent event) {

    //获取event的坐标,事件类型,及view的状态标志 mViewFlags,它通过位运算存储不同的状态

    final float x = event.getX();

    final float y = event.getY();

    final int viewFlags = mViewFlags;

    final int action = event.getAction();

    //view处于disable状态,能接受到事件,但是没有任何处理...

    if ((viewFlags & ENABLED_MASK) == DISABLED) {

    if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {

    setPressed(false); //传入false 表示还原之前 pass 状态

    }

    // 一个 view 是 disable 且 clickable 仍然会消耗事件,只是没有任何响应

    return (((viewFlags & CLICKABLE) == CLICKABLE

    || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)

    || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);

    }

    if (mTouchDelegate != null) {

    //代理来理事件,会把事件交给调用代理的view

    //代理使view可以处理的事件的接触面积变大.但其实还是调用view自己的实际处理方法

    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_DOWN: //这里调换了源代码的顺序

    mHasPerformedLongPress = false; //是否执行长按事件

    //看是否view是butten类似的控件,能否消耗掉点击事件-不太明白

    if (performButtonActionOnTouchDown(event)) {

    break;

    }

    //判断是否在滚动容器中

    boolean isInScrollingContainer = isInScrollingContainer();

    if (isInScrollingContainer) {

    // //mPrivateFlags 设置为 PFLAG_PREPRESSED(pass的前状态,后边会用到)

    mPrivateFlags |= PFLAG_PREPRESSED;

    if (mPendingCheckForTap == null) {

    //异步执行pass状态,及延时设置longClick状态

    //longclick状态有他自己的延时时长,

    mPendingCheckForTap = new CheckForTap();

    }

    mPendingCheckForTap.x = event.getX();

    mPendingCheckForTap.y = event.getY();

    //延时设置pass及longclick状态.

    postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());

    } else { 不在滑动中,直接设置press 及longclick状态

    // Not inside a scrolling container, so show the feedback right away

    //不在滑动中,立刻设置为press状态,同时设置为longclick状态

    setPressed(true, x, y);

    //检查执行长按,longclick有他自己的延时时间

    checkForLongClick(0, x, y);

    }

    break;

    case MotionEvent.ACTION_CANCEL://重置所有状态

    setPressed(false); //还原之前的press状态

    removeTapCallback();//还原 轻按状态

    removeLongPressCallback();//还原longclick状态

    mInContextButtonPress = false;

    mHasPerformedLongPress = false;

    mIgnoreNextUpEvent = false;

    break;

    case MotionEvent.ACTION_MOVE:

    //提醒子view(如果有)触摸点坐标变换了

    drawableHotspotChanged(x, y);

    //移动到了按钮外边

    if (!pointInView(x, y, mTouchSlop)) {

    // 移除tap回调

    removeTapCallback();

    //如果view为press状态

    if ((mPrivateFlags & PFLAG_PRESSED) != 0) {

    //还原 longclick和 press 状态

    removeLongPressCallback();

    setPressed(false);

    }

    }

    case MotionEvent.ACTION_UP:

    //是否为 prepress 状态

    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;

    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {

    //press状态或者 prePass 状态

    boolean focusTaken = false;

    // view 获取焦点

    if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {

    focusTaken = requestFocus(); //重新获取焦点

    }

    //之前为prepress 在up时就变更为press

    if (prepressed) {

    setPressed(true, x, y);

    }

    //移除longPass 状态

    if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {

    removeLongPressCallback();

    if (!focusTaken) { //没有获取焦点 有可能表示view 不接受输入

    //异步发送一个点击事件,调用 clicklistener 的 onclick 方法

    if (mPerformClick == null) {

    mPerformClick = new PerformClick();

    }

    //

    if (!post(mPerformClick)) {

    //异步发送失败,则手动发送点击事件

    performClick(); //调用click 方法

    }

    }

    }

    //一个异步还原press的 runnable类

    if (mUnsetPressedState == null) {

    mUnsetPressedState = new UnsetPressedState();

    }

    //异步还原pass 状态,如果失败就手动调用

    if (prepressed) {

    postDelayed(mUnsetPressedState,

    ViewConfiguration.getPressedStateDuration());

    } else if (!post(mUnsetPressedState)) {

    // If the post failed, unpress right now

    mUnsetPressedState.run();

    }

    removeTapCallback(); //移除tap状态

    mIgnoreNextUpEvent = false;

    break;

    }

    return true;

    }

    return false;

    }

    相关文章

      网友评论

          本文标题:view事件分发源代码详解

          本文链接:https://www.haomeiwen.com/subject/bwqakxtx.html