安卓-MotionEvent之ACTION_CANCEL

国外网站对于ACTION_CANCEL的解释为

You receive this when a parent takes possession of the motion, for example when the user has dragged enough across a list view or scroll view that it will start scrolling instead of letting you press the buttons inside of it.  

而在国内论坛,也看到了其他一些解释:

1 在设计设置页面的滑动开关时,如果不监听ACTION_CANCEL,在滑动到中间时,如果你手指上下移动,就是移动到开关控件之外,则此时会触发ACTION_CANCEL,而不是ACTION_UP,造成开关的按钮停顿在中间位置。

意思就是,当用户保持按下操作,并从你的控件转移到外层控件时,会触发ACTION_CANCEL,建议进行处理

ACTION_CANCEL事件,官方文档讲的是当前手势被释放,你将不会接收到其他的事件,应该向ACTION_UP一样对待它。

那到底什么情况会触发这个事件呢?当 当前控件(子控件,儿子)收到前驱事件(ACTION_DOWN或者ACTION_MOVE)后,它的父控件(老爸)突然插手,截断事件的传递,这时,当前控件就会收到ACTION_CANCEL,收到此事件后,不管子控件此时返回true或者false,都认为这一个动作已完成,不会再回传到父控件的OnTouchEvent中处理,同时后续事件,会通过dispatchEvent方法直接传送到父控件这里来处理。这和之前的结论有点相悖,之前说过如果子控件的OnTouchEvent返回false,表明事件未被处理,是回传到父控件去处理的,这里纠正一下,只有ACTION_DOWN事件才可以被回传,ACTION_MOVE和ACTION_UP事件会跟随ACTION_DOWN事件,即ACTION_DOWN是哪个控件处理的,后续事件都传递到这里,不会上抛到父控件,ACTION_CANCEL也不能回传。还有一点,触摸区域的范围问题,如果触摸区域在子控件内,同时父控件没有截断事件传递,刚不管子控件是否拦截此事件,都会传递到子控件的OnTouchEvent中处理,可以看成一种责任吧,因为我点的就是你,你父亲没有拦截,说明他不想处理,那到你这里了,不管你拦不拦截,都得你来处理。

结论:ACTION_CANCEL事件是收到前驱事件后,后续事件被父控件拦截的情况下产生,onTouchEvent的事件回传到父控件只会发生在ACTION_DOWN事件中

实例说明:在下面的实例中,LinearView1是父控件,LayoutView2是子控件,点击区域是LayoutView2,可以看到父控件的onInterceptTouchEvent方法在ACTION_DOWN的时候没有拦截事件,但是在ACTION_MOVE的时候突然插手,拦截掉事件,这时候,子控件就会收到ACTION_CANCEL。

public class LayoutView1 extends LinearLayout {  
  
    private final String TAG = "LayoutView1";  
  
    public LayoutView1(Context context, AttributeSet attrs) {  
        super(context, attrs);  
        Log.d(TAG, TAG);  
    }  
  
    @Override  
    public boolean onInterceptTouchEvent(MotionEvent ev) {  
        int action = ev.getAction();  
        switch (action) {  
        case MotionEvent.ACTION_DOWN:  
            Log.d(TAG, "1:onInterceptTouchEvent action:ACTION_DOWN");  
            //return true;  
            break;  
        case MotionEvent.ACTION_MOVE:  
            Log.d(TAG, "1:onInterceptTouchEvent action:ACTION_MOVE");  
            return true;  
            //break;  
        case MotionEvent.ACTION_UP:  
            Log.d(TAG, "1:onInterceptTouchEvent action:ACTION_UP");  
            //return true;  
            break;  
        case MotionEvent.ACTION_CANCEL:  
            Log.d(TAG, "1:onInterceptTouchEvent action:ACTION_CANCEL");  
            break;  
        }  
        return false;  
    }  
  
    @Override  
    public boolean onTouchEvent(MotionEvent ev) {  
        int action = ev.getAction();  
        switch (action) {  
        case MotionEvent.ACTION_DOWN:  
            Log.d(TAG, "1:onTouchEvent action:ACTION_DOWN");  
            break;  
        case MotionEvent.ACTION_MOVE:  
            Log.d(TAG, "1:onTouchEvent action:ACTION_MOVE");  
            break;  
        case MotionEvent.ACTION_UP:  
            Log.d(TAG, "1:onTouchEvent action:ACTION_UP");  
            break;  
        case MotionEvent.ACTION_CANCEL:  
            Log.d(TAG, "1:onTouchEvent action:ACTION_CANCEL");  
            break;  
        }  
        return true;  
    }  
}  

public class LayoutView2 extends LinearLayout {  
    private final String TAG = "LayoutView2";     
  
    public LayoutView2(Context context, AttributeSet attrs) {  
       super(context, attrs);  
       Log.d(TAG,TAG);  
    }  
  
    @Override  
    public boolean onInterceptTouchEvent(MotionEvent ev) {  
       int action = ev.getAction();  
       switch(action){  
       case MotionEvent.ACTION_DOWN:  
           Log.d(TAG,"2:onInterceptTouchEvent action:ACTION_DOWN");  
           break;  
           //return true;  
       case MotionEvent.ACTION_MOVE:  
           Log.d(TAG,"2:onInterceptTouchEvent action:ACTION_MOVE");  
           break;  
           //return true;  
       case MotionEvent.ACTION_UP:  
           Log.d(TAG,"2:onInterceptTouchEvent action:ACTION_UP");  
           break;  
       case MotionEvent.ACTION_CANCEL:  
           Log.d(TAG,"2:onInterceptTouchEvent action:ACTION_CANCEL");  
           break;  
       }    
       return false;  
}  
   
  
    @Override  
    public boolean onTouchEvent(MotionEvent ev) {  
       int action = ev.getAction();  
       switch(action){  
       case MotionEvent.ACTION_DOWN:  
           Log.d(TAG,"2:onTouchEvent action:ACTION_DOWN");  
           //return false;  
           break;  
       case MotionEvent.ACTION_MOVE:  
           Log.d(TAG,"2:onTouchEvent action:ACTION_MOVE");  
           return false;  
           //break;  
       case MotionEvent.ACTION_UP:  
           Log.d(TAG,"2:onTouchEvent action:ACTION_UP");  
           break;  
       case MotionEvent.ACTION_CANCEL:  
           Log.d(TAG,"2:onTouchEvent action:ACTION_CANCEL");  
           break;  
       }       
       return true;  
    }   
}  


package com.blog.demo11; import android.annotation.SuppressLint; import android.os.Bundle; import android.view.MotionEvent; import android.view.View; import android.widget.ImageView; import androidx.annotation.Nullable; import com.blog.BaseActivity; import com.blog.R; import static android.view.MotionEvent.INVALID_POINTER_ID; /** * 可拖拽效果类。 */ public class ViewDragActivity extends BaseActivity { private ImageView backgroundImage; private ImageView image1; private ImageView image2; @SuppressLint("MissingInflatedId") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.drag_layout); backgroundImage = findViewById(R.id.background_image); image1 = findViewById(R.id.image1); image2 = findViewById(R.id.image2); // Set touch listeners for each image image1.setOnTouchListener(new MultiTouchListener()); image2.setOnTouchListener(new MultiTouchListener()); } private class MultiTouchListener implements View.OnTouchListener { private float lastTouchX, lastTouchY; private int activePointerId = INVALID_POINTER_ID; @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: { final int pointerIndex = event.getActionIndex(); final float x = event.getX(pointerIndex); final float y = event.getY(pointerIndex); lastTouchX = x; lastTouchY = y; activePointerId = event.getPointerId(pointerIndex); break; } case MotionEvent.ACTION_MOVE: { final int pointerIndex = event.findPointerIndex(activePointerId); final float x = event.getX(pointerIndex); final float y = event.getY(pointerIndex); final float dx = x - lastTouchX; final float dy = y - lastTouchY; v.setX(v.getX() + dx); v.setY(v.getY() + dy); lastTouchX = x; lastTouchY = y; break; } case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { activePointerId = INVALID_POINTER_ID; break; } case MotionEvent.ACTION_POINTER_UP: { final int pointerIndex = event.getActionIndex(); final int pointerId = event.getPointerId(pointerIndex); if (pointerId == activePointerId) { final int newPointerIndex = pointerIndex == 0 ? 1 : 0; lastTouchX = event.getX(newPointerIndex); lastTouchY = event.getY(newPointerIndex); activePointerId = event.getPointerId(newPointerIndex); } break; } } return true; } } }
最新发布
06-04
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值