Android事件分发之View(一)

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值