ScaleGestureDetector.onTouchEvent always returns 'true'
I thought that the method "onTouchEvent()" of a "ScaleGestureDetector" instance was supposed to return "true" only if it really handles the touch event, i. e. if it detects a multi-touch scaling gesture (with two fingers). Otherwise I tought that it was supposed to return "false" in order to let other handlers handle the event, e. g. a long press for triggering a context menu.
I observed something different: scaleGestureDetector.onTouchEvent() always returns "true" in my case. The following code snippet of my MyView class:
public boolean onTouchEvent(MotionEvent event) {
boolean retval = scaleGestureDetector.onTouchEvent(event);
Log.v("MyView.onTouchEvent()", "Action: " + event.getAction() +
"; PointerCount: " + event.getPointerCount() +
"; scaleGestureDetector.onTouchEvent() RetVal: " + retval);
return(retval);
}
produced the follwing log output after I touched the view for approx 1 second with one finger, then performed a scale gesture with 2 fingers:
01-01 19:09:54.484: VERBOSE/MyView.onTouchEvent()(5930): Action: 0; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:09:54.510: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:09:54.541: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:09:54.580: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:09:54.820: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:09:54.910: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:09:55.050: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:09:55.350: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:09:55.400: VERBOSE/MyView.onTouchEvent()(5930): Action: 1; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:09:57.160: INFO/BatteryStatsImpl(96): notePhoneSignalStrengthLocked: 4->3
01-01 19:10:00.060: ERROR/ClockWidget(215): weatherClock onReceive~ mUseAnimation:false
01-01 19:10:00.060: ERROR/ClockWidget(215): handleUiMessage~ in pause. msg:36867
01-01 19:10:00.070: ERROR/ClockWidget(215): weatherClock onReceive~ mUseAnimation:false
01-01 19:10:00.090: INFO开发者_StackOverflow/PI.Alarms(699): Update Alarms start
01-01 19:10:00.090: INFO/PI.Alarms(699): Task Notifications: Already displaying the same alarms, no update
01-01 19:10:00.100: INFO/PI.Alarms(699): Event Notifications: Already displaying the same alarms, no update
01-01 19:10:00.830: VERBOSE/MyView.onTouchEvent()(5930): Action: 0; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:00.840: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:00.870: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:00.900: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:00.922: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:00.931: VERBOSE/MyView.onTouchEvent()(5930): Action: 261; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:00.950: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.002: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.030: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.060: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.090: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.120: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.140: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.172: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.200: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.230: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.252: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.280: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.310: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.342: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.370: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.390: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.424: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.450: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.480: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.510: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.530: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.580: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.690: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.780: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.815: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.830: VERBOSE/MyView.onTouchEvent()(5930): Action: 262; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true
01-01 19:10:01.840: VERBOSE/MyView.onTouchEvent()(5930): Action: 1; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true
As I said: The return value is always "true"! Is this a bug of ScaleGestureDetector.onTouchEvent()? What can I do to let other handlers handle all non-scale-gesture events (e. g. long press with 1 finger)? Please help!
Nemax
Don't know if it's a bug or intentional, but that's definitely what the source does (ScaleGestureDetector.java:156):
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getAction();
boolean handled = true;
/* ... bunch of code that never updates 'handled' */
return handled;
}
The way I solved this was to check all of the other types of touch events I might want to handle first, then invoke the gesture detector, e.g.
if (mLongPressGestureDetector != null && mLongPressGestureDetector.onTouchEvent(event))
return true;
else if (mIsInMoveMode && mScaleGestureDetector != null) {
// Check for a move
if (action == MotionEvent.ACTION_MOVE && !mScaleGestureDetector.isInProgress()) {
handleMove(event);
return true;
}
// Now a scale
mScaleGestureDetector.onTouchEvent(event);
return true;
}
The ScaleGestureDetector provides the method isInProgress() that might do what you want...
Here is an example of it in use:
public boolean onTouch(View v, MotionEvent event) {
mScaleDetector.onTouchEvent(event);
if (!mScaleDetector.isInProgress()) {
if (event.getAction() == MotionEvent.ACTION_DOWN || (event.getAction() == MotionEvent.ACTION_MOVE)) {
touchX = (int) event.getX();
touchY = (int) event.getY();
isTouched = true;
}
if (event.getAction() == MotionEvent.ACTION_UP) {
isTouched = false;
}
} else {
isTouched = false;
}
return true;
}
This is how I solved the problem: By overriding the Acitivity's onDispatchTouchEvent() method. Any other solution seemed not to work. The good thing about the onDispatchTouchEvent() method is that is always called prior to forwarding any touch events to any other receiver, so you can intercept every single touch event here.
If the event is handled somewhere in here (scale or swipe), I return immediately without forwarding the event to the super class, i. e. to the rest of the view hierarchy. If it is not, I forward it to the super class, so other views can handle it, e. g. to detect short or long clicks.
There were a few more problems to solve though: 1. If the user begins a scale gesture, I had to cancel any long click detection processes because the receiving view would get the first DOWN event, then nothing anymore (after the second finger comes down and the scaling started), and then spuriously think that a long press is being performed. 2. When a long press was performed and the context menu came up, I had to prevent swipe and scale gesture detection here in dispatchOnTouchEvent() until the next UP event, otherwise swiping and scaling would be performed even though the context menu is there.
Quite complicated, but I spent hours and hours and a lot of trial and error, and just could not find any simpler solution. Anyway, handling 1. scale gestures, 2. horizontal swipe gestures, 3. vertical scroll gestures, 4. long clicks and 5. short clicks, all on the same target view(s), is not quite simple a mission to accomplish...
Here is the code (relevant parts of it):
@Override
public boolean dispatchTouchEvent(MotionEvent e) {
if (eventInProgress) {
// View shall only receive scale gesture event if visible
if (targetView.isShown())
scaleGestureDetector.onTouchEvent(e);
if (scaleGestureDetector.isInProgress())
motionEventConsumed = true;
}
if (motionEventConsumed) {
if (e.getAction() == MotionEvent.ACTION_UP)
motionEventConsumed = false;
if (cancelLongPressEvent) {
cancelLongPressEvent = false;
targetView.cancelLongPress();
}
return (true);
}
// Get the action that was done on this touch event
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN: {
// store the X value when the user's finger was pressed down
downXValue = e.getX();
downYValue = e.getY();
cancelLongPressEvent = true;
eventInProgress = true;
break;
}
case MotionEvent.ACTION_MOVE:
// When having moved by too many x or y pixels, then
// cancel any ongoing long klick events
if (cancelLongPressEvent
&& Math.abs(e.getX() - downXValue)
+ Math.abs(e.getY() - downYValue) > 40) {
targetView.cancelLongPress();
cancelLongPressEvent = false;
}
break;
case MotionEvent.ACTION_UP: {
if (eventInProgress) {
// Get the X value when the user released his/her finger
float deltaX = e.getX() - downXValue;
float deltaY = e.getY() - downYValue;
if (Math.abs(deltaX) > Math.abs(deltaY)
&& Math.abs(deltaX) > 50) {
// going backwards: pushing stuff to the right
if (deltaX > 0) {
flipRight();
return (true);
}
// going forwards: pushing stuff to the left
if (deltaX < 0) {
flipLeft();
return (true);
}
break;
}
}
}
}
// If event was not handled here, then forward it to parent,
// i. e. to view hierarchy
return (super.dispatchTouchEvent(e));
}
[...]
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
MenuInflater mi = getMenuInflater();
mi.inflate(R.menu.lztv_context_menu, menu);
contextMenuTargetView = v;
eventInProgress = false;
}
精彩评论