Android - How to distinguish first pointer and second pointer move in onTouchEvent - java

I want handle a gesture in my activity. To do this i have override the public boolean onTouchEvent(MotionEvent MEvent) method on my Activity. The content looks like this:
motionaction = MEvent.getAction();
if(motionaction == MotionEvent.ACTION_DOWN)
{
...
return true;
}
if(motionaction == MotionEvent.ACTION_UP)
{
...
return true;
}
if(motionaction == MotionEvent.ACTION_MOVE)
{
...
return true;
}
motionaction = MEvent.getActionMasked();
if(motionaction == MotionEvent.ACTION_POINTER_DOWN)
{
...
return true;
}
if(motionaction == MotionEvent.ACTION_POINTER_UP)
{
...
return true;
}
return true;
The gesture it's the follow:
-finger1 on the screen hold its position (virtually because there is always a little movement)
-finger2 move on the screen. This is the movement that i want to grab.
I can grab the 5 action but the problem it's that when two fingers are on the screen the ACTION_MOVE grabs the movement of both first and second finger. The method MEvent.getActionIndex() don't works for the ACTION_MOVE that is return always 0; The only thing that i can do it's to save the position of the finger1 and to discard the movement near to that point. The result it's not perfect indeed sometime the movement of the finger2 it's "tainted" by the little finger1 movement because though the finger holds it's position on the screen the listener feels each minimal movement.
How can i improve this ?

Referring to the answer of Quintin Balsdon:
I have a similar thing in my code. I save the position of the finger1 in the ACTION_DOWN case, then when the finger2 move i see if the Y coordinate of the move is over the saved finger1 Y coordinate. If so the movement it's referred to the finger2 otherwise is referred to finger1 and i discard that in two finger mode.
If i try to draw a circle on my view in one finger mode i have got something like this:
http://img208.imageshack.us/img208/8113/onefingercircle.jpg
If i try to draw a circle on my view in two finger mode i have got something like this:
http://img256.imageshack.us/img256/6778/twofingercircle.jpg
So in one finger mode it work perfectly but not in two finger mode.
I don't know if it's related to the phone multitouch handler or the touch screen. It can be only hardware related also or my misunderstanding of the API.

This is managed by you as the developer. You are going to have to create some boolean variable to set (to true) when the "DOWN" action takes place and then unset (false) when the "UP" or "MOVE" motionevent occurs. In the code below, I maintain the coordinates when the "DOWN" even happens and make adjustments while the user moves around.
switch (e.Action) //e is a MotionEvent type
{
case MotionEvent.ACTION_DOWN:
{
_prevx = e.getX();
_prevy = e.getY();
}
break;
case MotionEvent.ACTION_MOVE:
{
_xoffset += e.GetX() - _prevx;
_yoffset += e.GetY() - _prevy;
Invalidate();
_prevx = e.GetX();
_prevy = e.GetY();
}
break;
}
If you want to do multi-finger dragging you need to implement ScaleGestureDetector.OnScaleGestureListener (http://developer.android.com/reference/android/view/ScaleGestureDetector.OnScaleGestureListener.html)

Related

Gridview touchListener for only visible cells

I am making a game. I use GridView to display game blocks of various shapes ( tetris-like). So I have for example L block, T block etc.
When player touch and press on a block, he can move it anywhere on the screen. Each block is actualy a GridView, which has few cells colored and few invisible (depends on the block shape).
The problem occurs, when the blocks are near to each other, and you want to move a block which is "under" other blocks. Because of the gridView shape, touching at 1 or 2 position of the green block will not trigger green block touch listener, but blue (since the blue block was placed after the green on the layout, and you are touching inside its boundaries). So the player will start moving the blue block, but of course he wanted to move the green.
How can I fix this problem?
Edit:
I tried to set touch listener to every Blocks gridView child (don't mind empty cells at this moment), and pass the information to the methods I made. However, ACTION_DOWN works, but once I move my finger, the ACTION_UP is triggered (without any ACTION_MOVE).
void setBlockTouchListener(final Block block)
{
for (int i = 0; i < block.getChildCount(); i++)
{
block.getChildAt(i).setOnTouchListener(new View.OnTouchListener()
{
#Override
public boolean onTouch(View v, MotionEvent event)
{
final int X = (int) event.getRawX();
final int Y = (int) event.getRawY();
switch (event.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
Log.d("TAG", "child down");
onDown(block, X, Y, MotionEvent.ACTION_DOWN);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
Log.d("TAG", "child cancel");
onCancel(block, X, Y, MotionEvent.ACTION_CANCEL);
break;
case MotionEvent.ACTION_MOVE:
Log.d("TAG", "child move");
onMove(block, X, Y, MotionEvent.ACTION_MOVE);
break;
}
return true;
}
});
}
}
Logcat (when I try to touch and move the block):
child_down
child_cancel
Try calling requestDisallowInterceptTouchEvent after
case MotionEvent.ACTION_DOWN
Another view may be stealing the touch events. This will disallow the theft.
Block block = ((Block) v.getParent());
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
block.requestDisallowInterceptTouchEvent(true);
onDown(block, X, Y, MotionEvent.ACTION_DOWN);
break;
}

Getting current fingers position on Android

I'm developing an application that needs to get all fingers on the screen of the Android. That's not complex, and I could make it in a few lines of code.
However, the way android sends callbacks is not what I need. I have done many tests, and here is what I could find out:
Once he sent the fingers positions, if no "big" movement has been made, he will not send a onTouch event.
Also, when a finger is REMOVED from the screen (for example: there are 3 fingers, and one is removed), it seems that it ONLY sends the next event, if at least one of the remaining fingers move on the screen.
I'm doing some tracking on fingers, and matching with objects, and to do this properly, I need to know all the fingers positions all the time. If there is not a way to "request" finger's touch event even when it didn't moved, how can I access the current finger positions without an callback event? Is there any other solution?
Here is my code:
ArrayList<Vector3> fingerTips = new ArrayList<Vector3>();
#Override
public boolean onTouchEvent(MotionEvent event) {
final int points = event.getPointerCount();
String out = "==========\n";
fingerTips.clear();
for(int i = 0; i < points; i++){
out += "\tPoint "+i+"\t("+event.getX(i)+", "+event.getY(i) + ")\n";
fingerTips.add( new Vector3(event.getX(i), event.getY(i)) );
}
Log.i(TAG, out);
// Send touches to SurfaceView
chwaziViewGL.onUpdateFingerTips(fingerTips);
return true;
}
My problem was that I didn't received one event with all the finger positions AFTER removing a finger...
After lot's of tests and logging, I discovered that it's true (in part).
Whenever you REMOVE a finger on the SCREEN, the NEXT event sent is an ACTION_POINTER_UP or ACTION_UP, meaning that the finger x is no longer touching the screen.
Since there was NO motion after removing the finger, the last MotionEvent sent was the UP, containg the removed finger also.
So, to fix that, I check if the action is UP, and on the loop that get's all the fingers, I created an if checking if that was the finger removed from the screen. If so, I just didn't add it to the array.
Here is my final code:
ArrayList<Vector3> fingerTips = new ArrayList<Vector3>();
#Override
public boolean onTouchEvent(MotionEvent event) {
final int points = event.getPointerCount();
// Check if it's an event that a finger
// was removed, if so, set removedPoint
int removedPoint = -1;
final int action = event.getAction() & MotionEvent.ACTION_MASK;
if(action == MotionEvent.ACTION_POINTER_UP || action == MotionEvent.ACTION_UP)
removedPoint = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
String out = "==========\n";
fingerTips.clear();
for(int i = 0; i < points; i++){
// Find out pointer ID
int pointerID = event.getPointerId(i);
if(pointerID == MotionEvent.INVALID_POINTER_ID){
out += "\tPoint "+pointerID+" INVALID\n";
continue;
}
// Check if it's the removed finger
if(removedPoint == i){
out += "\tPoint "+pointerID+" REMOVED\n";
continue;
}
out += "\tPoint "+pointerID+"\t("+event.getX(i)+", "+event.getY(i) + ")\n";
fingerTips.add( new Vector3(event.getX(i), event.getY(i), pointerID) );
}
Log.i(TAG, out);
// Send touches to SurfaceView
chwaziViewGL.onUpdateFingerTips(fingerTips);
return true;
}
Sorry, but your code cost me a lot of hairs;-(( After days I found the problem finally before I got insane. This code
final int action = event.getAction() & MotionEvent.ACTION_MASK;
if(action == MotionEvent.ACTION_POINTER_UP || action == MotionEvent.ACTION_UP)
removedPoint = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
could have never been worked!!!
Why? Because if you mask the value that is coming from getAction() it is simply a plain integer value of the action. And when you try to use this plain value and masking it again with something, it has no meaning at all!
The correction is to use again event.getAction() in the seconds masking:
removedPoint = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
It was really a nightmare to find this. But anyway, I think you just wanted the best and help others:-))) Its ok.

selecting textViews and getting their values

I am trying to accomplish following things
Suppose I have 5-6 TextViews with values S N A K E
Now, I want to press S textView then slide my finger over N to select it too and then move my way to E. I want to do that so that i can get "SNAKE" in my string or char sequence etc
If there is any idea do share me. I can't get it how to use onTouch here.
Also, i am adding textViews dynamically so i am setting their ids dynamically too
Best Regards
I've never done this, but here's a thought.
Assuming all your textviews are in some sort of layout, you could disable the focusability on your textview and create an onTouch listener on your underlying container.
Start tracking on Mouse Down events
#Override public boolean onTouch(View v, MotionEvent event) {
if(event.getAction == MotionEvent.ACTION_DOWN)
{
captureInput = true;
}
}
*On your ACTION_MOVE event, check if the current position is above a textview, if so, capture that value*
#Override public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
if(action == MotionEvent.ACTION_DOWN)
{
captureInput = true;
}
else if(action == MotionEvent.ACTION_MOVE
{
//get x/y of your finger
int X = (int)event.getX();
int Y = (int)event.getY();
//Somehow check if the x,y are overlapping with one of your textivews
// If so, add that textview's text to your list
// Below is pseudo code
/* for(TextView curTV: allTextViews)
{
if(isOverlapping(X,Y))
{
listOfSwipedCharacters.add(curTV.getText());
}
}
}
}
Lastly, when the user releases their finger, stop tracking and do something with the word list
#Override public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
if(action == MotionEvent.ACTION_DOWN)
{
captureInput = true;
}
else if(action == MotionEvent.ACTION_MOVE
{
//get x/y of your finger
int X = (int)event.getX();
int Y = (int)event.getY();
//Somehow check which textview the X,Y coords are overlapping
// Below is pseudo code
/* for(TextView curTV: allTextViews)
{
if(isOverlapping(X,Y))
{
listOfSwipedCharacters += (curTV.getText());
}
}*/
}
else if(action == MotionEvent.ACTION_UP)
{
captureInput = false;
//Do whatever you want with the string of information that you've captured
}
}
Once again, I have no idea if that will work, it's just a guess at an approach. I suspect performance of cycling through each textview to check for overlaps will be pretty crappy, so there may be a more efficient approach than running a for loop.
Since you know you can't jump over letters when swyping, you could create a short list of all the possible swipe locations (surrounding letters) whenever you add a new letter. Then on your mouse event, check if any of those letters have been hit by the mouse move event. For example, in a standard word search game, once you select a letter, there are only 9 other possible locations for you to swipe (the surrounding letters), so you should only bother checking if those 9 locations have been hit when you capture the mouse move event.
Edit: I suppose you could use the solution above if you attached an onTouch listener to each and every textview. The same premise in regards to touch would apply: Down = start capture, move = capture text, up = stop capture. It would save you from having to identify which textview is being hovered as the textview that fires the onTouch event will be the one that the user is swiping across.

onTouch problem - x < y don't reacting

When I use the method onTouch, I use the methon event.getY() to give the variable y2 the finger precision. But one problem apper when I use a if-statement:
public boolean onTouch(View v, MotionEvent event){
if(y2 <= knifeY){
//y2 = where the finger is, knifeY = where on the height a knife will be on.
knifeDown = false;
// The knife stops
point--;
// score - 1;
knife = BitmapFactory.decodeResource(getResources(),R.drawable.bloddyknife);
// change picture.
return true;
}
}
But the problem is that you must move your finger when you hold it on the screen, if you're holding it still, the knife just slip past. :(
Someone help?
Do you assign int y2=event.getY() ? Because I dont see it anywhere, it must be done inside the onTouch()
I'm not sure if this is good approach
public boolean onTouch(View v, MotionEvent event)
{
while(event.getAction()!=MotionEvent.ACTION_CANCEL) // or here measure the pressure applied on the screen surface
{
if(event.getY() <= knifeY)
{
knifeDOwn=false; // keep the knife down
point--;
knife = BitmapFactory.decodeResource(getResources(),R.drawable.bloddyknife); //
//maybe here you might stop the pointers collecting with using event.setAction(MotionEvent.ACTION_CANCEL) (or ACTION_UP) to exit the while loop, i dont know your idea for it, at this point i'm just guessing, but you still need to change the action of the event at anypoint at this code to exit the while loop.
}
}
return true;
}
I suggest you to use GestureListener and use onFling() for this purpose.
Read here more on constants of MotionEvent class:
http://developer.android.com/reference/android/view/MotionEvent.html#ACTION_DOWN

Slide finger into buttons android

How can I make it possible that when someone slides their finger into or over a button, the button acts as if it were getting pushed?
An example of what i'm looking for would be a keyboard app. Where you slide your fingers over all the keys to play sounds.
You can use an OnTouchListener like this:
boolean firstTime = true;
OnTouchListener testTouchListener = new OnTouchListener(){
public boolean onTouch(View v, MotionEvent me){
Rect r = new Rect();
secondButton.getDrawingRect(r);
if(r.contains((int)me.getX(),(int)me.getY())){
//Log.i(myTag, "Moved to button 2");
if(firstTime == true){
firstTime = false;
secondButton.performClick();
}
}
if(me.getAction() == MotionEvent.ACTION_UP){
//When we lift finger reset the firstTime flag
firstTime = true;
}
return false;
}
};
firstButton.setOnTouchListener(testTouchListener);
With this approach though you are going to get a flood of touch events because onTouch() gets called a lot with MotionEvent.ACTION_MOVE. So you'll have to keep a boolean that tells you if its the first time you've gotten onTouch() call. Then you can reset that boolean in onTouch() for MotionEvent.ACTION_UP so it works again the next time. However this could get kind of complicated if you are trying to do more than just 2 buttons. I think you'd have to keep a boolean for each of them seperately (or perhaps an array of booleans to hold them all). and you'd need an additional if(r.contains(x,y) statements for every button. This should get you started on the right path though.

Categories