Mono Android pinch zoom whilst overriding ondraw - android

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;
}
}
}

Related

Custom image view that can be rotate and scale with one or two finger

I'm trying to devellop a custom view that can be rotate using a button placed in the right bottom corner and also with two finger.
using that link https://github.com/ryanch741/android-view-rotate-zoom-single-finger
i'm trying to modify it's custom view. For that and i used class MultiTouchListener created in that project https://github.com/thuytrinh/android-collage-views
and with that i can use the button and two finger.
But when i'm using two finger i can't maintain the button at the right, bottom corner. I tried to use the matrix of the view with no success
So how can i calculate the new positions of the imageView after the rotate, scale and translate action ?
here is my class
class ViewOnTouchListener2 implements View.OnTouchListener {
private final static String LOG_TAG = ViewOnTouchListener2.class.getSimpleName();
Point pushPoint;
int lastImgLeft;
int lastImgTop;
FrameLayout.LayoutParams viewLP;
FrameLayout.LayoutParams pushBtnLP;
int lastPushBtnLeft;
int lastPushBtnTop;
private View mPushView;
RectF mRect;
/**/
private static final int INVALID_POINTER_ID = -1;
private int mActivePointerId = INVALID_POINTER_ID;
public boolean isRotateEnabled = true;
public boolean isTranslateEnabled = true;
public boolean isScaleEnabled = true;
public float minimumScale = 0.5f;
public float maximumScale = 10.0f;
private ScaleGestureDetector mScaleGestureDetector;
/**/
ViewOnTouchListener2(View mPushView) {
this.mPushView = mPushView;
mScaleGestureDetector = new ScaleGestureDetector(new ViewOnTouchListener2.ScaleGestureListener());
}
#Override
public boolean onTouch(View view, MotionEvent event) {
int pointerCount = event.getPointerCount();
mScaleGestureDetector.onTouchEvent(view, event);
int action = event.getAction();
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
if (null == viewLP) {
viewLP = (FrameLayout.LayoutParams) view.getLayoutParams();
}
if (null == pushBtnLP) {
pushBtnLP = (FrameLayout.LayoutParams) mPushView.getLayoutParams();
}
// Save the ID of this pointer.
mActivePointerId = event.getPointerId(0);
//pushPoint = getRawPoint(event);
pushPoint = getRawPoint(event,mActivePointerId,viewLP);
//Log.d(LOG_TAG," pushPoint x "+pushPoint.x);
//Log.d(LOG_TAG," pushPoint y "+pushPoint.y);
lastImgLeft = viewLP.leftMargin;
lastImgTop = viewLP.topMargin;
//mRect = new RectF (viewLP.leftMargin, viewLP.topMargin, viewLP.rightMargin, viewLP.bottomMargin);
//mRect = new RectF (viewLP.leftMargin, viewLP.topMargin, viewLP.rightMargin, viewLP.bottomMargin);
Log.d(LOG_TAG," lastImgLeft "+lastImgLeft);
Log.d(LOG_TAG," lastImgTop "+lastImgTop);
lastPushBtnLeft = pushBtnLP.leftMargin;
lastPushBtnTop = pushBtnLP.topMargin;
break;
case MotionEvent.ACTION_MOVE:
//un seul doigt
int pointerIndex = event.findPointerIndex(mActivePointerId);
if (pointerIndex != -1) {
//Point newPoint = getRawPoint(event);
Point newPoint = getRawPoint(event,pointerIndex,viewLP);
float moveX = newPoint.x - pushPoint.x;
float moveY = newPoint.y - pushPoint.y;
if (!mScaleGestureDetector.isInProgress()) {
viewLP.leftMargin = (int) (lastImgLeft + moveX);
viewLP.topMargin = (int) (lastImgTop + moveY);
view.setLayoutParams(viewLP);
pushBtnLP.leftMargin = (int) (lastPushBtnLeft + moveX);
pushBtnLP.topMargin = (int) (lastPushBtnTop + moveY);
mPushView.setLayoutParams(pushBtnLP);
}
}
break;
case MotionEvent.ACTION_CANCEL:
mActivePointerId = INVALID_POINTER_ID;
break;
case MotionEvent.ACTION_UP:
mActivePointerId = INVALID_POINTER_ID;
break;
case MotionEvent.ACTION_POINTER_UP: {
// Extract the index of the pointer that left the touch sensor.
int pointerIndex2 = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
int pointerId = event.getPointerId(pointerIndex2);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
int newPointerIndex = pointerIndex2 == 0 ? 1 : 0;
//mPrevX = event.getX(newPointerIndex);
//mPrevY = event.getY(newPointerIndex);
pushPoint = getRawPoint(event,newPointerIndex,viewLP);
lastImgLeft = viewLP.leftMargin;
lastImgTop = viewLP.topMargin;
lastPushBtnLeft = pushBtnLP.leftMargin;
lastPushBtnTop = pushBtnLP.topMargin;
mActivePointerId = event.getPointerId(newPointerIndex);
}
}
break;
}
return false;
}
private Point getRawPoint(MotionEvent event) {
//Log.d(LOG_TAG,"event.getRawX() : "+event.getRawX());
//Log.d(LOG_TAG,"event.getRawY() : "+event.getRawY());
return new Point((int) event.getRawX(), (int) event.getRawY());
}
private Point getRawPoint(MotionEvent event,int pointerIndex,FrameLayout.LayoutParams viewLp) {
//Log.d(LOG_TAG,"event.getRawX() ajusté : "+(viewLp.leftMargin + (int) event.getX(pointerIndex)));
//Log.d(LOG_TAG,"event.getRawY() ajusté : "+(viewLp.topMargin+ (int) event.getY(pointerIndex)));
return new Point(viewLp.leftMargin + (int) event.getX(pointerIndex),
viewLp.topMargin+ (int) event.getY(pointerIndex));
}
private class ScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
private float mPivotX;
private float mPivotY;
private Vector2D mPrevSpanVector = new Vector2D();
#Override
public boolean onScaleBegin(View view, ScaleGestureDetector detector) {
mPivotX = detector.getFocusX();
mPivotY = detector.getFocusY();
mPrevSpanVector.set(detector.getCurrentSpanVector());
return true;
}
#Override
public boolean onScale(View view, ScaleGestureDetector detector) {
ViewOnTouchListener2.TransformInfo info = new ViewOnTouchListener2.TransformInfo();
info.deltaScale = isScaleEnabled ? detector.getScaleFactor() : 1.0f;
info.deltaAngle = isRotateEnabled ? Vector2D.getAngle(mPrevSpanVector, detector.getCurrentSpanVector()) : 0.0f;
info.deltaX = isTranslateEnabled ? detector.getFocusX() - mPivotX : 0.0f;
info.deltaY = isTranslateEnabled ? detector.getFocusY() - mPivotY : 0.0f;
info.pivotX = mPivotX;
info.pivotY = mPivotY;
info.minimumScale = minimumScale;
info.maximumScale = maximumScale;
move(view, info);
return false;
}
}
private void move(View view, ViewOnTouchListener2.TransformInfo info) {
computeRenderOffset(view, info.pivotX, info.pivotY);
adjustTranslation(view, info.deltaX, info.deltaY);
// Assume that scaling still maintains aspect ratio.
float scale = view.getScaleX() * info.deltaScale;
scale = Math.max(info.minimumScale, Math.min(info.maximumScale, scale));
view.setScaleX(scale);
view.setScaleY(scale);
//Log.d(LOG_TAG,"scale : "+scale);
float rotation = adjustAngle(view.getRotation() + info.deltaAngle);
view.setRotation(rotation);
}
private void setPushButton(View view,TransformInfo info,float rotation){
float[] prevPoint = {0.0f, 0.0f};
view.getMatrix().mapPoints(prevPoint);
view.setPivotX(info.pivotX);
view.setPivotY(info.pivotY);
float[] currPoint = {0.0f, 0.0f};
view.getMatrix().mapPoints(currPoint);
float offsetX = currPoint[0] - prevPoint[0];
float offsetY = currPoint[1] - prevPoint[1];
//view.setTranslationX(view.getTranslationX() - offsetX);
//view.setTranslationY(view.getTranslationY() - offsetY);
//Point newPushButtonPosition = getPositionOfPushButton(view,(FrameLayout.LayoutParams) view.getLayoutParams());
//Log.d(LOG_TAG," new x "+currPoint[0]);
//Log.d(LOG_TAG," new y "+currPoint[1]);
pushBtnLP.leftMargin = (int)(view.getTranslationX());
pushBtnLP.topMargin = (int)(view.getTranslationY());
mPushView.setLayoutParams(pushBtnLP);
}
private Point getPositionOfPushButton(View view,FrameLayout.LayoutParams layoutParams){
//Log.d(LOG_TAG," layoutParams.leftMargin x "+layoutParams.leftMargin);
//Log.d(LOG_TAG," layoutParams.topMargin y "+layoutParams.topMargin);
//Log.d(LOG_TAG," width : "+view.getWidth()*view.getScaleX());
//Log.d(LOG_TAG," getHeight : "+view.getHeight()*view.getScaleY());
return new Point(layoutParams.leftMargin+ view.getWidth()*view.getScaleX(),
layoutParams.topMargin+view.getHeight()*view.getScaleY());
}
private static void adjustTranslation(View view, float deltaX, float deltaY) {
float[] deltaVector = {deltaX, deltaY};
view.getMatrix().mapVectors(deltaVector);
view.setTranslationX(view.getTranslationX() + deltaVector[0]);
view.setTranslationY(view.getTranslationY() + deltaVector[1]);
}
private static void computeRenderOffset(View view, float pivotX, float pivotY) {
if (view.getPivotX() == pivotX && view.getPivotY() == pivotY) {
return;
}
float[] prevPoint = {0.0f, 0.0f};
view.getMatrix().mapPoints(prevPoint);
view.setPivotX(pivotX);
view.setPivotY(pivotY);
float[] currPoint = {0.0f, 0.0f};
view.getMatrix().mapPoints(currPoint);
float offsetX = currPoint[0] - prevPoint[0];
float offsetY = currPoint[1] - prevPoint[1];
view.setTranslationX(view.getTranslationX() - offsetX);
view.setTranslationY(view.getTranslationY() - offsetY);
}
private static float adjustAngle(float degrees) {
if (degrees > 180.0f) {
degrees -= 360.0f;
} else if (degrees < -180.0f) {
degrees += 360.0f;
}
return degrees;
}
private class TransformInfo {
public float deltaX;
public float deltaY;
public float deltaScale;
public float deltaAngle;
public float pivotX;
public float pivotY;
public float minimumScale;
public float maximumScale;
}
}

How to rotate and scale an ImageView (not image in it) on gestures?

I have multiple ImageViews dynamically added to an activity and I want all the ImageViews to be rotated and scaled on touch gestures (on them) . I had been searching a lot, all I could is
1) Below class for handling rotation gesture and to find angle of rotation needed
public class RotationGestureDetector {
private static final int INVALID_POINTER_ID = -1;
private float fX, fY, sX, sY;
private int ptrID1, ptrID2;
private float mAngle;
private OnRotationGestureListener mListener;
public float getAngle() {
return mAngle;
}
public RotationGestureDetector(OnRotationGestureListener listener){
mListener = listener;
ptrID1 = INVALID_POINTER_ID;
ptrID2 = INVALID_POINTER_ID;
}
public boolean onTouchEvent(MotionEvent event,ImageView newImageView){
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
ptrID1 = event.getPointerId(event.getActionIndex());
break;
case MotionEvent.ACTION_POINTER_DOWN:
ptrID2 = event.getPointerId(event.getActionIndex());
sX = event.getX(event.findPointerIndex(ptrID1));
sY = event.getY(event.findPointerIndex(ptrID1));
fX = event.getX(event.findPointerIndex(ptrID2));
fY = event.getY(event.findPointerIndex(ptrID2));
break;
case MotionEvent.ACTION_MOVE:
if(ptrID1 != INVALID_POINTER_ID && ptrID2 != INVALID_POINTER_ID){
float nfX, nfY, nsX, nsY;
nsX = event.getX(event.findPointerIndex(ptrID1));
nsY = event.getY(event.findPointerIndex(ptrID1));
nfX = event.getX(event.findPointerIndex(ptrID2));
nfY = event.getY(event.findPointerIndex(ptrID2));
mAngle = angleBetweenLines(fX, fY, sX, sY, nfX, nfY, nsX, nsY);
if (mListener != null) {
mListener.OnRotation(this, newImageView);
}
}
break;
case MotionEvent.ACTION_UP:
ptrID1 = INVALID_POINTER_ID;
break;
case MotionEvent.ACTION_POINTER_UP:
ptrID2 = INVALID_POINTER_ID;
break;
}
return true;
}
private float angleBetweenLines (float fX, float fY, float sX, float sY, float nfX, float nfY, float nsX, float nsY)
{
float angle1 = (float) Math.atan2( (fY - sY), (fX - sX) );
float angle2 = (float) Math.atan2( (nfY - nsY), (nfX - nsX) );
float angle = ((float)Math.toDegrees(angle1 - angle2)) % 360;
if (angle < -180.f) angle += 360.0f;
if (angle > 180.f) angle -= 360.0f;
return angle;
}
public static interface OnRotationGestureListener {
public void OnRotation(RotationGestureDetector rotationDetector,ImageView newImageView);
}
}
2) Below code for rotating my ImageView with the obtained angle, it works but crashes and
response is very slow.
Matrix mat = new Matrix();
Bitmap bMap = ((BitmapDrawable)newImageView.getDrawable()).getBitmap(); ;
mat.postRotate(angle, bMap.getWidth()/2,bMap.getHeight()/2);
Bitmap bMapRotate = Bitmap.createBitmap(bMap, 0, 0,bMap.getWidth(),bMap.getHeight(), mat, true);
newImageView.setImageBitmap(bMapRotate);
3) I tried AnimationRotation, but in that case Image is being rotated not the ImageView.
Also in both of the above cases the ImageView is shown in old postion on touching it.
I'm using the below code for handling "Drag".
#Override
public boolean onTouch(View v, MotionEvent event)
{
mRotationDetector.onTouchEvent(event, newImageView);
float x = event.getX();
float y = event.getY();
System.out.println("matrix=" + savedMatrix.toString());
switch(event.getAction())
{
case MotionEvent.ACTION_DOWN :
{
parms = (RelativeLayout.LayoutParams) newImageView.getLayoutParams();
dx = event.getRawX() - parms.leftMargin;
dy = event.getRawY() - parms.topMargin;
touchFlag=1;
}
break;
case MotionEvent.ACTION_MOVE :
{
touchFlag=0;
x = event.getRawX();
y = event.getRawY();
parms.leftMargin = (int) (x-dx);
parms.topMargin = (int) (y - dy);
newImageView.setLayoutParams(parms);
}
break;
case MotionEvent.ACTION_UP :
{
}
break;
}
return false;
}
});
Please give me some useful link or code , thanks in advance

android : touch point in imageview is marked away from actual point by about 30px always

Am trying out an Android App, where am marking the points where the user does a long press.
For a touch-sensitive image view, I used the most of the code from here:
http://android-developers.blogspot.in/2010/06/making-sense-of-multitouch.html
http://developer.android.com/training/gestures/scale.html
along with some suggestions from various posts in stackoverflow.
This is the code for the touch image view :
public class SimpleImageView extends ImageView implements OnTouchListener,OnGestureListener {
public HashMap<meeCoordinates, Integer> plotPointsMap = new HashMap<meeCoordinates, Integer>();
private float mPosX = 0f;
private float mPosY = 0f;
private boolean didLongPress = false;
private float mLastTouchX;
private float mLastTouchY;
private float magicX;
private float magicY;
private static final int INVALID_POINTER_ID = -1;
Context context;
Canvas canvas;
private GestureDetector gestureScanner;
int deviceWidth, deviceHeight;
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;
PointF start = new PointF();
PointF mid = new PointF();
float oldDist = 1f;
String savedItemClicked;
private int mActivePointerId = INVALID_POINTER_ID;
public SimpleTouchImageView(Context ctx) {
// The ‘active pointer’ is the one currently moving our object.
this(ctx, null, 0);
this.context = ctx;
}
public SimpleTouchImageView(Context ctx, AttributeSet attrs) {
this(ctx, attrs, 0);
this.context = ctx;
}
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
public SimpleTouchImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// Create our ScaleGestureDetector
if (!this.isInEditMode()) {
mScaleDetector = new ScaleGestureDetector(context,new ScaleListener());
}
gestureScanner = new GestureDetector(context, this);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
if (mScaleDetector != null) {
mScaleDetector.onTouchEvent(ev);
}
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
Log.d(TAG, "onTouchEvent MotionEvent.ACTION_DOWN");
final float x = ev.getX();
final float y = ev.getY();
// Set them to the X, Y at the beginning of the touch event
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
// _handler.postDelayed(_longPressed, LONG_PRESS_TIME);
break;
}
case MotionEvent.ACTION_MOVE: {
// _handler.removeCallbacks(_longPressed);
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
if (mScaleDetector != null) {
if (!mScaleDetector.isInProgress()) {
// Calculate the distance moved
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
// Move the object
mPosX += dx;
mPosY += dy;
// Remember this touch position for the next move event
mLastTouchX = x;
mLastTouchY = y;
invalidate();
}
}
mLastTouchX = x;
mLastTouchY = y;
break;
}
case MotionEvent.ACTION_UP: {
// _handler.removeCallbacks(_longPressed);
mActivePointerId = INVALID_POINTER_ID;
// Calculate the Actual X & Y
Drawable drawable = this.getDrawable();
Rect imageBounds = drawable.getBounds();
int intrinsicHeight = this.getDrawable().getIntrinsicHeight();
int intrinsicWidth = this.getDrawable().getIntrinsicWidth();
int imageOffsetX = (int) (ev.getX() - imageBounds.left);
int imageOffsetY = (int) (ev.getY() - imageBounds.top);
float[] f = newfloat[9];
this.getImageMatrix().getValues(f);
final float scaleX = f[Matrix.MSCALE_X];
final float scaleY = f[Matrix.MSCALE_Y];
final int actualWd = Math.round(intrinsicWidth * mScaleFactor);
final int actualHt = Math.round(intrinsicHeight * mScaleFactor);
// CALCULATE THE X,Y COORDINATES CORRESPONDING TO THE POINT OF TOUCH
// IN THE ACTUAL IMAGE,
magicX = ((ev.getX() + (-1 * mPosX)) / mScaleFactor);
magicY = ((ev.getY() + (-1 * mPosY)) / mScaleFactor);
mLastTouchX = ev.getX();
mLastTouchY = ev.getY();
if (didLongPress == true) {
// STORE THE Point where user did long press IN AN ARRAY
plotPointsMap.put ( coordinates, marker );
invalidate();
didLongPress = false;
}
break;
}
case MotionEvent.ACTION_CANCEL: {
// _handler.removeCallbacks(_longPressed);
Log.d(TAG, "onTouchEvent MotionEvent.ACTION_CANCEL");
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
// _handler.removeCallbacks(_longPressed);
Log.d(TAG, "onTouchEvent MotionEvent.ACTION_POINTER_UP");
break;
}
}
// return true;
return gestureScanner.onTouchEvent(ev);
}
/** Determine the space between the first two fingers */
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);
}
/** Calculate the mid point of the first two fingers */
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);
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
}
#Override
public void onDraw(Canvas canvas) {
WindowManager wm = (WindowManager) this.context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
DisplayMetrics metrics = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(metrics);
deviceWidth = metrics.widthPixels;
deviceHeight = metrics.heightPixels;
canvas.drawRect(0, 0, getWidth(), getHeight(), backgroundPaint);
if (this.getDrawable() != null) {
canvas.save();
canvas.translate(mPosX, mPosY);
Matrix matrix = new Matrix();
matrix.postScale(mScaleFactor, mScaleFactor, pivotPointX,pivotPointY);
canvas.drawBitmap(((BitmapDrawable) this.getDrawable()).getBitmap(), matrix,null);
// ---add the marker---
if ((plotPointsMap != null) && (plotPointsMap.size() > 0)) {
for (int index = 0; index < plotPointsMap.size(); index++) {
Set<MyCoordinates> setCoordinates = plotPointsMap.keySet();
if ((setCoordinates != null) && (setCoordinates.size() > 0)) {
Iterator<MyCoordinates> setIterator =setCoordinates.iterator();
if (setIterator != null) {
while (setIterator.hasNext()) {
MyCoordinates coordinates = setIterator.next();
int resource = plotPointsMap.get(coordinates).intValue();
int resourceId = R.drawable.location_marker;
Bitmap marker = BitmapFactory.decodeResource(getResources(), resourceId);
float xCoordinate = coordinates.getCoordinateX();
float yCoordinate = coordinates.getCoordinateY();
canvas.drawBitmap(marker, xCoordinate * mScaleFactor, yCoordinate * mScaleFactor, null);
}
}
}
}
}
canvas.restore();
}
}
#Override
public void setImageDrawable(Drawable drawable) {
// Constrain to given size but keep aspect ratio
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
mLastTouchX = mPosX = 0;
mLastTouchY = mPosY = 0;
mScaleFactor = Math.min((float) getLayoutParams().width/ width, (float) getLayoutParams().height/ height);
pivotPointX = ((float) getLayoutParams().width - (int) (width * mScaleFactor)) / 2;
pivotPointY = ((float) getLayoutParams().height - (int) (height * mScaleFactor)) / 2;
super.setImageDrawable(drawable);
}
float pivotPointX = 0f;
float pivotPointY = 0f;
...
...
}
The Issue
Let's keep aside scaling for now. Say, the image is loaded in its actual size. The point where the user touches is recorded correctly. Say, if the user touches at (120, 50) with x=120 and y=50, then it detects correctly as (120, 50). Am storing the points in an array and re-drawing a marker at each of the points in
public void onDraw(Canvas canvas)
The problem is that when the marker is drawn at the point (am printing it within onDraw(canvas)), the marker is drawn about 30px away from the actual point. i.e. if actual (x,y) is (120,50), then the marker image is drawn at about (150,80). Always there is this 30px difference. Why is this? Am breaking my head on this since the last two weeks in vain. Can someone please help?
here is an image showing the touch point in blue (over the letter 'S' in 'U.S.'), and you can see that the black marker is being drawn off the touch point :
EDIT
When I pan the image, inverse.mapPoints() doesn't account for the offset of the image away from the screen.
For example, here is the image when the App begins, this is the image loaded
and when the user pans, the image might go to the left of the screen-edge as below:
In such a case, inverse.mapPoints() only returns the value from the edge of the screen, but I want the value from the edge of the original image. How do I do it?
I tried googl'ing and tried few things given in stackoverflow (like getTop(), and getLocationOnscreen()) but in vain.
can you help?
1 use getImageMatrix()
2 invert it - invert()
3 use inverted.mapPoints()

Whenever I try to rotate imageview, it only rotates on axis

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.

Get Canvas coordinates after scaling up/down or dragging in android

I'm developing an application in which I'm pasting images, doing drawing and painting on canvas. This app can also Scale up/down the canvas or drag it to different location.
My problem is: I can't get the correct canvas coordinates after scaling or dragging the canvas. I want to draw finger paint after the canvas is scaled or dragged but unable to retrieve the right place where i've touched..:(
Also I'm new bee. Here is the code.
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
//canvas.translate(mPosX, mPosY);
canvas.scale(mScaleFactor, mScaleFactor, super.getWidth() * 0.5f,
super.getHeight() * 0.5f);
mIcon.draw(canvas);
for (Path path : listPath) {
canvas.drawPath(path, paint);
}
canvas.restore();
}
public TouchExampleView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events.
mScaleDetector.onTouchEvent(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;
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
// 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;
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (ev.getAction() & 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;
}
}
float objectNewX,objectNewY;
if (mScaleFactor >= 1) {
objectNewX = ev.getX() + (ev.getX() - super.getWidth() * 0.5f) * (mScaleFactor - 1);
objectNewY = ev.getY() + (ev.getY() - super.getHeight() * 0.5f) * (mScaleFactor - 1);
} else {
objectNewX = ev.getX() - (ev.getX() - super.getWidth() * 0.5f) * (1 - mScaleFactor);
objectNewY = ev.getY() - (ev.getY() - super.getHeight() * 0.5f) * (1 - mScaleFactor);
}
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
path = new Path();
path.moveTo(objectNewX,objectNewY);
path.lineTo(objectNewX,objectNewY);
} else if (ev.getAction() == MotionEvent.ACTION_MOVE) {
path.lineTo(objectNewX,objectNewY);
listPath.add(path);
} else if (ev.getAction() == MotionEvent.ACTION_UP) {
path.lineTo(objectNewX,objectNewY);
listPath.add(path);
}
return true;
}
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, 5.0f));
invalidate();
return true;
}
}
Done it finally by myself.
Draw everything by applying this formula to (px,py) coordinates:
float px = ev.getX() / mScaleFactor + rect.left;
float py = ev.getY() / mScaleFactor + rect.top;
rect = canvas.getClipBounds();
//Get them in on Draw function and apply above formula before drawing
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
clipBounds_canvas = canvas.getClipBounds();
/////Do whatever you want to do..!!!
};
#Override
public boolean onTouchEvent(MotionEvent ev) {
int x = ev.getX() / zoomFactor + clipBounds_canvas.left;
int y = ev.getY() / zoomFactor + clipBounds_canvas.top;
//Use the above two values insted of ev.getX() and ev.getY();
}
Hope this will help.

Categories

Resources