I'm wandering that is it possible to invoke a service on pinch to zoom in/out in android, please don't mind if this is a silly doubt for you, please help me in understanding.
startService(MyService);
How to invoke this service on pinch to zoom in on top of any activity.
To detect pinch to zoom I'm using the following code in my MainActivity
private static final int INVALID_POINTER_ID = -1;
private int mActivePointerId = INVALID_POINTER_ID;
#Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
// Save the ID of this pointer
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
// Find the index of the active pointer and fetch its position
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
mLastTouchX = x;
mLastTouchY = y;
invalidate();
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
// Extract the index of the pointer that left the touch sensor
final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
break;
}
}
return true;
}
Knowledge sharing would be highly appreciated. Thanks in advance.
Related
my question can be pretty strange, but I can not understand why my scene is moving back, if I touch the screen elsewhere, please help me figure it out.
https://youtu.be/oKdwqn0HC0Y - in this video record my problem,
To better understand the problem
#Override
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
return true;
}
switch (event.getPointerCount()) {
case 3:
Log.e("Event", "PointerCount = " +event.getPointerCount());
return mScaleDetector.onTouchEvent(event);
case 2:
Log.e("Event", "PointerCount = " +event.getPointerCount());
return doRotationEvent(event);
case 1:
Log.e("Event", "PointerCount = " +event.getPointerCount());
return doMoveEvent(event);
}
return true;
}
private boolean doMoveEvent(MotionEvent event)
{
final int action = event.getAction();
Log.e("doMoveEvent", "action = " +action);
switch (action) {
case MotionEvent.ACTION_DOWN: {
Log.e("doMoveEvent", "action_down");
final float x = event.getX();
Log.e("doMoveEvent", " x = " +x);
final float y = event.getY();
Log.e("doMoveEvent", " y = " +y);
// Remember where we started
mLastTouchX = x;
mLastTouchY = y;
break;
}
case MotionEvent.ACTION_MOVE: {
final float x = event.getX();
final float y = event.getY();
// Calculate the distance moved
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
Log.e("doMoveEvent", " DX = " +dx);
Log.e("doMoveEvent", " DY = " +dy);
renderer.angleX += dy;
renderer.angleY += dx;
mLastTouchX = x;
mLastTouchY = y;
// Invalidate to request a redraw
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
}
return true;
}
I solved my problem this way
#Override
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
final float x = event.getX(); //(NEW)
final float y = event.getY(); //(NEW)
mLastTouchX = x; //(NEW)
mLastTouchY = y; //(NEW)
return true;
}
switch (event.getPointerCount()) {
case 3:
Log.e("Event", "PointerCount = " +event.getPointerCount());
return mScaleDetector.onTouchEvent(event);
case 2:
Log.e("Event", "PointerCount = " +event.getPointerCount());
return doRotationEvent(event);
case 1:
Log.e("Event", "PointerCount = " +event.getPointerCount());
return doMoveEvent(event);
}
return true;
}
I want to implement dragging and scaling of canvas on two finger.
I have tried with following approach.
Step 1 : onTouchEvent i have checked if getPointerCount == 2 then do dragging of canvas similar to single finger dragging.
Code:
if(ev.getPointerCount() == 2) {
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_DOWN: {
final float x = ev.getX(0);
final float y = ev.getY(0);
mLastTouchX = x;
mLastTouchY = y;
break;
}
case MotionEvent.ACTION_MOVE: {
final float x = ev.getX(0);
final float y = ev.getY(0);
// Only move if the ScaleGestureDetector isn't processing a gesture.
if (!mScaleDetector.isInProgress()) {
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
invalidate();
}
mLastTouchX = x;
mLastTouchY = y;
//invalidate();
break;
}
case MotionEvent.ACTION_UP: {
Log.i(TAG,"Double Touch : ACTION_UP SingleTouch:"+isSingleTouch );
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
Log.i(TAG,"Double Touch : ACTION_CANCEL SingleTouch:"+isSingleTouch );
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
Log.i(TAG,"Double Touch : ACTION_POINTER_UP SingleTouch:"+isSingleTouch );
// Extract the index of the pointer that left the touch sensor
final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
break;
}
}
Step 2: add Scale Gesture Detector
Code:
public boolean onTouchEvent(MotionEvent ev) {
mScaleDetector.onTouchEvent(ev);
/*Some code */
}
How to allow only one touch on Android? I don't want to support multi-touch and I want my app to discard other touches if my one of my fingers is already touching the screen. It's like using exclusive touch on iOS.
Also, is there a way to set the number of touches allowed?
Thanks!
EDIT:
Minimum target API = 8.
yes you can disable the multi-touch by using this android:splitMotionEvents="false" or android:windowEnableSplitTouch="false"
You will have to keep track of the pointer id of the first touch and use only the touch events corressponding to that touch. Here is good example from The official Android blog "Making Sense of Multitouch "
private static final int INVALID_POINTER_ID = -1;
// The ‘active pointer’ is the one currently moving our object.
private int mActivePointerId = INVALID_POINTER_ID;
// Existing code ...
#Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
// Save the ID of this pointer
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
// Find the index of the active pointer and fetch its position
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
mLastTouchX = x;
mLastTouchY = y;
invalidate();
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
// Extract the index of the pointer that left the touch sensor
final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
break;
}
}
return true;
}
So I have been attempting to create a program that can drag, zoom and rotate a photo. The big problem I seem to be running into is that whenever I try to rotate the photo, it rotates along the corner, rather than around the center. This means that as I try to rotate the image, it quickly leaves my fingers.
Another big problem I have is that every time I touch with two fingers, the image resets to being perfectly upright, instead of the angle it held when i touched it.
#Override
protected void onDraw(Canvas canvas) {
canvas.save();
//if(mode==DRAG)
canvas.translate(mPosX, mPosY);
if (myScale.isInProgress()) {
canvas.scale(mScaleFactor, mScaleFactor, myScale.getFocusX(), myScale.getFocusY());
}
else{
canvas.scale(mScaleFactor, mScaleFactor, mLastGestureX, mLastGestureY);
}
if (myScale.isInProgress()) {
canvas.rotate(degrees, myScale.getFocusX(), myScale.getFocusY());
}
else{
canvas.rotate(degrees, mLastGestureX, mLastGestureY);
}
//canvas.setMatrix(matrix);
//setImageMatrix(matrix);
super.onDraw(canvas);
canvas.restore();
//canvas.drawBitmap(,matrix,new Paint());
}
#Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
if(event.getPointerCount()>1){
myScale.onTouchEvent(event);
}
switch (event.getAction() & MotionEvent.ACTION_MASK){
case MotionEvent.ACTION_DOWN: {
savedMatrix.set(matrix);
final float x = event.getX();
final float y = event.getY();
mode=DRAG;
// Remember where we started
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = event.getPointerId(0);
lastEvent = null;
break;
}
case MotionEvent.ACTION_POINTER_DOWN: {
oldDist = spacing(event);
//savedMatrix.set(matrix);
//midPoint(mid, event);
Log.d("touchResponse: ", "mode=ZOOM");
final float gx = myScale.getFocusX();
final float gy = myScale.getFocusY();
mLastGestureX=gx;
mLastGestureY=gy;
mode=ZOOM;
lastEvent = new float[4];
lastEvent[0] = event.getX(0);
lastEvent[1] = event.getX(1);
lastEvent[2] = event.getY(0);
lastEvent[3] = event.getY(1);
d = rotation(event);
break;
}
case MotionEvent.ACTION_MOVE: {
final int pointerIndex = event.findPointerIndex(mActivePointerId);
final float x = event.getX(pointerIndex);
final float y = event.getY(pointerIndex);
// Calculate the distance moved
if(!myScale.isInProgress()&&mode==DRAG){
// Move the object
float dx = x-mLastTouchX;
float dy = y-mLastTouchY;
mPosX+=dx;
mPosY+=dy;
// Remember this touch position for the next move event
// Invalidate to request a redraw
invalidate();
}
mLastTouchX = x;
mLastTouchY = y;
if(event.getPointerCount()==2){
if (lastEvent!=null){
newRot=rotation(event);
degrees = newRot-d;
}
}
invalidate();
break;
}
case MotionEvent.ACTION_UP: {
}
case MotionEvent.ACTION_POINTER_UP: {
// Extract the index of the pointer that left the touch sensor\
mode=NONE;
mode = NONE;
lastEvent = null;
final int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = event.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = event.getX(newPointerIndex);
mLastTouchY = event.getY(newPointerIndex);
mActivePointerId = event.getPointerId(newPointerIndex);
}
invalidate();
break;
}
}
return true;
}
//this is a method i ripped from a tutoriaql
private float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}
private void midPoint(PointF point, MotionEvent event) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 10.0f));
//matrix=temp;
invalidate();
return true;
}
}
private float rotation(MotionEvent event) {
double delta_x = (event.getX(0) - event.getX(1));
double delta_y = (event.getY(0) - event.getY(1));
double radians = Math.atan2(delta_y, delta_x);
//if (Constant.TRACE) Log.d("Rotation ~~~~~~~~~~~~~~~~~", delta_x+" ## "+delta_y+" ## "+radians+" ## "
// +Math.toDegrees(radians));
Log.d("Rotation ~~~~~~~~~~~~~~~~~", delta_x+" ## "+delta_y+" ## "+radians+" ## "
+Math.toDegrees(radians));
return (float) Math.toDegrees(radians);
}
If you want to rotate around the center, you need to translate to the center first. That will set the new origin as the center of the image, and rotate always rotates around the origin.
I've ported some android code for zooming an imageview to mono for android, but I am having trouble with the scale detector focus point. I need to programmatically draw objects so I am overriding the ondraw method.
The problem is that when I have a zoom scale of anything other than 1, if I pinch on one side of the drawn object, the drawn object jumps away from pinch focus point and you end up with a focus point other than what you started with (if the zoom scale is greater than 1). If the zoom scale is less than 1, the drawn object jumps towards the pinch focus.
I've tried just about everything, but I must be getting too old and tired, because I just can't figure it out.
In the ondraw, I am scaling then translating, then drawing the object at a fixed point.
Am I going about this in the right manner? I cannot find anything on searches that involve drawing an object at a point on a canvas whilst implementing pinch zooming.
If anyone can help, it would be greatly appreciated.
Code is as follows...
class clsTarget : ImageView, ImageView.IOnTouchListener
{
private ScaleGestureDetector mScaleDetector;
private ScaleListener sListener;
private static float mScaleFactor = 0.6F;
private static float scalePointX;
private static float scalePointY;
private static int INVALID_POINTER_ID = -1;
private int mActivePointerId = INVALID_POINTER_ID;
private static float mPosX, mPosY, mLastTouchX, mLastTouchY;
public clsTarget(Context context)
: base(context)
{
this.SetOnTouchListener(this);
sListener = new ScaleListener();
mScaleDetector = new ScaleGestureDetector(context, sListener);
mScaleFactor = 800F / (float)Math.Min(Resources.DisplayMetrics.WidthPixels, Resources.DisplayMetrics.HeightPixels);
}
protected override void OnDraw(Android.Graphics.Canvas canvas)
{
canvas.Save();
canvas.Scale(mScaleFactor, mScaleFactor, scalePointX, scalePointY);
canvas.Translate(mPosX, mPosY);
Paint p = new Paint(PaintFlags.AntiAlias);
p.Color = Color.Orange;
p.SetStyle(Paint.Style.Fill);
p.StrokeWidth = 1F;
canvas.DrawCircle(400, 400, 200, p);
canvas.Restore();
}
public bool OnTouch(View v, MotionEvent e)
{
mScaleDetector.OnTouchEvent(e);
switch (e.Action & MotionEventActions.Mask)
{
case MotionEventActions.Down:
float x = (e.GetX() - scalePointX) / mScaleFactor;
float y = (e.GetY() - scalePointY) / mScaleFactor;
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = e.GetPointerId(0);
break;
case MotionEventActions.Move:
int pointerIndex = e.FindPointerIndex(mActivePointerId);
float x2 = (e.GetX(pointerIndex) - scalePointX) / mScaleFactor;
float y2 = (e.GetY(pointerIndex) - scalePointY) / mScaleFactor;
float dx = (x2 - mLastTouchX);
float dy = (y2 - mLastTouchY);
if (!mScaleDetector.IsInProgress)
{
mPosX += dx;
mPosY += dy;
mLastTouchX = x2;
mLastTouchY = y2;
}
this.Invalidate();
break;
case MotionEventActions.Up:
mActivePointerId = INVALID_POINTER_ID;
break;
case MotionEventActions.Cancel:
mActivePointerId = INVALID_POINTER_ID;
break;
case MotionEventActions.PointerUp:
int pointerIndex2 = (int)(e.Action & MotionEventActions.PointerIndexMask) >> (int)MotionEventActions.PointerIndexShift;
int pointerID = e.GetPointerId(pointerIndex2);
if (pointerID == mActivePointerId)
{
int newPointerIndex = pointerIndex2 == 0 ? 1 : 0;
mLastTouchX = (e.GetX(newPointerIndex) - scalePointX) / mScaleFactor;
mLastTouchY = (e.GetY(newPointerIndex) - scalePointY) / mScaleFactor;
mActivePointerId = e.GetPointerId(newPointerIndex);
}
break;
}
return true;
}
private class ScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener
{
public override bool OnScale(ScaleGestureDetector detector)
{
scalePointX = detector.FocusX;
scalePointY = detector.FocusY;
mScaleFactor *= detector.ScaleFactor;
mScaleFactor = Math.Max(0.5f, Math.Min(mScaleFactor, 7.0f));
return true;
}
}
}
Sorry,bad English.
First,Case MotionEventActions.Move,when ScaleDetector.IsInProgress is true, set mLastGestureX and mLastGesturey.
In the OnDraw,when ScaleDetector.IsInProgress is false,
canvas.Scale(mScaleFactor, mScaleFactor,mLastGestureX,mLastGestureY)
Code is as follows...
class ImageViewExt : ImageView
{
private static int INVALID_POINTER_ID = -1;
private float mPosX;
private float mPosY;
private float mLastTouchX;
private float mLastTouchY;
private float mLastGestureX;
private float mLastGestureY;
private int mActivePointerId = INVALID_POINTER_ID;
private ScaleGestureDetector mScaleDetector;
private static float mScaleFactor = 1.0f;
public ImageViewExt(Context context):base(context)
{
mScaleDetector = new ScaleGestureDetector(Context,new ScaleListener());
}
public override bool OnTouchEvent (MotionEvent e)
{
mScaleDetector.OnTouchEvent (e);
//int action = e.Action;
switch (e.Action & MotionEventActions.Mask) {
case MotionEventActions.Down:
if (!mScaleDetector.IsInProgress) {
float x = e.GetX ();
float y = e.GetY ();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = e.GetPointerId (0);
}
break;
case MotionEventActions.Pointer1Down:
if (mScaleDetector.IsInProgress) {
float gx = mScaleDetector.FocusX;
float gy = mScaleDetector.FocusY;
mLastGestureX = gx;
mLastGestureY = gy;
}
break;
case MotionEventActions.Move:
if (!mScaleDetector.IsInProgress) {
int pointerIdx = e.FindPointerIndex (mActivePointerId);
float x = e.GetX (pointerIdx);
float y = e.GetY (pointerIdx);
float dx = x - mLastTouchX;
float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
Invalidate ();
mLastTouchX = x;
mLastTouchY = y;
} else {
float gx = mScaleDetector.FocusX;
float gy = mScaleDetector.FocusY;
float gdx = gx - mLastGestureX;
float gdy = gy - mLastGestureY;
mPosX += gdx;
mPosY += gdy;
Invalidate ();
mLastGestureX = gx;
mLastGestureY = gy;
}
break;
case MotionEventActions.Up:
mActivePointerId = INVALID_POINTER_ID;
break;
case MotionEventActions.Cancel:
mActivePointerId = INVALID_POINTER_ID;
break;
case MotionEventActions.PointerUp:
int pointerIdx2 = (int)(e.Action & MotionEventActions.PointerIndexMask) >> (int)MotionEventActions.PointerIndexShift;
int pointerId = e.GetPointerId (pointerIdx2);
if (pointerId == mActivePointerId) {
int NewPointerIndex = pointerIdx2 == 0 ? 1 : 0;
mLastTouchX = e.GetX (NewPointerIndex);
mLastTouchY = e.GetY (NewPointerIndex);
mActivePointerId = e.GetPointerId (NewPointerIndex);
}
else{
int TempPointerIdx = e.FindPointerIndex(mActivePointerId);
mLastTouchX = e.GetX(TempPointerIdx);
mLastTouchY = e.GetY(TempPointerIdx);
}
break;
}
return true;
}
protected override void OnDraw (Canvas canvas)
{
canvas.Save ();
canvas.Translate (mPosX, mPosY);
if (mScaleDetector.IsInProgress) {
canvas.Scale (mScaleFactor, mScaleFactor, mScaleDetector.FocusX, mScaleDetector.FocusY);
} else {
canvas.Scale (mScaleFactor, mScaleFactor,mLastGestureX,mLastGestureY);
}
base.OnDraw (canvas);
canvas.Restore();
}
private class ScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener
{
public override bool OnScale (ScaleGestureDetector detector)
{
mScaleFactor *= detector.ScaleFactor;
//小さすぎず大きすぎず処理
mScaleFactor = Math.Max(0.1f, Math.Min(mScaleFactor, 10.0f));
return true;
}
}
}