Android事件分发之View(一)

来源:互联网 发布:剑三捏脸数据成女网盘 编辑:程序博客网 时间:2024/06/05 18:00
1.View的dispatchTouchEvent:
  1. /**
  2. * Pass the touch screen motion event down to the target view, or this
  3. * view if it is the target.
  4. *
  5. * @param event The motion event to be dispatched.
  6. * @return True if the event was handled by the view, false otherwise.
  7. */
  8. public boolean dispatchTouchEvent(MotionEvent event) {
  9. if (!onFilterTouchEventForSecurity(event)) {
  10. return false;
  11. }
  12. if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
  13. mOnTouchListener.onTouch(this, event)) {
  14. return true;
  15. }
  16. return onTouchEvent(event);
  17. }
上述代码首先判断mOnTouchListener不为null,并且view是enable的状态,然后 mOnTouchListener.onTouch(this, event)返回true,这三个条件如果都满足,直接return true ; 也就是下面的onTouchEvent(event)不会被执行了;

2、onTouchEvent

  1. public boolean onTouchEvent(MotionEvent event) {
  2. final int viewFlags = mViewFlags;
  3. if ((viewFlags & ENABLED_MASK) == DISABLED) {
  4. return (((viewFlags & CLICKABLE) == CLICKABLE ||
  5. (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
  6. }
  7. //如果设置了Touch代理,就交给代理来处理,mTouchDelegate默认是null
  8. if (mTouchDelegate != null) {
  9. if (mTouchDelegate.onTouchEvent(event)) {
  10. return true;
  11. }
  12. }
  13. //如果View是clickable或者longClickable的onTouchEvent就返回true, 否则返回false
  14. if (((viewFlags & CLICKABLE) == CLICKABLE ||
  15. (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
  16.    ……
  17. return true;
  18. }
  19. return false;
  20. }
以上基于2.2版本的SDK代码
一个clickable或者longclickable的View是一直消费Touch事件的,不管它是否enable。如果clickable或者longclickable,onTouchEvent会直接返回true,表示该view会直接消费TouchEvent事件。而一般的View既不是clickable也不是longclickable的(即不会消费Touch事件,只会执行ACTION_DOWN而不会执行ACTION_MOVE和ACTION_UP) Button是clickable的,可以消费Touch事件,但是我们可以通过setClickable()和setLongClickable()来设置View是否为clickable和longClickable。当然还可以通过重写View的onTouchEvent()方法来控制Touch事件的消费与否。

长按事件是在DOWN中执行的,点击事件是在UP中执行的。想要执行点击事件的前提是消费了ACTION_DOWN和ACTION_MOVE,并且没有设置OnLongClickListener的情况下,如设置了OnLongClickListener的情况,则必须使onLongClick()返回false

DOWN时
  1. case MotionEvent.ACTION_DOWN:
  2. if (mPendingCheckForTap == null) {
  3. mPendingCheckForTap = new CheckForTap();
  4. }
  5. mPrivateFlags |= PREPRESSED;
  6. mHasPerformedLongPress = false;
  7. postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
  8. break;
a、首先设置标志为PREPRESSED,设置mHasPerformedLongPress=false ;然后发出一个115ms后的mPendingCheckForTap;到达延时时间后执行CheckForTap
  1. private final class CheckForTap implements Runnable {
  2. public void run() {
  3. mPrivateFlags &= ~PREPRESSED;
  4. mPrivateFlags |= PRESSED;
  5. refreshDrawableState();
  6. if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
  7. //检测长按
  8. postCheckForLongClick(ViewConfiguration.getTapTimeout());
  9. }
  10. }
  11. }
b、如果115ms内没有触发UP,则将标志置为PRESSED,清除PREPRESSED标志,同时发出一个延时为500-115ms的,检测长按任务消息
  1. private void postCheckForLongClick(int delayOffset) {
  2. mHasPerformedLongPress = false;
  3. if (mPendingCheckForLongPress == null) {
  4. mPendingCheckForLongPress = new CheckForLongPress();
  5. }
  6. mPendingCheckForLongPress.rememberWindowAttachCount();
  7. postDelayed(mPendingCheckForLongPress,
  8. ViewConfiguration.getLongPressTimeout() - delayOffset);
  9. }

  1. class CheckForLongPress implements Runnable {
  2. private int mOriginalWindowAttachCount;
  3. public void run() {
  4. if (isPressed() && (mParent != null)
  5. && mOriginalWindowAttachCount == mWindowAttachCount) {
  6. if (performLongClick()) {
  7. mHasPerformedLongPress = true;
  8. }
  9. }
  10. }
c、如果500ms内(从DOWN触发开始算),则会触发LongClickListener:此时如果LongClickListener不为null,则会执行回调,8-10行,同时如果LongClickListener.onClick返回true,才把mHasPerformedLongPress设置为true;否则mHasPerformedLongPress依然为false;

MOVE时
  1. case MotionEvent.ACTION_MOVE:
  2. final int x = (int) event.getX();
  3. final int y = (int) event.getY();
  4. // Be lenient about moving outside of buttons
  5. int slop = mTouchSlop;
  6. if ((x < 0 - slop) || (x >= getWidth() + slop) ||
  7. (y < 0 - slop) || (y >= getHeight() + slop)) {
  8. // Outside button
  9. removeTapCallback();
  10. if ((mPrivateFlags & PRESSED) != 0) {
  11. // Remove any future long press/tap checks
  12. removeLongPressCallback();
  13. // Need to switch from pressed to not pressed
  14. mPrivateFlags &= ~PRESSED;
  15. refreshDrawableState();
  16. }
  17. }
  18. break;

主要就是检测用户是否划出控件,如果划出了:
115ms内,直接移除mPendingCheckForTap;
115ms后,则将标志中的PRESSED去除,同时移除长按的检查:removeLongPressCallback();

UP时
  1. case MotionEvent.ACTION_UP:
  2. boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;
  3. if ((mPrivateFlags & PRESSED) != 0 || prepressed) {
  4. // take focus if we don't have it already and we should in
  5. // touch mode.
  6. boolean focusTaken = false;
  7. if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
  8. focusTaken = requestFocus();
  9. }
  10. if (!mHasPerformedLongPress) {
  11. // This is a tap, so remove the longpress check
  12. removeLongPressCallback();
  13. // Only perform take click actions if we were in the pressed state
  14. if (!focusTaken) {
  15. // Use a Runnable and post this rather than calling
  16. // performClick directly. This lets other visual state
  17. // of the view update before click actions start.
  18. if (mPerformClick == null) {
  19. mPerformClick = new PerformClick();
  20. }
  21. if (!post(mPerformClick)) {
  22. performClick();
  23. }
  24. }
  25. }
  26. if (mUnsetPressedState == null) {
  27. mUnsetPressedState = new UnsetPressedState();
  28. }
  29. if (prepressed) {
  30. mPrivateFlags |= PRESSED;
  31. refreshDrawableState();
  32. postDelayed(mUnsetPressedState,
  33. ViewConfiguration.getPressedStateDuration());
  34. } else if (!post(mUnsetPressedState)) {
  35. // If the post failed, unpress right now
  36. mUnsetPressedState.run();
  37. }
  38. removeTapCallback();
  39. }
  40. break;

a、如果115ms内,触发UP,此时标志为PREPRESSED,则执行UnsetPressedState,setPressed(false);会把setPress转发下去,可以在View中复写dispatchSetPressed方法接收;
b、如果是115ms-500ms间,即长按还未发生,则首先移除长按检测,执行onClick回调;
c、如果是500ms以后,那么有两种情况:
i.设置了onLongClickListener,且onLongClickListener.onClick返回true,则点击事件OnClick事件无法触发;
ii.没有设置onLongClickListener或者onLongClickListener.onClick返回false,则点击事件OnClick事件依然可以触发(mHasPerformedLongPress为false,UP的时候执行28行的if语句,开始执行点击事件);
  1. if (!mHasPerformedLongPress) {
  2. removeLongPressCallback();
  3. if (!focusTaken) {
  4. if (mPerformClick == null) {
  5. mPerformClick = new PerformClick();
  6. }
  7. if (!post(mPerformClick)) {
  8. performClick();
  9. }
  10. }
  11. }
d、最后执行mUnsetPressedState.run(),将setPressed传递下去,然后将PRESSED标识去除;

2 0
原创粉丝点击