How can get Touch of 2 Fingers at a Time? - android

I click 2 fingers on a SurfaceView at a Time, i want get X,Y of 2 Points:
This is my code, but it only get X,Y of 1 Finger.
How can get X,Y of 2 Fingers at a Time?
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
int ipoiter = event.getPointerCount();
Toast.makeText(getContext(), String.valueOf(ipoiter), Toast.LENGTH_SHORT).show();
for(int i=0;i<ipoiter;i++){
int x= (int)event.getX(i);
int y = (int)event.getY(i);
Toast.makeText(getContext(), String.valueOf(x), Toast.LENGTH_SHORT).show();
}
}
}
I had try below code but action=0, so can't process.
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction() & MotionEvent.ACTION_MASK;
Toast.makeText(getContext(), String.valueOf(action), Toast.LENGTH_SHORT).show();
if (action == MotionEvent.ACTION_POINTER_DOWN) {
int ipoiter = event.getPointerCount();
Toast.makeText(getContext(), String.valueOf(ipoiter), Toast.LENGTH_SHORT).show();
for(int i=0;i<ipoiter;i++){
int x= (int)event.getX(i);
int y = (int)event.getY(i);
Toast.makeText(getContext(), String.valueOf(x), Toast.LENGTH_SHORT).show();
}
}
}

ACTION_POINTER_DOWN is triggered for each newly touched point on screen.
to get the all touch points try something as follows.
You will get the active finger points in mActivePointers
private SparseArray<PointF> mActivePointers = new SparseArray<PointF>();
#Override
public boolean onTouchEvent(MotionEvent event) {
// get pointer index from the event object
int pointerIndex = event.getActionIndex();
// get pointer ID
int pointerId = event.getPointerId(pointerIndex);
// get masked (not specific to a pointer) action
int maskedAction = event.getActionMasked();
switch (maskedAction) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN: {
// We have a new pointer. Lets add it to the list of pointers
PointF f = new PointF();
f.x = event.getX(pointerIndex);
f.y = event.getY(pointerIndex);
mActivePointers.put(pointerId, f);
break;
}
case MotionEvent.ACTION_MOVE: { // a pointer was moved
for (int size = event.getPointerCount(), i = 0; i < size; i++) {
PointF point = mActivePointers.get(event.getPointerId(i));
if (point != null) {
point.x = event.getX(i);
point.y = event.getY(i);
}
}
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_CANCEL: {
mActivePointers.remove(pointerId);
break;
}
}
invalidate();
return true;
}
Source : Check this

Related

How can I use pinch zoom without calling my swipe function in webview?

When I use the pinch zoom gesture, in a horizontal way, the swipe function is always called when it reached a certain difference of the pressed coordinates and the released coordinates.
How can I fix this?
This are the settings for the webview
view.getSettings().setBuiltInZoomControls(true);
view.getSettings().setSupportZoom(true);
view.getSettings().setDisplayZoomControls(false);
The swipe function
protected void swipePage(View v, MotionEvent event, int book) {
int action = MotionEventCompat.getActionMasked(event);
switch (action) {
case (MotionEvent.ACTION_DOWN):
swipeOriginX = event.getX();
swipeOriginY = event.getY();
break;
case (MotionEvent.ACTION_UP):
int quarterWidth = (int) (screenWidth * 0.3);
float diffX = swipeOriginX - event.getX();
float diffY = swipeOriginY - event.getY();
float absDiffX = Math.abs(diffX);
float absDiffY = Math.abs(diffY);
if ((diffX > quarterWidth) && (absDiffX > absDiffY)) {
try {
navigator.goToNextChapter(index);
} catch (Exception e) {
errorMessage(getString(R.string.error_cannotTurnPage));
}
} else if ((diffX < -quarterWidth) && (absDiffX > absDiffY)) {
try {
navigator.goToPrevChapter(index);
} catch (Exception e) {
errorMessage(getString(R.string.error_cannotTurnPage));
}
}
break;
}
}
check if this work - put a if condition checking if single finger touch is there then only do the swipe. change "event" variable to your local variable.
if(event.getPointerCount() == 1)
{
switch (action) {
case (MotionEvent.ACTION_DOWN):
swipeOriginX = event.getX();
swipeOriginY = event.getY();
break;
case (MotionEvent.ACTION_UP):
int quarterWidth = (int) (screenWidth * 0.3);
float diffX = swipeOriginX - event.getX();
float diffY = swipeOriginY - event.getY();
float absDiffX = Math.abs(diffX);
float absDiffY = Math.abs(diffY);
if ((diffX > quarterWidth) && (absDiffX > absDiffY)) {
x = 1;
try {
navigator.goToNextChapter(index);
x = 0;
} catch (Exception e) {
x = 1;
errorMessage(getString(R.string.error_cannotTurnPage));
}
} else if ((diffX < -quarterWidth) && (absDiffX > absDiffY)) {
x = 1;
try {
navigator.goToPrevChapter(index);
x = 0;
} catch (Exception e) {
x = 1;
errorMessage(getString(R.string.error_cannotTurnPage));
}
break;
}
}
}
else if(event.getPointerCount()==2)
{
// code for pinch zoom
}

How does MotionEvent.ACTION_MOVE work inside an onTouchEvent() of a View in Android?

I have implemented the following onTouchEvent() function inside a class extended from View:
public boolean onTouchEvent(MotionEvent event){
// Log.d("MYLOG","TOUCH EVENT OCCURED");
int i, pointToTrack = 0;
boolean trackAPoint = false;
float temp = 0.0f;
float x = event.getX();
float y = event.getY();
// Log.d("MYLOG","TOUCH AT ("+Float.toString(x)+","+Float.toString(y)+")");
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// Toast.makeText(getApplicationContext(), "ACT_DOWN", Toast.LENGTH_SHORT).show();
// Log.d("MYLOG", "ACT_DOWN");
for(i=0; i<NUM_OF_GRAPH_POINTS; i++){
// if(graphPoints[i][0] == y){
if(Math.abs(graphPoints[i][0] - y)<10.0f ){
pointToTrack = i;
trackAPoint = true;
Log.d("MYLOG", "PRESSED PNT = "+Float.toString(y));
}
}
break;
case MotionEvent.ACTION_MOVE:
// Toast.makeText(getApplicationContext(), "ACT_MOVE", Toast.LENGTH_SHORT).show();
// Log.d("MYLOG", "ACT_MOVE");
/*
if(trackAPoint == true){
graphPoints[pointToTrack][1] = y;
}
*/
temp = temp + 10.0f;
Log.d("MYLOG", "temp="+Float.toString(temp));
receiveGainValues(temp);
break;
case MotionEvent.ACTION_UP:
// Toast.makeText(getApplicationContext(), "ACT_UP", Toast.LENGTH_SHORT).show();
// Log.d("MYLOG", "ACT_UP");
trackAPoint = false;
break;
}
// invalidate();
postInvalidate();
return true;
}
Please look at this section in particular:
case MotionEvent.ACTION_MOVE:
// Toast.makeText(getApplicationContext(), "ACT_MOVE", Toast.LENGTH_SHORT).show();
// Log.d("MYLOG", "ACT_MOVE");
/*
if(trackAPoint == true){
graphPoints[pointToTrack][1] = y;
}
*/
temp = temp + 10.0f;
Log.d("MYLOG", "temp="+Float.toString(temp));
receiveGainValues(temp);
break;
the logcat shows temp = 10 multiple times while I move my finger over the tablet surface. However, since it is detecting the motion, shouldn't temp continue to increase as long as the motion is detected? Why dies it increase once, then stops, and yet the Log.d() below it keeps on getting called?
The tablet is nexus 7 with Android 4.3 and API level 18 if it is relevant
However, since it is detecting the motion, shouldn't temp continue to increase as long as the motion is detected?
Because you reset temp in begin of onTouchEvent:
public boolean onTouchEvent(MotionEvent event){
...
float temp = 0.0f;
Set it global but not in onTouchEvent of method
public class YourClass{
private float temp = 0.0f;
...
}
Well, since you declare the temp variable inside the onTouchEvent() method, it is re-set to 0 for every new MOVE event. Try declaring temp outside of onTouchEvent() and observe the change.

Touch movement in only four directions?

I'm doing a simple match-three game, similar to Bejeweled and I just want to move sprite objects by touching the sprite object and then move it one step in four directions like left, right, up and down. I do this by comparing the X and Y values on Down with the X and Y values on Move. It's working but it's far from perfect! It's so easy to get a wrong value if the movement isn't straight. My questions is: is there a way to improve this and make it better?
I have also looked at gesture, but this seems very complicated to use with a surfaceview that I have.
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i("test","Down");
touchActionDownX = (int)event.getX();
touchActionDownY = (int)event.getY();
touchActionMoveStatus = true;
gameLoop.touchX = (int)event.getX();
gameLoop.touchY = (int)event.getY();
gameLoop.touchActionDown = true;
break;
case MotionEvent.ACTION_POINTER_UP:
touchActionMoveStatus = true;
break;
case MotionEvent.ACTION_MOVE:
//Log.i("test","Move");
gameLoop.touchActionMove = true;
if(touchActionMoveStatus) {
touchActionMoveX = (int)event.getX();
touchActionMoveY = (int)event.getY();
if(touchActionMoveX < touchActionDownX)
Log.i("test","Move Left");
else if(touchActionMoveX > touchActionDownX)
Log.i("test","Move Right");
else if(touchActionMoveY < touchActionDownY)
Log.i("test","Move Up");
else if(touchActionMoveY > touchActionDownY)
Log.i("test","Move Down");
touchActionMoveStatus = false; // Will be set to true when pointer is up
}
break;
}
// return false;
return true; // This gets the coordinates all the time
}
Try something like this:
#Override
public boolean onTouch(View v, MotionEvent event) {
//You may have to play with the value and make it density dependant.
int threshold = 10;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i("test","Down");
touchActionDownX = (int)event.getX();
touchActionDownY = (int)event.getY();
touchActionMoveStatus = true;
gameLoop.touchX = (int)event.getX();
gameLoop.touchY = (int)event.getY();
gameLoop.touchActionDown = true;
break;
case MotionEvent.ACTION_POINTER_UP:
touchActionMoveStatus = false;
break;
case MotionEvent.ACTION_MOVE:
//Log.i("test","Move");
gameLoop.touchActionMove = true;
if(touchActionMoveStatus) {
touchActionMoveX = (int)event.getX();
touchActionMoveY = (int)event.getY();
if(touchActionMoveX < (touchActionDownX - threshold) && (touchActionMoveY > (touchActionDownY - threshold)) && (touchActionMoveY (touchActionDownY + threshold))){
Log.i("test","Move Left");//If the move left was greater than the threshold and not greater than the threshold up or down
touchActionMoveStatus = false;
}
else if(touchActionMoveX > (touchActionDownX + threshold) && (touchActionMoveY > (touchActionDownY - threshold)) && (touchActionMoveY < (touchActionDownY + threshold))){
Log.i("test","Move Right");//If the move right was greater than the threshold and not greater than the threshold up or
touchActionMoveStatus = false;
}
else if(touchActionMoveY < (touchActionDownY - threshold) && (touchActionMoveX > (touchActionDownX - threshold)) && (touchActionMoveX < (touchActionDownX + threshold))){
Log.i("test","Move Up");//If the move up was greater than the threshold and not greater than the threshold left or right
touchActionMoveStatus = false;
}
else if(touchActionMoveY > (touchActionDownY + threshold) && (touchActionMoveX > (touchActionDownX - threshold)) && (touchActionMoveX < (touchActionDownX + threshold))){
Log.i("test","Move Down");//If the move down was greater than the threshold and not greater than the threshold left or right
touchActionMoveStatus = false;
}
}
break;
}
// return false;
return true; // This gets the coordinates all the time
}
Or use a ratio:
#Override
public boolean onTouch(View v, MotionEvent event) {
//You may have to play with the value.
//A value of two means you require the user to move twice as
//far in the direction they intend to move than any perpendicular direction.
float threshold = 2.0;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.i("test","Down");
touchActionDownX = (int)event.getX();
touchActionDownY = (int)event.getY();
touchActionMoveStatus = true;
gameLoop.touchX = (int)event.getX();
gameLoop.touchY = (int)event.getY();
gameLoop.touchActionDown = true;
break;
case MotionEvent.ACTION_POINTER_UP:
touchActionMoveStatus = true;
break;
case MotionEvent.ACTION_MOVE:
//Log.i("test","Move");
gameLoop.touchActionMove = true;
if(touchActionMoveStatus) {
touchActionMoveX = (int)event.getX();
touchActionMoveY = (int)event.getY();
// I haven't tested this so you may have a few typos to correct.
float ratioLeftRight = Math.abs(touchActionMoveX - touchActionDownX)/Math.abs(touchActionMoveY - touchActionDownY)
float ratioUpDown = Math.abs(touchActionMoveY - touchActionDownY)/Math.abs(touchActionMoveX - touchActionDownX)
if(touchActionMoveX < touchActionDownX && ratioLeftRight > threshold){
Log.i("test","Move Left");
touchActionMoveStatus = false;
}
else if(touchActionMoveX > touchActionDownX && ratioLeftRight > threshold){
Log.i("test","Move Right");
touchActionMoveStatus = false;
}
else if(touchActionMoveY < touchActionDownY && ratioUpDown > threshold){
Log.i("test","Move Up");
touchActionMoveStatus = false;
}
else if(touchActionMoveY > touchActionDownY && ratioUpDown > threshold){
Log.i("test","Move Down");
touchActionMoveStatus = false;
}
}
break;
}
// return false;
return true; // This gets the coordinates all the time
}
I would choose the dimension with the LARGEST movement and completely ignore the other, for example if the move is x=10 and y=8 then only use the x dimension (i.e. left/right) and vice versa.
Also as noted by Larry McKenzie, using a threshold to ignore smaller movements is a good idea to prevent registering accidental movements that the user did not intend. Tweak the threshold value to someting that feels natural.
Here is some code using your example (only the ACTION_MOVE case):
case MotionEvent.ACTION_MOVE:
//Log.i("test","Move");
gameLoop.touchActionMove = true;
if(touchActionMoveStatus) {
touchActionMoveX = (int)event.getX();
touchActionMoveY = (int)event.getY();
// setup a threshold (below which no movement would occur)
int threshold = 5; /* tweak this as needed */
// first calculate the "delta" movement amounts
int xMove = touchActionMoveX - touchActionDownX;
int yMove = touchActionMoveY - touchActionDownY;
// now find the largest of the two (note that if they
// are equal, x is assumed largest)
if ( Math.abs( xMove ) >= Math.abs( yMove ) ) { /* X-Axis */
if ( xMove >= threshold )
Log.i("test","Move Right");
else if ( xMove <= -threshold )
Log.i("test","Move Left");
}
else { /* Y-Axis */
if ( yMove >= threshold )
Log.i("test","Move Down");
else if ( yMove <= -threshold )
Log.i("test","Move Up");
}
touchActionMoveStatus = false; // Will be set to true when pointer is up
}
}
break;
NOTE: As mentioned in some of the other answers, because multiple events with very small values can occur, it might be best to accumulate (i.e. sum up) the movements UNTIL the threshold is reached - you can use members for this that reset in ACTION_DOWN. Once the threshold is reached (in either dimension) THEN you can perform the checks for which direction.
Alternative Approach
Another way to go about it would be to detect the largest movement in the FIRST ACTION_MOVE event, and then lock all further movements to that dimension. For this you would need to add various state members - these would need to be updated in each state.
Here is a rough example (with only the state tracking):
// members
private boolean axisLock = false; /* Track When Lock is Required */
private boolean axisX = true; /* Axis to Lock (true) for X, (false) for Y */
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// set this state so that ACTION_MOVE knows a lock is required
axisLock = true;
break;
case MotionEvent.ACTION_UP:
// clear the state in case no move was made
axisLock = false;
case MotionEvent.ACTION_MOVE:
// now lock the axis if this is the first move event
if ( axisLock ) {
// this will set whether the locked axis is X (true) or Y (false)
axisX = event.getX() >= event.getY();
// reset the state (to keep the axis locked)
axisLock = false;
}
// at this point you only need to consider the movement for the locked axis
if ( axisX ) {
int movement = (int)event.getX(); /* Get Movement for Locked Axis */
// check for your movement conditions here
}
else {
int movement = (int)event.getY(); /* Get Movement for Locked Axis */
// check for your movement conditions here
}
break;
}
return true;
}
You could add many optimizations to this code, for now it just illustrates the basic idea.
larry had the right idea, i just want to put in a lil fix,
//put this in the wraping class
private static int THRESHOLD = 10;
private static int initX;
private static int initY;
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
initX = (int)event.getX();
initY = (int)event.getY();
break;
case MotionEvent.ACTION_POINTER_UP:
//you can add in some kind of "move back" animation for the item
break;
case MotionEvent.ACTION_MOVE:
if(((int)event.getY - initY) > THRESHOLD){
//move down
break;
}
if(((int)event.getY - initY) > -THRESHOLD){
//move up
break;
}
if(((int)event.getX - initX) > THRESHOLD){
//move right
break;
}
if(((int)event.getX - initX) < -THRESHOLD){
//move left
break;
}
break;
}
}
i didn't test this code, only free write it, but i hope you get my idea :)

Understanding Multitouch in Android

I have been studying multitouch on android but i got couldn't understand some of the lines i found.i searched google but couldn't find that understandable resources. i am posting the code.
I understand most of the part except "first two lines of onTouch method", if (event.getAction() != MotionEvent.ACTION_MOVE && i != pointerIndex) and case MotionEvent.ACTION_MOVE:
Please explain them.
Thanks for your help~~
package --- ;
--imports--
#TargetApi(5)
public class MultiTouchTest extends Activity implements OnTouchListener {
StringBuilder builder = new StringBuilder();
TextView textView;
float[] x = new float[10];
float[] y = new float[10];
boolean[] touched = new boolean[10];
int[] id = new int[10];
private void updateTextView() {
builder.setLength(0);
for (int i = 0; i < 10; i++) {
builder.append(touched[i]);
builder.append(", ");
builder.append(id[i]);
builder.append(", ");
builder.append(x[i]);
builder.append(", ");
builder.append(y[i]);
builder.append("\n");
}
textView.setText(builder.toString());
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
textView = new TextView(this);
textView.setText("Touch and drag(multiple fingers supported!");
textView.setOnTouchListener(this);
setContentView(textView);
for (int i = 0; i < 10; i++) {
id[i] = -1;
}
updateTextView();
}
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction() & MotionEvent.ACTION_MASK;
int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;
int pointerCount = event.getPointerCount();
for (int i = 0; i < 10; i++) {
if (i >= pointerCount) {
touched[i] = false;
id[i] = -1;
continue;
}
if (event.getAction() != MotionEvent.ACTION_MOVE
&& i != pointerIndex) {
continue;
}
int pointerId = event.getPointerId(i);
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
touched[i] = true;
id[i] = pointerId;
x[i] = (int) event.getX(i);
y[i] = (int) event.getY(i);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_OUTSIDE:
case MotionEvent.ACTION_CANCEL:
touched[i] = false;
id[i] = -1;
x[i] = (int) event.getX(i);
y[i] = (int) event.getY(i);
break;
case MotionEvent.ACTION_MOVE:
touched[i] = true;
id[i] = pointerId;
x[i] = (int) event.getX(i);
y[i] = (int) event.getY(i);
break;
}
}
updateTextView();
return true;
}
}
/*Extract the index of the pointer that touch the sensor
Return the masked action being performed, without pointer index
information.
May be any of the actions: ACTION_DOWN, ACTION_MOVE, ACTION_UP,
ACTION_CANCEL, ACTION_POINTER_DOWN, or ACTION_POINTER_UP.
And return the index associated with pointer actions.*/
**int action = event.getAction() & MotionEvent.ACTION_MASK;**
/* Extract the index of the pointer that left the touch sensor
For ACTION_POINTER_DOWN or ACTION_POINTER_UP as returned by getActionMasked(),
this returns the associated pointer index. The index may be used with
getPointerId(int), getX(int), getY(int), getPressure(int), and getSize(int)
to get information about the pointer that has gone down or up.*/
**int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;**
For more details refer:
Link 1
Link 2

Stop OnLongClickListener from firing while dragging

I have a custom View with bitmaps on it that the user can drag about.
I want to make it so when they long click one of them I can pop up a context menu with options such as reset position etc.
In the custom View I add my OnLongClickListener:
this.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
// show context menu..
return true;
}
});
And override onTouchEvent to look something like this:
public boolean onTouchEvent(MotionEvent event) {
handleDrag(event);
super.onTouchEvent(event);
return true;
}
The handleDrag function finds what object is been pressed, and handles updating it's position.
My problem is that when I start to drag an image the OnLongClickListener fires also. I'm not sure the best way around this.
I've tried adding a threshold to handleDrag to return false if user touches down but doesn't attempt to drag, but I'm finding it still difficult to get the correct handler fired.
Can anyone suggest a way to skip the OnLongClickListener while dragging?
I think I have this solved through tweaking my threshold approach.
First, I changed my onTouchEvent to look like this:
public boolean onTouchEvent(MotionEvent event) {
mMultiTouchController.handleDrag(event);
return super.onTouchEvent(event);
}
They now both fire, so I then changed my OnLongClickListener to the following:
this.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
if (!mMultiTouchController.has_moved) {
// Pop menu and done...
return false;
}
return true;
}
});
(mMultiTouchController is the class containing all my gesture detection code).
The key here is within this class, I added the bool 'has_moved'. When I go to start a drag I then compute the delta:
float diffX = Math.abs(mCurrPtX - mPrevPt.getX());
float diffY = Math.abs(mCurrPtY - mPrevPt.getY());
if (diffX < threshold && diffY < threshold) {
has_moved = false;
return;
}
Now when the onLongClick fires I know whether to take action or not.
The final piece was to set:
setHapticFeedbackEnabled(false);
in my View so that the user doesn't get a vibrate every time the longClick fires but no action is taken. I plan to do the vibration manually as a next step.
This seems to be ok so far, hope that helps anyone who has come across a similar situation as this one.
I would stop using the onLongClickListener and just implement your own, which is pretty easy to do. Then you have the control you need to keep them from interfering with each other.
The following code implements the following gestures: drag, tap, double tap, long click, and pinch.
static final short NONE = 0;
static final short DRAG = 1;
static final short ZOOM = 2;
static final short TAP = 3;
static final short DOUBLE_TAP = 4;
static final short POST_GESTURE = 5;
short mode = NONE;
static final float MIN_PINCH_DISTANCE = 30f;
static final float MIN_DRAG_DISTANCE = 5f;
static final float DOUBLE_TAP_MAX_DISTANCE = 30f;
static final long MAX_DOUBLE_TAP_MS = 1000;
static final long LONG_PRESS_THRESHOLD_MS = 2000;
public class Vector2d {
public float x;
public float y;
public Vector2d() {
x = 0f;
y = 0f;
}
public void set(float newX, float newY) {
x = newX;
y = newY;
}
public Vector2d avgVector(Vector2d remote) {
Vector2d mid = new Vector2d();
mid.set((remote.x + x)/2, (remote.y + y)/2);
return mid;
}
public float length() {
return (float) Math.sqrt(x * x + y * y);
}
public float distance(Vector2d remote) {
float deltaX = remote.x - x;
float deltaY = remote.y - y;
return (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY);
}
}
private Vector2d finger1 = new Vector2d();
private Vector2d finger2 = new Vector2d();
private Vector2d pinchStartDistance = new Vector2d();
private Vector2d pinchMidPoint;
private Vector2d fingerStartPoint = new Vector2d();
private long gestureStartTime;
private Marker selectedMarker;
#Override
public boolean onTouch(View v, MotionEvent event) {
// Dump touch event to log
dumpEvent(event);
// Handle touch events here...
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
finger1.set(event.getX(), event.getY());
if (mode == TAP) {
if (finger1.distance(fingerStartPoint) < DOUBLE_TAP_MAX_DISTANCE) {
mode = DOUBLE_TAP;
} else {
mode = NONE;
gestureStartTime = SystemClock.uptimeMillis();
}
} else {
gestureStartTime = SystemClock.uptimeMillis();
}
fingerStartPoint.set(event.getX(), event.getY());
break;
case MotionEvent.ACTION_POINTER_DOWN:
finger2.set(event.getX(1), event.getY(1));
pinchStartDistance.set(Math.abs(finger1.x - finger2.x), Math.abs(finger1.y - finger2.y));
Log.d(TAG, String.format("pinch start distance = %f, %f", pinchStartDistance.x, pinchStartDistance.y));
if (pinchStartDistance.length() > MIN_PINCH_DISTANCE) {
if (pinchStartDistance.x < MIN_PINCH_DISTANCE) {
pinchStartDistance.x = MIN_PINCH_DISTANCE;
}
if (pinchStartDistance.y < MIN_PINCH_DISTANCE) {
pinchStartDistance.y = MIN_PINCH_DISTANCE;
}
pinchMidPoint = finger1.avgVector(finger2);
mode = ZOOM;
Log.d(TAG, "mode=ZOOM" );
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
if (mode == ZOOM) {
Vector2d pinchEndDistance = new Vector2d();
pinchEndDistance.set(Math.abs(finger1.x - finger2.x), Math.abs(finger1.y - finger2.y));
if (pinchEndDistance.x < MIN_PINCH_DISTANCE) {
pinchEndDistance.x = MIN_PINCH_DISTANCE;
}
if (pinchEndDistance.y < MIN_PINCH_DISTANCE) {
pinchEndDistance.y = MIN_PINCH_DISTANCE;
}
Log.d(TAG, String.format("pinch end distance = %f, %f", pinchEndDistance.x, pinchEndDistance.y));
zoom(pinchMidPoint, pinchStartDistance.x/pinchEndDistance.x, pinchStartDistance.y/pinchEndDistance.y);
// Set mode to "POST_GESTURE" so that when the other finger lifts the handler won't think it was a
// tap or something.
mode = POST_GESTURE;
} else if (mode == NONE) {
// The finger wasn't moved enough for it to be considered a "drag", so it is either a tap
// or a "long press", depending on how long it was down.
if ((SystemClock.uptimeMillis() - gestureStartTime) < LONG_PRESS_THRESHOLD_MS) {
Log.d(TAG, "mode=TAP");
mode = TAP;
selectedMarker = checkForMarker(finger1);
if (selectedMarker != null) {
Log.d(TAG, "Selected marker, mode=NONE");
mode = NONE;
((Activity) parent).showDialog(ResultsActivity.DIALOG_MARKER_ID);
}
}
else {
Log.d(TAG, "mode=LONG_PRESS");
addMarker(finger1);
requestRender();
}
} else if (mode == DOUBLE_TAP && (SystemClock.uptimeMillis() - gestureStartTime) < MAX_DOUBLE_TAP_MS) {
// The finger was again not moved enough for it to be considered a "drag", so it is
// a double-tap. Change the center point and zoom in.
Log.d(TAG, "mode=DOUBLE_TAP");
zoom(fingerStartPoint, 0.5f, 0.5f);
mode = NONE;
} else {
mode = NONE;
Log.d(TAG, "mode=NONE" );
}
break;
case MotionEvent.ACTION_MOVE:
if (mode == NONE || mode == TAP || mode == DOUBLE_TAP) {
finger1.set(event.getX(), event.getY());
if (finger1.distance(fingerStartPoint) > MIN_DRAG_DISTANCE) {
Log.d(TAG, "mode=DRAG" );
mode = DRAG;
scroll(fingerStartPoint.x - finger1.x, fingerStartPoint.y - finger1.y);
}
}
else if (mode == DRAG) {
scroll(finger1.x - event.getX(), finger1.y - event.getY());
finger1.set(event.getX(), event.getY());
}
else if (mode == ZOOM) {
for (int i=0; i<event.getPointerCount(); i++) {
if (event.getPointerId(i) == 0) {
finger1.set(event.getX(i), event.getY(i));
}
else if (event.getPointerId(i) == 1) {
finger2.set(event.getX(i), event.getY(i));
}
else {
Log.w(TAG, String.format("Unknown motion event pointer id: %d", event.getPointerId(i)));
}
}
}
break;
}
return true;
}
/** Show an event in the LogCat view, for debugging */
private void dumpEvent(MotionEvent event) {
String names[] = { "DOWN" , "UP" , "MOVE" , "CANCEL" , "OUTSIDE" ,
"POINTER_DOWN" , "POINTER_UP" , "7?" , "8?" , "9?" };
StringBuilder sb = new StringBuilder();
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
sb.append("event ACTION_" ).append(names[actionCode]);
if (actionCode == MotionEvent.ACTION_POINTER_DOWN
|| actionCode == MotionEvent.ACTION_POINTER_UP) {
sb.append("(pid " ).append(
action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
sb.append(")" );
}
sb.append("[" );
for (int i = 0; i < event.getPointerCount(); i++) {
sb.append("#" ).append(i);
sb.append("(pid " ).append(event.getPointerId(i));
sb.append(")=" ).append((int) event.getX(i));
sb.append("," ).append((int) event.getY(i));
if (i + 1 < event.getPointerCount())
sb.append(";" );
}
sb.append("]" );
Log.d(TAG, sb.toString());
}
//This code is to handle the gestures detection
final Handler handler = new Handler();
private Runnable mLongPressRunnable;
detector = new GestureDetector(this, new MyGestureDectector());
view.setOnTouchListener(new OnTouchListener() {
#SuppressLint("ClickableViewAccessibility")
#SuppressWarnings("deprecation")
#Override
public boolean onTouch(View v, MotionEvent event) {
detector.onTouchEvent(event);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
handler.postDelayed(mLongPressRunnable, 1000);
}
if ((event.getAction() == MotionEvent.ACTION_MOVE)
|| (event.getAction() == MotionEvent.ACTION_UP)) {
handler.removeCallbacks(mLongPressRunnable);
}
}
return true;
}
});
mLongPressRunnable = new Runnable() {
public void run() {
Toast.makeText(MainActivity.this, "long", Toast.LENGTH_SHORT)
.show();
}
};
class MyGestureDectector implements GestureDetector.OnDoubleTapListener,
OnGestureListener {
//Implement all the methods
}

Categories

Resources