我遇到这个问题,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
答案 0 :(得分:12)
我已经调试了这个很长一段时间了,因为它非常随机地出现了很多困扰我。然后我在不同设备上测试了我的代码,并意识到Android实现了API实现的变化。 Android 4.1.2(在Galaxy Tab 2上测试)的预期行为和代码工作绝对正常,但您描述的错误可以在Nexus 7(Android 4.2)上看到。 显然,Android改变了API 17中处理MotionEvent的方式。
当错误未发生时,一个特殊情况是视图位于GroupLayout
下的ScrollView
。滚动时,ACTION_CANCEL
会被触发。但是,当不能滚动时,错误仍然存在。
起初我尝试合并OnClickListener
和OnTouchListener
,以便最后一个只能处理动画,但无济于事。从父母调度事件也不起作用。
一种解决方法是捕获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);
}