zoukankan      html  css  js  c++  java
  • 既然你已经不爱我了,那我们还是不要联系了

    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想扇自己两巴掌……

  • 相关阅读:
    SQLSERVER2000使用TSQL将数据导入ACCESS并压缩生成rar
    收回动态VHD的未使用空间
    如何在html中添加引用公共模块文件 bling
    C语言I博客作业02
    C语言I博客作业06
    C语言I博客作业05
    C语言I博客作业04
    C语言I博客作业03
    Kali 安装JAVA(来源:oschina)
    DHCP服务器内网攻击测试
  • 原文地址:https://www.cnblogs.com/chihane/p/4878326.html
Copyright © 2011-2022 走看看