问题
Here is my very standard button onTouchListener:
b.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
b.setPressed(true);
}
if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
b.setPressed(false);
// Do the button action stuff here
}
return true;
}
});
When the user presses down on the button I set the button to "pressed". When the user lifts the finger I set the button to "not pressed" and then do the stuff the button is supposed to do.
This is all great except for the case where the user presses the button and then, while continuing to keep the finger down, moves the finger outside of the button area. When the finger is lifted the ACTION_UP stuff runs anyway. I would like for this behavior to act as a "cancel" of the button press. So, as soon as the pressed finger moves outside the button area I'd like to b.setPressed(false);
and then NOT do the button action stuff when the finger is raised.
I've tried using MotionEvent.ACTION_CANCEL
and MotionEvent.ACTION_OUTSIDE
(as suggested by How to detect when finger slides off the ImageButton?) but I must be missing something because they're not working.
From Detect when a user moves off of a button in Android it would appear that this can be done with event.getX()
and event.getY()
. If getX() is negative you're off the left and if getY() is negative you're off the top, but to figure out if you're off the right or bottom I'll have to figure out the size of the button and then... there has to be a simpler way. Is there?
回答1:
You had the right answer. Use getWidth()
and getHeight()
to get the width and height and check if x or y is greater.
ACTION_CANCEL
doesn't work because CANCEL
is generated when a parent view takes control of the touch events, like a ListView
does when scrolling.
ACTION_OUTSIDE
only happens in some unusual cases where you request it. It isn't going to be generated for just a normal MOVE
or UP
.
回答2:
Since no one posted any code, here is one. A Boolean is used to indicate when the respective actions should be performed.
The MotionEvent.ACTION_MOVE
event is handled to check if the finger is moved out of the bounds. If yes, b.setPressed(false);
is called and the boolean is used to indicate that MotionEvent.ACTION_UP
shouldn't be fired the next time, ie to cancel the button click.
And in MotionEvent.ACTION_UP
, call b.setPressed(false);
only if our boolean doesn't indicate a skip (as mentioned above). And if it does, invert the boolean so that button action stuff would execute the next time.
private Boolean notNow = false;
private Rect rect; // Variable rect to hold the bounds of the view
b.setOnTouchListener(new OnTouchListener(){
@Override
public boolean onTouch(View v, MotionEvent event){
if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN){
b.setPressed(true);
rect = new Rect(v.Left, v.Top, v.Right, v.Bottom);
}
if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP){
if (!notNow){
b.setPressed(false);
}
else //button press canceled
notNow = false;
}
if((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_MOVE){
if(!notNow)
if (!rect.contains(v.getLeft() + (int) event.getX(), v.getTop() + (int) event.getY())){
// finger moved out of bounds
b.setPressed(false);
notNow = true; //cancel button press the next time
}
}
return true;
});
来源:https://stackoverflow.com/questions/24710547/cancel-button-press-if-user-moves-finger-off-button