既然你已经不爱我了,那我们还是不要联系了

MotionEvent.ACTION_UP 可以当做点击事件的触发条件吗?

 

答案是不行。

 

许多人为了给自定义View添加点击事件也是想破了脑袋。

如何让自定义View既允许外部设置OnClickListener,又能保证在自定义View被点击时也执行一些其他的代码呢?

首先View里根本没有onClick之类的方法可以方便地重写,于是很多人就很自然地把视线转移到了onTouchEvent上来:

@Override
public boolean onTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_UP) {
        clicked();
    }
    return super.onTouchEvent(event);
}

把手拿起来的时候就是点击结束的时候嘛,我真是太机智了。

这段代码看起来一点问题都没有对不对?

 

对个大头鬼。

 

按下按钮之后你突然发现不能反悔了。就算把手指从按钮上移开,代码还是会跑到 ACTION_UP 下面去。

怎么回事,这跟说好的不一样啊?不是按下去之后移走就取消操作的么?我写了两个小时文档点叉之后不小心点到不保存的时候就是这招救了我的命的啊?

 

不说别的,如果点击事件真这么容易处理,Google程序员把

  1     public boolean onTouchEvent(MotionEvent event) {
  2         final float x = event.getX();
  3         final float y = event.getY();
  4         final int viewFlags = mViewFlags;
  5         final int action = event.getAction();
  6 
  7         if ((viewFlags & ENABLED_MASK) == DISABLED) {
  8             if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
  9                 setPressed(false);
 10             }
 11             // A disabled view that is clickable still consumes the touch
 12             // events, it just doesn't respond to them.
 13             return (((viewFlags & CLICKABLE) == CLICKABLE
 14                     || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
 15                     || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
 16         }
 17 
 18         if (mTouchDelegate != null) {
 19             if (mTouchDelegate.onTouchEvent(event)) {
 20                 return true;
 21             }
 22         }
 23 
 24         if (((viewFlags & CLICKABLE) == CLICKABLE ||
 25                 (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
 26                 (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
 27             switch (action) {
 28                 case MotionEvent.ACTION_UP:
 29                     boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
 30                     if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
 31                         // take focus if we don't have it already and we should in
 32                         // touch mode.
 33                         boolean focusTaken = false;
 34                         if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
 35                             focusTaken = requestFocus();
 36                         }
 37 
 38                         if (prepressed) {
 39                             // The button is being released before we actually
 40                             // showed it as pressed.  Make it show the pressed
 41                             // state now (before scheduling the click) to ensure
 42                             // the user sees it.
 43                             setPressed(true, x, y);
 44                        }
 45 
 46                         if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
 47                             // This is a tap, so remove the longpress check
 48                             removeLongPressCallback();
 49 
 50                             // Only perform take click actions if we were in the pressed state
 51                             if (!focusTaken) {
 52                                 // Use a Runnable and post this rather than calling
 53                                 // performClick directly. This lets other visual state
 54                                 // of the view update before click actions start.
 55                                 if (mPerformClick == null) {
 56                                     mPerformClick = new PerformClick();
 57                                 }
 58                                 if (!post(mPerformClick)) {
 59                                     performClick();
 60                                 }
 61                             }
 62                         }
 63 
 64                         if (mUnsetPressedState == null) {
 65                             mUnsetPressedState = new UnsetPressedState();
 66                         }
 67 
 68                         if (prepressed) {
 69                             postDelayed(mUnsetPressedState,
 70                                     ViewConfiguration.getPressedStateDuration());
 71                         } else if (!post(mUnsetPressedState)) {
 72                             // If the post failed, unpress right now
 73                             mUnsetPressedState.run();
 74                         }
 75 
 76                         removeTapCallback();
 77                     }
 78                     mIgnoreNextUpEvent = false;
 79                     break;
 80 
 81                 case MotionEvent.ACTION_DOWN:
 82                     mHasPerformedLongPress = false;
 83 
 84                     if (performButtonActionOnTouchDown(event)) {
 85                         break;
 86                     }
 87 
 88                     // Walk up the hierarchy to determine if we're inside a scrolling container.
 89                     boolean isInScrollingContainer = isInScrollingContainer();
 90 
 91                     // For views inside a scrolling container, delay the pressed feedback for
 92                     // a short period in case this is a scroll.
 93                     if (isInScrollingContainer) {
 94                         mPrivateFlags |= PFLAG_PREPRESSED;
 95                         if (mPendingCheckForTap == null) {
 96                             mPendingCheckForTap = new CheckForTap();
 97                         }
 98                         mPendingCheckForTap.x = event.getX();
 99                         mPendingCheckForTap.y = event.getY();
100                         postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
101                     } else {
102                         // Not inside a scrolling container, so show the feedback right away
103                         setPressed(true, x, y);
104                         checkForLongClick(0);
105                     }
106                     break;
107 
108                 case MotionEvent.ACTION_CANCEL:
109                     setPressed(false);
110                     removeTapCallback();
111                     removeLongPressCallback();
112                     mInContextButtonPress = false;
113                     mHasPerformedLongPress = false;
114                     mIgnoreNextUpEvent = false;
115                     break;
116 
117                 case MotionEvent.ACTION_MOVE:
118                     drawableHotspotChanged(x, y);
119 
120                     // Be lenient about moving outside of buttons
121                     if (!pointInView(x, y, mTouchSlop)) {
122                         // Outside button
123                         removeTapCallback();
124                         if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
125                             // Remove any future long press/tap checks
126                             removeLongPressCallback();
127 
128                             setPressed(false);
129                         }
130                     }
131                     break;
132             }
133 
134             return true;
135         }
136 
137         return false;
138     }
View.onTouchEvent()

写这么长是为了好玩吗。

悟空,回想一下SeekBar死前三天的样子吧。

拖动SeekBar的滑块时手指如果离开了SeekBar的范围会怎么样?

滑块还是会左右动的。

 

所以单纯把离开范围的所有事件直接扔掉显然是不可以的。

那么这个问题怎么处理呢?

 

聪(sàng)明(xīn)伶(bìng)俐(kuáng)的爆栈网网友想出了许多波谲云诡的技巧,比如:

 1         switch (event.getAction()) {
 2         case MotionEvent.ACTION_DOWN:
 3             rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
 4             return true;
 5         case MotionEvent.ACTION_UP:
 6             if (rect != null
 7                     && !rect.contains(v.getLeft() + (int) event.getX(),
 8                         v.getTop() + (int) event.getY())) {
 9                 // The motion event was outside of the view, handle this as a non-click event
10 
11                 return true;
12             }
13             // The view was clicked.
14             // TODO: do stuff
15             return true;
16         default:
17             return true;

 

实在是太有想法了,让人想上金正恩拍手图。

但是这个方法太不优雅了,高贵如我怎能用这种庶民级的work-around。

于是我花了一下午时间思考,终于得出了答案。

 

 1 public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
 2     ......
 3     super.setOnClickListener(this);
 4 }
 5 
 6 @Override
 7 public void setOnClickListener(final OnClickListener l) {
 8     super.setOnClickListener(new OnClickListener() {
 9         @Override
10         public void onClick(View v) {
11             CustomView.this.onClick(v);
12 
13             if (l != null) {
14                 l.onClick(v);
15             }
16         }
17     });
18 }
19 
20 @Override
21 public void onClick(View v) {
22     // Do something
23 }

 

 

……………………………………真TM想扇自己两巴掌……

posted @ 2015-10-14 18:36  Chihane  阅读(303)  评论(0编辑  收藏  举报