android: how to pinch zoom without triggering a click (ACTION_DOWN) - android

Currently I have a zoom implemented for my drawing application which works quite well. Just some lines so you know what I'm talking about:
setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent rawEvent) {
WrapMotionEvent event = WrapMotionEvent.wrap(rawEvent);
// Handle touch events here...
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
drawSomethingHere();
case MotionEvent.ACTION_POINTER_DOWN:
startZoomingModeHere();
...
So what I want is: draw with one finger, zoom with both fingers.
Problem is: there is always a ACTION_DOWN event triggered BEFORE the second finger hits the surface. So everytime I want to zoom, my app draws a point before the zoom mode is started.
Does someone has an idea how to solve this?

I think you should wait some milliseconds before starting draw, because it's impossible to put exactly two fingers at same millisecond.
You could have a boolean var to wait.
boolean boolean_pointer_down = false;
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
// Start a thread to call drawSomethingHere in 300 - 700 milliseconds
// if action_pointer_down haven’t been called.
new ThreadDraw().start();
case MotionEvent.ACTION_POINTER_DOWN:
boolean_pointer_down=true;
startZoomingModeHere();
....
//Method run in the thread
public void run(){
wait(300); //if the user didn't put more fingers in 300 ms he's not going to zoom
if (!boolean_pointer_down){
drawSomethingHere();
boolean_pointer_down = false;
}
}

Solved as follows:
Draw on ACTION_UP and ACTION_MOVE instead of ACTION_DOWN
On ACTION_MOVE don't draw immediately, put the points in a buffer instead (this will make sure that you don't interpret accidental single touch events before the multitouch event happens)
as soon as the buffer has 3 points, start the drawing mode
as soon as an ACTION_POINTER_DOWN event occurs (multitouch happened), clear the buffer and start zooming mode
This is the best I could do. The result is pretty convincing...

Related

Is there a offset between onTouchEvent and onTouchListener?

I have developed a game that shoots when player touches the screen by using onTouchListener for my custom SurfaceView and Thread.
But now I want to change the approach and instead of onTouchListener I added onTouchEvent to the SurfaceView o my Activity.
The problem is that I get some kind of offset when I click on the emulator.
Everything is working great, except that offset keeps appearing and I don't understand why.
Also let me mention that my app is running in landscape mode, maybe this is relevant.
I suspect that it isn't working properly because onTouchListener was added to the view and depended on it, but onTouchEvent doesn't depend on the view.
Also my view doesn't have any padding or margin properties, it is full-screen view (fill_parent).
Does anyone have any ideas on this?
I have done my application, and everything works correctly now, but i still do not know what the problem was.
After lots of debugging of my application the onTouchEvent returned random Y values that were always higher than the ones that the onTouchListener returned. And i am not sure why this is hapening since my view that recognizes the onTouchListener is a full-screen view.
So i figured out a way to get passed this by doing some math.
The first function that the android calls is the onTouch method which gives the correct values but just for one touch. I needed it to give right values even on the MotionEvent.ACTION_MOVE so i noticed that MotionEvent.ACTION_MOVE is actually doing correctly depending on the first touch recognized by the onTouchEvent.
So i just got the coordinate Y from the onTouch and the different coordinate with the offset from onTouchEvent calculated the difference and in every onTouchEvent from there, until the user lifts up their finger, i just subtract that difference and that gives me the correct value.
If anyone else has this problem, and doesn't know how to fix it, here is my code, maybe it will be helpful.
#Override
public boolean onTouchEvent(MotionEvent arg1) {
/*you can only touch when the thread is running*/
if(game.state() != STATE_PAUSE){
if(arg1.getAction() == MotionEvent.ACTION_DOWN){
coordinateX = arg1.getX();
coordinateY = arg1.getY();
differenceY = Math.abs(coordinateY - touchedY);
coordinateY = coordinateY - differenceY;
shootingIsOkay = true;
game.setDrawCircle(coordinateX,coordinateY);
}
if(arg1.getAction() == MotionEvent.ACTION_MOVE){
coordinateX = arg1.getX();
coordinateY = arg1.getY();
coordinateY = coordinateY - differenceY;
shootingIsOkay = true;
game.setDrawCircle(coordinateX,coordinateY);
}
if(arg1.getAction() == MotionEvent.ACTION_UP){
shootingIsOkay = false;
}
}
return false;
}
And the onTouch method that is called from the onTouchListener that depends on the view is just simple, here:
#Override
public boolean onTouch(View arg0, MotionEvent arg1) {
touchedY = arg1.getY();
return false;
}
If anyone knows how to fix this problem, please post your answer, i am very curious why this is happening

Consuming touch event prevents me from detecting future events

I have a problem with my app : I have a FrameLayout with a Fragment which contains a map from the Google Maps Android APi (v2), and a customized view which I use to display various buttons on top of the map.
I'd like some of these elements not to be shown while the map is being moved, for this I use the onTouchEvent() function of the View :
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
pressed = true;
invalidate();
break;
case MotionEvent.ACTION_UP:
pressed = false;
invalidate();
break;
}
return false;
}
The onDraw() function only displays the elements drawn over the map if pressed == false;
Now, my problem is in the return : if I put false, I say the event wasn't consumed, and the Android OS no longer notifies me when touch events are recorded, thus the MotionEvent.ACTION_UP isn't detected and the buttons stay hidden. If I put true, the touch events are no longer detected this time by the GoogleMap object, and I can't move the map around. Is there a way to keep detecting all future touch events, while not consuming the first event ?
Use onInterceptTouchEvent or dispatchTouchEvent.
You may read about the differences here: Android: Difference between onInterceptTouchEvent and dispatchTouchEvent?

Android (AndEngine): How to obtain touch input events outside onSceneTouchEvent method?

So, here's my problem: in my game I made a countdown at the start of the game, during which the user can't press the screen, so I wait until the countdown is over to enable the OnSceneTouchListener. As soon as it's enabled, the user will be able to do some stuff moving the finger (not important what).
The problem is that if the user starts moving the finger while there's the countdown, as soon as it's over the onSceneTouchEvent method starts getting inputs, but it skips the ACTION_DOWN event since the user is already moving the finger.
To prevent this I could of course use a boolean inside the onSceneTouchEvent method, but this way it would always check that for every single input. It doesn't influence much the performance, but I'd rather find another way if it's possible.
So I was thinking, is there a way in the AndEngine to obtain the touch input event (with the information of the coordinate pressed, that's what I really need), so that I could "force" the ACTION_DOWN event by simple doing what I should do with it in the method that makes the countdown?
Thanks in advance!
Here is an example, how you can get touch coordinates.
public boolean onTouchEvent(MotionEvent event) {
int myEventAction = event.getAction();
float X = event.getX();
float Y = event.getY();
switch (myEventAction) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE: {
mySprite.setPosition(X, Y);
break;}
case MotionEvent.ACTION_UP:
break;
}
return true;
}
Source of this example

Adjust touch input sample rate on Android

On Android, is there a way of adjusting the touch input sample rate?
You can't stop the system generating these events however you can selectively ignore some of the ACTION_MOVE events as you will see up to 60 per second each reporting the same co-ordinates.
You may wish to only process these ACTION_MOVE events after a set time since the last event, or skip to every 5th or 10th event etc. You'll have to experiment and see what works best for you.
Just make sure you don't skip ACTION_UP or your application may get into a confused state with the touches.
I'm supposing you're trying to adjust the rate as you dealing within onTouch function.
public boolean onTouch(View v, MotionEvent event) {
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
downTime = event.getDownTime();
// Do something you want to.
break;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
eventTime = event.getEventTime();
if(eventTime - downTime > 25){
// Do something you want to.
}
break;
}
}
You should declare long variable downTime and eventTime in your class.
Also the unit of them is ms.
Hope that will help you out.
2011/7/20
Maybe you can try:
Tread.sleep(time);
See if it helps.

Dragging across an android button

I would like an android button to be triggered if a user drags across it and releases his finger on it in addition to the standard activation if it is clicked and released.
I was trying to use the ACTION_MOVE MotionEvent, but this event is only triggered between ACTION_DOWN and ACTION_UP events. I also think that ACTION_OUTSIDE might be a solution. Is there a way to do this?
Here is my code with the actions taken stripped out (b is a button):
b.setOnTouchListener(new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction()== MotionEvent.ACTION_DOWN){
//Tiggers correctly
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
//Triggers correctly between events, but not when the finger is dragged ontop outside of the element
} else if (event.getAction() == MotionEvent.ACTION_OUTSIDE){
//Can't get this to trigger in any case but I think it might be the solution
} else if (event.getAction() == MotionEvent.ACTION_UP){
//Triggers correctly
}
}
return false;
}
});
ACTION_OUTSIDE can be triggered only when your touch area is extended by the ViewRoot.
If you think about dragging, you have 2 options, but for both of them you can detect rectangle, where your button positioned on screen View.getHitRect(Rect) and then, basing on this knowledge you can decide wheter MotionEvent is inside Rect or not.
Otherwise you can create DragLayer and implement all logic there.
You might want to give the Gesture Listener a try. There is a rather good description about how to use it here.
It seems like you need to extend Button and add possibilities for gesture detection in order to accomplish this.

Categories

Resources