安卓事件分发框架

  1. dispatchTouchEvent
    1. 代码不长,重要的地方有两个,分别是调用mOnTouchListener.onTouch(this, event)和onTouchEvent(event)
    2. 12行默认返回false
    3. 29到34行:如果mOnTouchListener不为空,执行mOnTouchListener.onTouch(this, event)方法,这个方法就是public void setOnTouchListener(OnTouchListener l) {    getListenerInfo().mOnTouchListener = l;},如果onTouch(this, event)方法返回true,result=true
    4. 36到38,如果上面onTouch方法返回true,不会执行下面的onTouchEvent(event)方法,接下来看onTouchEvent方法
  2. onTouchEvent
    1. 这个代码稍微长一点,还是抓重点分析
    2. 代码中两次出现了下面的判断
      (((viewFlags & CLICKABLE) == CLICKABLE
              || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)       
      || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
      第一次:7到16行,状态为DISABLE即代码中调用view.setEnable(true)时,返回上面的结果
      第二次:23行到133行,即函数结束前的一个大括号,满足上面的条件返回true这里说明只要View满足clickable、longClickable和contextClickable(api23开始支持)的任何一项,onTouchEvent返回true,返回true意味着可以消费onTouchEvent事件,使得事件不再继续向上传递。
      举个例子:下面的代码onTouchEvent事件的
      case MotionEvent.ACTION_MOVE:不会执行,后面会说明原因。
      public class MyView extends TextView {
          protected static final String TAG "MyButton";
          public MyView(Context context) {
              super(context);
          }

          public MyView(Context context, AttributeSet attrs) {
              super(context, attrs);
          }

          public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
              super(context, attrs, defStyleAttr);
          }

          @Override
          public boolean onTouchEvent(MotionEvent event) {
              int action = event.getAction();
              switch (action)
              {
                  case MotionEvent.ACTION_DOWN:
                      Log.e(TAG"MyView onTouchEvent ACTION_DOWN");
                      break;
                  case MotionEvent.ACTION_MOVE:
                      Log.e(TAG"MyView onTouchEvent ACTION_MOVE");
                      break;
                  case MotionEvent.ACTION_UP:
                      Log.e(TAG"MyView onTouchEvent ACTION_UP");
                      break;
                  default:
                      break;
              }
              return super.onTouchEvent(event);
          }
      }
    3. 26到131行对event三种动作的解析,核心代码出来了
    4. 先说ACTION_DOWN,Touch的第一个动作计划司ACOIN_DOWN,80到105行.这里面设计三个动作分别是onclick,longclick和CheckForTap
      1. 88行,判断view是否在可以滑动的组件里面,例如:scrollview和listview
        public boolean isInScrollingContainer() {
            ViewParent p = getParent();
            while (p != null && p instanceof ViewGroup) {
                if (((ViewGroup) p).shouldDelayChildPressedState()) {
                    return true;
                }
                p = p.getParent();
            }
            return false;
        }
      2. 94到99行,如果发生touch事件的view在可以滑动的组件里面,执行postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());这个方法的意思是延迟 TAP_TIMEOUT(100毫秒)后,执行CheckForTap()方法,如下:
        private final class CheckForTap implements Runnable {
            public float x;
            public float y;

            @Override
            public void run() {
                mPrivateFlags &= ~PFLAG_PREPRESSED;
                setPressed(truexy);
                checkForLongClick(ViewConfiguration.getTapTimeout(), xy);
            }
        }
        三行代码:
        1. 设置状态mPrivateFlags为false,这个后面后用到
        2. 设置press状态
        3. checkForLongClick,longClick事件,注意这里的参数传的是ViewConfiguration.getTapTimeout(),  即检查滑动延迟的TAP_TIMEOUT(100毫秒) 这个函数下面详细说明
      3. 继续向下103行checkForLongClick(0, x, y)有调用了这个函数
        1. 很简单就是调用mOnLongClickListener.onLongClick方法,注意下面的代码做了对LONG_CLICKABLE的判断,也即是说如果设置了view.setOnLongClickListener(false)则无法执行onLongClick事件
          private void 
          checkForLongClick(int delayOffset, float x, float y) {
              if ((mViewFlags LONG_CLICKABLE) == LONG_CLICKABLE) {
                  mHasPerformedLongPress false;

                  if (mPendingCheckForLongPress == null) {
                      mPendingCheckForLongPress new CheckForLongPress();
                  }
                  mPendingCheckForLongPress.setAnchor(x, y);
                  mPendingCheckForLongPress.rememberWindowAttachCount();
                  postDelayed(mPendingCheckForLongPress,
                          ViewConfiguration.getLongPressTimeout() - delayOffset);
              }
          }
        2. onLongClick触发的条件是执行完ACTION_DOWN动作后500毫秒,这里的两个动作都采用了postDelay的方式执行,这里是为了方便ACTION_UP或ACTION_MOVE动作发生时,可以直接将未执行的延迟任务remove掉
      4. ACTION_DOWN动作说完了,总结一下就是:
        1. 如果执行ACTION_DOWN的view在可以滑动的组件内,犹豫不能确定是不是滑动作,给了一个100毫秒的延迟动作
        2. 延迟500毫秒执行longClick事件
    5. 27到78行下面看最复杂的ACTION_UP事件
      1. 注意看33行的focusTaken判断,再看50行,连起来就是如果view可以获得焦点并且已经获得了焦点则不能再执行click事件(例如edittext不能执行onClick)
      2. 51到53说明了即使是立即执行的onClick方法也推荐使用post,目的就是使view点击后视图状态先发生变化,真是煞费苦心啊
      3. 45到47行,removeLongPressCallback();此时如果postDelay500ms的longClock事件没有发生,将其remove掉
    6. 剩下的两个时间ACTION_CANCEL和ACTION_MOVE两个事件比较简单,做了一些动作处理,设置Press状态,移除longClick和TapCallBack动作
    7. 至此onTouchEvent解析完毕,有点罗嗦,总结一下
      1. 代码中setEnable和setClickable的作用相信很多人都曾困扰过,setEnable为false不能点击,setLongClickable为false不能长按,setClickable为false对onClick事件没有直接影响,就是名字唬人而已
      2. View的onClickable、onLongClickable,onContextClickable任一成立,该View会消费此onTouchEvent事件,这个非常重要
      3. longClick事件在TOUCH_DOWN中连续摁下500ms执行,onClick在TOUCH_UP中执行,两者可以同时执行,longClick返回false时。处于焦点状态的View不能执行onClick事件
  3. 未完待续
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值