Android事件分发

来源:互联网 发布:工业造型设计书籍知乎 编辑:程序博客网 时间:2024/06/06 14:00

郭霖的专栏:http://blog.csdn.net/guolin_blog/article/details/9097463
Carson_Ho的博客:http://blog.csdn.net/carson_ho/article/details/54136311

事件分发

1.基础知识

1.1事件分发的对象

  1. 答:事件
  2. 任何事件列都是以DOWN事件开始,UP事件结束,中间有无数的MOVE事件
  3. 事件列:手指从接触屏幕到离开屏幕,产生的一些列事件

1.2事件分发的本质

  1. 将点击时间像某个View进行传递并最终得到处理
  2. 传递的过程就是分发过程

1.3事件在哪些对象之间进行传递?

  1. 答:Activity、ViewGroup、View
  2. 一个点击事件产生后,传递顺序是:Activity(Window) -> ViewGroup -> View

1.4事件分发过程由哪些方法协作完成?

  1. 答:dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent()

1.5总结

分发机制本质要解决:点击事件由哪个对象发出,经过哪些对象,最终达到哪个对象并最终得到处理。

源码解析

View

public boolean dispatchTouchEvent(MotionEvent event) {      if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&              mOnTouchListener.onTouch(this, event)) {          return true;      }      return onTouchEvent(event);  }

该方法首先是进入一个if判断块,判断的第一个条件mOnTouchListener赋值的地方是在:

public void setOnTouchListener(OnTouchListener l) {      mOnTouchListener = l;  }  

即当我们给控件注册了OnTouchListener时,该变量就不为null.

第二个条件(mViewFlags & ENABLED_MASK) == ENABLED是判断控件是否enable

第三个条件mOnTouchListener.onTouch(this, event)是如果注册了Touch事件,就会去回调该函数,如果onTouch()方法返回值为true,直接在dispatchTouchEvent()返回true;否则往下执行该控件的onTouchEvent()方法:

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;  }

在第14行((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) 表示如果控件可点击,就会进入判断块内,若当前事件为MotionEvent.ACTION_UP,经过各种if判断,执行38行的performClick(),想必这个方法大家都十分熟悉,只要OnClickListener不为null,内部就调用onClick().

这里需要注意,如果你在执行ACTION_DOWN的时候返回了false,后面一系列其它的action就不会再得到执行了。简单的说,就是当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发后一个action。(这句话看不懂,有知道的小伙伴们解释一下吗?)

onTouch()与onTouchEvent()的区别

  1. onTouch()优先于onTouchEvent()执行,如果onTouch返回true,则onTouchEvent()得不到执行.onClick()放在在onTouchEvent()内部执行.
  2. 如果你有一个控件是非enable的,那么给它注册onTouch事件将永远得不到执行。对于这一类控件,如果我们想要监听它的touch事件,就必须通过在该控件中重写onTouchEvent方法来实现.

未完待续……

原创粉丝点击