onTouch期间不会触发MotionEvent.ACTION_CANCEL

时间:2013-10-17 03:13:00

标签: android ontouchlistener motionevent

我遇到这个问题,ACTION_CANCEL没有被触发,我已经在我的其他项目中实现了它并且工作正常。似乎ACTION_UP是在MotionEvent之后调用的唯一ACTION_DOWN。一旦我的手指不再在视野中或屏幕外,我想触发ACTION_CANCEL

示例场景:我在ACTION_DOWN上点击了一个LinearLayout btw视图,其背景更改为图像的“点击/暗淡”版本,并在触发ACTION_UP时其背景发生变化仅当手指在LinearLayout内时才返回默认图像。现在的问题是,当我按下它并将手指放在屏幕上,然后将我的手指拖到LinearLayout之外时,ACTION_UP仍会触发它不应该的位置。

这是我的代码:

    dimView.setOnTouchListener(new OnTouchListener() {
        public boolean onTouch(final View view,
                final MotionEvent motionEvent) {
            if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
                Log.d("TAG", "DOWN");
                return true;
            } else if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
                Log.d("TAG", "UP");
                return true;
            } else if (motionEvent.getAction() == MotionEvent.ACTION_CANCEL) {
                Log.d("TAG", "CANCEL");
                return true;
            }
            return false;
        }
    });

其中:dimView是一个LinearLayout

2 个答案:

答案 0 :(得分:12)

我已经调试了这个很长一段时间了,因为它非常随机地出现了很多困扰我。然后我在不同设备上测试了我的代码,并意识到Android实现了API实现的变化。 Android 4.1.2(在Galaxy Tab 2上测试)的预期行为和代码工作绝对正常,但您描述的错误可以在Nexus 7(Android 4.2)上看到。 显然,Android改变了API 17中处理MotionEvent的方式。

当错误未发生时,一个特殊情况是视图位于GroupLayout下的ScrollView。滚动时,ACTION_CANCEL会被触发。但是,当不能滚动时,错误仍然存​​在。

起初我尝试合并OnClickListenerOnTouchListener,以便最后一个只能处理动画,但无济于事。从父母调度事件也不起作用。

一种解决方法是捕获ACTION_MOVE事件并使用v.getX()v.getY()检查手指是否位于视图边界之外,并将它们与event.getX()event.getY()进行比较。全局布尔变量(isOutside)可用于存储最新信息。在启动ACTION_UP之前,您可以检查isOutside的最新状态,并相应地执行动画和操作。您也可以返回true或false,具体取决于您是否捕获了该事件。

更新:在这里挖了一下后我发现了这个解决方案: Android: Detect if user touches and drags out of button region?并编译了此代码。这个想法是相同的,除了它创建一个矩形并检查事件边界是否在视图的矩形内。

    someView.setOnTouchListener(new View.OnTouchListener() {

                private Rect rect;

                @Override
                public boolean onTouch(View v, MotionEvent event) {
                    Log.d(TAG,"Touched: "+event.getAction());
                    if (event.getAction() == MotionEvent.ACTION_DOWN) {
                        Log.d(TAG,"ACTION_DOWN");
                        animateImageButtonOnClick(v, event);
                        rect = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
                    }

                    if (event.getAction() == MotionEvent.ACTION_UP) {
                        Log.d(TAG,"ACTION_UP");
                        if (!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())) {
                            Log.d(TAG,"ACTION_UP - outside");
                            animateImageButtonOnRelease(v, event);
                        }  else {
                            Log.d(TAG,"ACTION_UP - inside");
                            // do your stuff here  
                        }
                    }

                    if(event.getAction() == MotionEvent.ACTION_MOVE){
                        if(!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())){
                            animateImageButtonOnReleaseWithLowDuration(v, event);
                        }
                    }

                    if (event.getAction() == MotionEvent.ACTION_CANCEL){
                        Log.d(TAG,"ACTION_CANCEL");
                        animateImageButtonOnRelease(v, event);
                        return true;
                    }

                    return true;
                }
            });

答案 1 :(得分:0)

Override dispatchTouchEvent method maybe deal with it;

dimView.setOnTouchListener(new OnTouchListener(){

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            // TODO Auto-generated method stub
            // to call super touch method
            return false;
        }
    });

@Override
public boolean dispatchTouchEvent(MotionEvent motionEvent) {
    // TODO Auto-generated method stub
    if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
        Log.d("TAG", "DOWN");
        return true;
    } else if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
        Log.d("TAG", "UP");
        return true;
    } else if (motionEvent.getAction() == MotionEvent.ACTION_CANCEL) {
        Log.d("TAG", "CANCEL");
        return true;
    }
    return super.dispatchTouchEvent(motionEvent);
}