I have the usual gesture detector for detecting fling , It is an instance attribute of a SurfaceView
GestureDetector flingDetector = new GestureDetector(getContext(),new SimpleOnGestureListener() {
#Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
// Fling implementation
return true;
}
});
I am drawing a lot of complex stuff on a canvas and I have a translate(dx,dy) method that I use with onScroll.
So my question is how do I implement the fling using the translate method ?
There seem to be a lot of questions on detecting fling , my question is on implementing it .
I am not sure this will answer your question, I'll give it a try.
Check http://developer.android.com/reference/android/view/MotionEvent.html for MotionEvent.
You can use the two events received as e1 and e2 on the onFling method, and calculate coordinate differences with e1.getX(), e2.getX(), e1.getY(), e2.getY().... With this you would have the dx and dy to use with translate(dx,dy).
Since the fling seems more of a dynamic gesture, you could decide that fling means an ampler movement, and apply an amplification factor to dx and dy, so that when the user scrolls, they get a precise movement, but on fling, the actual movement gets amplified.
If this factor depends on velocity, you have a custom response for every user input.
(A different thing would be animating the result, which I guess would depend on other things).
An example I might try if it were me:
User scrolls softly: Movement is dx,dy. Translate(dx,dy).
User flings:
Real motion: dx=(e2.getX()-e1.getX(). dy = (e2.getY()-e1.getY().
Fling factor: (Custom implementation).
Modified motion: dxModified = dx*velocityX*F. dyModified = dy*velocityY*F.
Finally: translate (dxModified,dyModified)
Hope this helps to some extent.
Edit: I did not realize this question was from 2012, hopefully this will help someone some time. It would be nice to know about the final implementation anyway!.
Related
I am trying to make all my drawn Sprites dragable for a little game. It should be able to touch anywhere and the sprites should move the same distance, the finger moves.
With the following method they will move on an ACTION_MOVE event, but only very slow, a shorter distance and sometimes they dont:
addToX/Y only adds the gap to the coordinates of the sprites
#Override
public boolean onTouchEvent(MotionEvent evt){
switch(evt.getAction()){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
if(getHistorySize() > 0){
for(int i = 1, n = evt.getHistorySize(); i < n; i++){
int calcX = (int) getHistoricalX(i) - (int) getHistoricalX(i-1);
int calcY = (int) getHistoricalY(i) - (int) getHistoricalY(i-1);
for(Sprite sprite : spriteList) {
sprite.addToX(calcX);
sprite.addToY(calcY);
}
}
}
return true;
}
Any ideas on this?
Assuming your Sprite class is an (potentially-indirect) extension of android.view.View, then you can use setOnDragListener() to define an onDrag() override for them. Then you can use startDrag(...) on them to begin the drag. This is typically triggered by a long-press gesture on the view to be dragged, but in your case you can trigger it from within onTouchEvent() in ACTION_MOVE once (or even ACTION_DOWN). See here for more details on these methods.
Also, with respect to the code you posted, one issue with it that probably explains why it doesn't always work is that you are only using the historical points (which may or may not have accumulated on any particular call to onTouchEvent()). Whether or not getHistorySize() is greater than 0, you should still also use evt.getX() and evt.getY() on each call to onTouchEvent(). But of course, if you use the drag listener approach I suggested instead, you won't need to worry about this.
Update per comment
If you want to move all of the sprites at once, you can put the sprites into a full-screen FrameLayout and attach a GestureDetector that uses a GestureDetector.SimpleOnGestureListener to capture onScroll() callbacks and then calls scrollTo() on the FrameLayout. When the parent FrameLayout scrolls, all of its children sprites will appear to move together.
I'm working with touch gestures in Android using the OnGestureListener interface and GestureDetector.
I made an app to test if detecting two fingers works, in onFlp(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY), I print the id of the different MotionEvents but these ids are the same (apparently only detects one finger).
Does GestureDetector support multi-touch events?
The Issue
Using OnGestureListener to detect multitouch gestures does not seem to be implemented by default.
The first thing you may have tried is reading event.pointerCount to get the count of fingers on the screen. However, this will be equal to 1. This is because you will (quite likely) never be able touch the screen with both fingers in exactly the same millisecond.
Fixing it
You have to buffer pointerCount (the amount of fingers on screen). First add those variables somewhere in the context that you intend to track gestures in:
// track how many fingers are used
var bufferedPointerCount = 1
var bufferTolerance = 500 // in ms
var pointerBufferTimer = Timer()
Then, in the onTouchEvent(event: MotionEvent) function, you add this:
// Buffer / Debounce the pointer count
if (event.pointerCount > bufferedPointerCount) {
bufferedPointerCount = event.pointerCount
pointerBufferTimer = fixedRateTimer("pointerBufferTimer", true, bufferTolerance, 1000) {
bufferedPointerCount = 1
this.cancel() // a non-recurring timer
}
}
Essentially this tracks the maximum amount of fingers on the display and keeps it valid for bufferTolerance milliseconds (here: 500).
I currently am implementing it in a custom Android Launcher I created (finnmglas/Launcher | see related issue)
My app needs to detect simple gestures (scroll, tap, long tap), and pinch zoom. Either detector works fine on its own - GestureDetector.SimpleOnGestureListener for tap / scroll and ScaleGestureDetector.SimpleOnScaleGestureListener for pinch zoom. The problem is combining the two. More specifically, it is very hard to start pinch zoom so that a couple of onScroll events are not generated before onScaleBegin.
Is there any good way to fix this? The only solution I can think about is buffer a few events before processing them (event queue), and discard onScroll / onTap without processing once onScaleBegin is detected. But that would introduce input lag (which my app already has and I don't want to make it even worse).
try this:
#Override
public boolean onTouchEvent(MotionEvent event) {
boolean res = mScaleGestureDetector.onTouchEvent(event);
if (!mScaleGestureDetector.isInProgress()) {
res = mGestureDetector.onTouchEvent(event);
}
return res;
}
Is it possible to get the speed or velocity or acceleration of touch event in android with the existing api? I have gone through MotionEvent class and none of the fields in that class seem to retrieve information that i need. Any help would be greatly appreciated
MotionEvent does not help you in this case. You can use VelocityTracker class. It gets MotionEvent instances and calculates velocity of recent touch events.
You can take a look at its documentation here:
http://developer.android.com/reference/android/view/VelocityTracker.html
First you have to get an instance by obtain() method:
VelocityTracker velocity = VelocityTracker.obtain();
then you can add ACTION_MOVE events to its queue:
if(event.getAction() == MotionEvent.ACTION_MOVE)
{
velocity.addMovement(event);
}
then you can compute velocity and extract x_velocity and y_velocity
velocity.computeCurrentVelocity(1000);
float x_velocity = velocity.getXVelocity();
float y_velocity = velocity.getYVelocity();
I hope it works for you.
You need to calculate velocity manually between each two events using System.nanoTime. Same way for acceleration (but using velocity instead of coordinates this time).
What is the difference of events of onFling() and onScroll() of android.view.GestureDetector.OnGestureListener?
link text
onScroll() happens after the user puts his finger down on the screen and slides his finger across the screen without lifting it. onFling() happens if the user scrolls and then lifts his finger. A fling is triggered only if the motion was fast enough.
Actually onFling has nothing to do with the speed at which the movement ocurred. It's the user, via the velocityX and velocityY parameters that determine if the speed (or distance, via the MotionEvent parameters) was good enough for their purposes.
The onScroll is constantly called when the user is moving his finger, where as the onFling is called only after the user lifts his finger.
You can see the code of framework/base/core/java/android/view/GestureDetector.java, at the onTouchEvent() method. onFling() is called in case of MotionEvent.ACTION_UP and velocityY > mMinimumFlingVelocity or velocityX > mMinimumFlingVelocity. onScroll() is called in case of MotionEvent.ACTION_MOVE.
You can differentiate between the two after the onFling() happens. First, in onDown() store the current coordinates of the image as class variables. The onScroll() will work as expected but if the onFling() determines that this is a fling event, just restore the original coordinates that were stored in onDown(). I found this works very well.
#Override
public boolean onDown(MotionEvent e) {
// remember current coordinates in case this turns out to be a fling
mdX = imageView.dX;
mdY = imageView.dY;
return false;
}