I thought pinch would always trigger MotionEvent.ACTION_POINTER_UP, but it happens only about 50% of the time (it varies a lot) for an app on at least two tested tablet devices. It appears to be random. I do not know how to pinch to make it happen consistently.
The code is very straightforward as the following overridden method of the activity in question:
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction())
{
...
case MotionEvent.ACTION_POINTER_UP:
Log.d("Debug", "ACTION_POINTER_UP triggered");
default:
break;
}
...
}
Could anyone shed some light on this?
You may change your code to use getActionMasked().
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getActionMasked())
{
//...
case MotionEvent.ACTION_POINTER_UP:
Log.d("Debug", "ACTION_POINTER_UP triggered");
default:
break;
}
//...
}
MotionEven getAction() reports an integer which consists of both ACTION_whatever and
the pointer index left-shifted by ACTION_POINTER_INDEX_SHIFT (8) bits.
Return the kind of action being performed. Consider using getActionMasked() and getActionIndex() to retrieve the separate masked action and pointer index.
Returns
The action, such as ACTION_DOWN or the combination of ACTION_POINTER_DOWN with a shifted pointer index.
getAction() can be quite confusing to work with in multi-touch situations. If you want to get the kind of action (ACTION_DOWN, ACTION_POINTER_DOWN, and so on), which your case most likely is, you may use getActionMasked() instead of getAction().
getActionMasked() always returns ACTION_whatever, without the pointer index bits.
getAction() returns (getActionIndex() << 8 | ACTION_whatever).
(pointer indexes are very counter-intuitive, with Android trying to re-use the lowest indices available giving strange behavior if you use them directly, but that's another story, here better use getActionMasked() and not worry about indices). This is numerically equal to getActionMasked() only when the pointer has index=0, therefore you will see the "random" behavior: As you complete each pinch "naturally",
sometimes you may have released pointer 0 first, then pointer 1 last,
you get 2 events that getAction() returns ACTION_POINTER_UP followed by 1<<8 | ACTION_UP respectively, and hits the case ACTION_POINTER_UP label on the first event.
The other time you may have released pointer 1 first, then pointer 0 last,
you get 2 events that getAction() return 1<<8 | ACTION_POINTER_UP followed by ACTION_UP.
There is no case label for these, all get down to the default.
If you use getActionMasked(), you get ACTION_POINTER_UP followed by ACTION_UP, in both cases consistently.
Related
Quick, probably simple, question. I have a view whose background is animating, and during that time, I want to disable the user from interacting with the view. My view is a FrameLayout, and I'm capturing touch events with onTouchEvent(). Solutions I've tried:
1) First setOnTouchListener(null), then setOnTouchListener(this). Problem is, my view only calls onTouchEvent(MotionEvent), and not onTouch(View, MotionEvent), so I can't pump through the Events there.
2) First setEnabled(false), then setEnabled(true). The source code says: A disabled view that is clickable still consumes the touch events, it just doesn't respond to them. Problem is, MotionEvents still get pumped through in onTouchEvent().
3) requestDisallowInterceptTouchEvent(false), then (true). This only handles touch events from the parent.
The solution I have working is using a boolean variable isAnimating, and checking the value of that in onTouchEvent(). I'd rather not do this, because it looks ugly to me and I'd rather use the API for it than reinvent the wheel, sooo... anyone got any ideas? Thanks.
Try:
setFocusable() and setFocusableInTouchMode()
Try:
#Override
public boolean onTouchEvent(MotionEvent event) {
//Check if event is on your view
if(event.getY()>view.getTop()&&event.getY()<view.getBottom()&&event.getX()>view.getLeft()&&event.getX()<view.getRight()){
//event occured inside your view
}
//here return super or as your logic prefer
return super.onTouchEvent(event);
}
I'm new to bitwise operator, I don't know what are they use for, but I know the following:
5 & 3 = 1
because
0101 & 0011 = 0001
So yeah, I understand that we multiply each bit pair like 1 x 0 = 0, 1 x 1 = 1 and 0 x 0 = 0
Now when it comes to coding, I found the following code for onTouchEvent:
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction() & MotionEvent.ACTION_MASK;
switch(action) {
case MotionEvent.ACTION_DOWN : {
break;
}
case MotionEvent.ACTION_MOVE : {
break;
}
case MotionEvent.ACTION_POINTER_DOWN : {
break;
}
case MotionEvent.ACTION_POINTER_UP : {
break;
}
case MotionEvent.ACTION_UP : {
break;
}
}
return true;
}
Ok firstable, the part int action = event.getAction() & MotionEvent.ACTION_MASK; what is the value of action after the operation, and what does it means? why not just simply use action = event.getAction() what's the meaning.
second, I've never seen the use of {} to define a code block for a case: is this specific because we're using the bitwise and operator or this is something totally normal and I happen to just notice that you can use them?
The reason the event is masked is to remove unrelated bits from the event. Performing bitwise-and with ACTION_MASK isolates only those particular bits. This is how you combine and test status flags that are packed into an integer. If you did not mask it, then if any other bit is set on the event, you would not be able to easily test your actions for equality.
What this is actually doing is reserving a small number of bits for an action, like having a 3-bit integer (or whatever it actually is). The mask just sets the relevant bits, so that all other bits are thrown away (ie are unimportant).
As for the braces in case statements, that's fine. You can put braces just about anywhere - it creates a a block-scope level, where any new variables you create inside that block will only exist in the block. In case statements this is quite normal, and prevents a variable in one case from "falling through" to subsequent cases. For example:
switch(foo)
{
case 0:
int i = 42;
// do something with i
break;
case 1:
// The variable i is still in scope here, which is often not
// intentional (but can sometimes be useful)
}
To stop i from falling through:
switch(foo)
{
case 0:
{
int i = 42;
// do something with i
}
break;
case 1:
// The variable i defined above is not available here.
}
In your case, the braces are superfluous, but are probably there because the person who wrote the code has adopted this practice into their coding style. Most likely they've been burned by an unwanted side-effect before, or their compiler emits warnings about variable fall-through and they've decided to just use braces all the time. Personally, I think it's cleaner to only use them when necessary.
The event that comes back from event.getAction() is actually an integer. It contains more than just information about the type of action apparently. It probably contains flags that describe the action in more detail. And by passing all that detail in through an int, it saves the use of a class object to represent the action itself.
The mask is bitwise for the first byte in the integer. Only the first two bytes out of the eight. And for comparison purposes, you need to remove the upper parts of the integer before comparing it against the actions, which are themselves only using the bottom byte of an integer.
The upper parts of the event integer are still useful for other purposes when checking against them. So the mask is 0x000000ff representing that you only want information related to the action type at the bottom and the entire event takes up the rest of it with other useful information.
I have a some small questions to do with the masking of touch events. I have read many things on here and elsewhere but am still a little confused with parts.
1) Is event.getActionMasked() same as event.getAction() & MotionEvent.ACTION_MASK (they appear to be on Samsung S2 and HTC Desire)
2) Will the two above commands give all the information and more than that event.getAction() alone will, or is it different.
3) I have written a peice of code which simple says where is being touch and with which pointerId, or if the pointer is not in use (only written for two touches at the moment). It seems to work correctly on the devices mention above, however I know how some devices can act very differently if not done absolutely correct. I am therefore wondering if the following is correct and will behave on all devices.
boolean pointer0down=false, pointer1down=false;
String st="", str0 ="", str1 ="";
public boolean onTouchEvent( MotionEvent event )
{
if (event.getActionMasked()==MotionEvent.ACTION_UP
||event.getActionMasked()==MotionEvent.ACTION_POINTER_UP
||event.getActionMasked()==MotionEvent.ACTION_CANCEL)
{
if (event.getPointerId((event.getActionIndex()& MotionEvent.ACTION_MASK))==0)
pointer0down=false;
if (event.getPointerId((event.getActionIndex()& MotionEvent.ACTION_MASK))==1)
pointer1down=false;
}
if (event.getActionMasked()==MotionEvent.ACTION_DOWN
||event.getActionMasked()==MotionEvent.ACTION_POINTER_DOWN)
{
if (event.getPointerId((event.getActionIndex()& MotionEvent.ACTION_MASK))==0)
pointer0down=true;
if (event.getPointerId((event.getActionIndex()& MotionEvent.ACTION_MASK))==1)
pointer1down=true;
}
if (pointer0down)
str0="\tx: " + event.getX(0) + "\ty: "+event.getY(0);
else
str0="\tNot down";
if (pointer1down )
str1="\tx: " + event.getX(1) + "\ty: "+event.getY(1);
else
str1="\tNot down";
st="pointer0:"+str0+"\npointer1:"+str1;
return true;
}
Some background -
The integer returned from getAction is a packed integer that encodes both the action code (e.g ACTION_DOWN) and pointer index (e.g which finger).
For single touch, since there is only one finger involved there is no need to store the pointer index so the value is just the action code. Hence its safe to do something like getAction() == ACTION_DOWN
For multi touch, this comparison is incorrect b/c you have the extra pointer index in the packaged integer. You can extract the action code by doing getAction() & ACTION_MASK and extract the pointer index by doing getAction() & ACTION_POINTER_INDEX_MASK
Now to your question -
1: Yes. think of getActionMasked as a helper method that extract the action code bits. To extract the pointer index you have the equivalent getActionIndex helper method. Both these methods will work for single or multi touch hence they are the "safest".
2: As explained above, getAction also encodes pointer index for multitouch
3: there is a bug in your code. event.getPointerId((event.getActionIndex() & MotionEvent.ACTION_MASK)) is technically incorrect. Instead you should be doing event.getPointerId(event.getActionIndex()). You are getting very lucky with the incorrect logical and operator on getActionIndex which turns out to be 2 bytes and the mask is itself 2 bytes (0xff) :)
I would like to process all touch events at the container level. In other words, I want all touch events to reach the container level. Right now I am finding that TouchEvents only reach the containing ViewGroup if the location of the touch does not land on a UI Component that is contained by the view group. The contained UI Element processes the TouchEvent and it does not bubble up to the container. Does anyone know how to guarantee that all touch events reach the top level container?
Just to be clear, picture an activity with several buttons, several edit texts, and several check boxes. A typical form. Normally I am seeing that each UI component will catch the TouchEvent that lands on it and the container is none the wiser. So I want to know how the container/viewgroup could be informed of all touches that land anywhere within its region whether or not that region is occupied with a button, empty space or an edit text.
This is how I interpret your question:
You have View tree like this
ViewGroup G
View A
View B
...
and you want G to handle the MotionEvent created on touch even if it was consumed by any of A, B, ...
How to:
I'll give you two options. Depending on if you want to ignore consumption by
every child
only selected children
use one of the following templates:
Override dispatchTouchEvent() in G
// In G
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
super.dispatchTouchEvent(event);
// Handle event here instead of using an OnTouchListener
return true;
}
Override dispatchTouchEvent() in every child whose consumption should be ignored
Ignore some children
// In every child whose consumption should be ignored
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
super.dispatchTouchEvent(ev);
return false; // false means "I did not consume the event"
}
Don't forget to setup your listener
// In G, before touch should be recognized
setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// Handle event here
return true; // true means "I did consume the event"
}
});
Measured in lines of code, option 1 is a lot simpler. Option 2 is there just in case you need special treatment for some of A, B ...
I know it is late, but I think future readers might want to look at ViewGroups.onInterceptTouchEvent().
I have a seemingly simple question that I need help with. I have a button. I want it so when I click on the button quickly it adds one to a total. I also want it to be so that when I hold down this same button for about 2 seconds, it removes one from the total. The only part I am having trouble with is the motion event part. I have been experimenting with ACTION_UP and ACTION_DOWN with no luck. Is there an easy way to do this?
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
{
total ++;
return true;
}
case MotionEvent.ACTION_UP:
{
if(total >0){
total--;
}
return true;
}
}
Thanks for the help!
Why not use the Button's setOnClickListener and setOnLongClickListener?
The only limitation with this approach is that you cannot set the the timeout value, which is somewhat over 1,5 seconds, if I remember correctly.
If you're persistent about the two seconds (or some other value), then I suppose you could use an onTouchListener and keep track of the pressed time yourself. In that case, the MotionEvent's getDownTime might be of interest to you.