How to determine scale factor after zoom. I am using below code for pinch zoom. After zoom I have to draw circle on bitmap at correct location. I heard this the formula we have to use to draw at correct point after zoom.
Formula:
float x = ev.getX() / mScaleFactor + rect.left;
float y = ev.getY() / mScaleFactor + rect.top;
I have no idea how to calculate mScaleFactor.
I am using below code for pinch zoom,
public boolean onTouch(View v, MotionEvent rawEvent) {
WrapMotionEvent event = WrapMotionEvent.wrap(rawEvent);
// ...
ImageView view = (ImageView) v;
// Dump touch event to log
dumpEvent(event);
// Handle touch events here...
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
Log.d(TAG, "mode=DRAG");
mode = DRAG;
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
Log.d(TAG, "oldDist=" + oldDist);
if (oldDist > 10f) {
savedMatrix.set(matrix);
midPoint(mid, event);
mode = ZOOM;
Log.d(TAG, "mode=ZOOM");
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
Log.d(TAG, "mode=NONE");
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
// ...
matrix.set(savedMatrix);
matrix.postTranslate(event.getX() - start.x,
event.getY() - start.y);
}
else if (mode == ZOOM) {
float newDist = spacing(event);
Log.d(TAG, "newDist=" + newDist);
if (newDist > 10f) {
matrix.set(savedMatrix);
float scale = newDist / oldDist;
matrix.postScale(scale, scale, mid.x, mid.y);
}
}
break;
}
view.setImageMatrix(matrix);
return true; // indicate event was handled
}
I am using imageview for displaying the image and zoom.
Using canvas and bitmap I am drawing the circle and setting to the image view after draw.
How to calculate scale factor for above pinch zoom code ?
Thanks in advance.
f you use the ScaleGestureDetector.SimpleOnScaleGestureListener class you can do something like this:
#Override
public boolean onScaleBegin(ScaleGestureDetector detector){
//the focal point before taking into account the zoom/translations/etc
float startX = detector.getFocusX();
float startY = detector.getFocusY();
initialPoints = new float[]{startX, startY};
if(transformMatrix.invert(inverseTransformMatrix))
inverseTransformMatrix.mapPoints(initialPoints );
//initialPoints now contain the correct points based on the current zoom
}
Related
I'm working on application where i implemented zoom feature but when i tries to pinch for zoom, it is being very fast zoom. How can i slow down or normalize this zoom feature ?
It's pleasure if any can help.
Here is the intialization of used variable in zooming
private static final float MIN_ZOOM = 1f;
private static final float MAX_ZOOM = 4f;
private float scaleAll = 1;
The code in OnTouchListener for zooming a view in my custom view is,
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
if (mode != MOVING_MODE) {
float[] v = new float[9];
matrix.getValues(v);
// translation
float mScalingFactor = v[Matrix.MSCALE_X];
RectF r = new RectF();
matrix.mapRect(r);
// mScalingFactor shall contain the scale/zoom factor
float scaledX = (x - r.left);
float scaledY = (y - r.top);
scaledX /= mScalingFactor;
scaledY /= mScalingFactor;
x = scaledX;
y = scaledY;
}
int maskedAction = event.getActionMasked();
switch (maskedAction) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
touchMode = DRAG;
if (mode == MOVING_MODE) {
DownPT.x = event.getX();
DownPT.y = event.getY();
}
invalidate();
break;
case MotionEvent.ACTION_MOVE:
if (touchMode == DRAG) {
if (mode == MOVING_MODE) {
PointF mv = new PointF(event.getX() - DownPT.x, event.getY() - DownPT.y);
matrix.postTranslate(mv.x, mv.y);
DownPT.x = event.getX();
DownPT.y = event.getY();
}
invalidate();
} else if (touchMode == ZOOM && mode == MOVING_MODE) {
// pinch zooming
float newDist = spacing(event);
if (newDist > 10f) {
scale = newDist / oldDist; //setting the scaling of the matrix...
// normalize scale
if (scale * scaleAll > MAX_ZOOM) {
scale = MAX_ZOOM / scaleAll;
}
if (scale * scaleAll < MIN_ZOOM) {
scale = MIN_ZOOM / scaleAll;
}
scaleAll *= scale;
//if scale > 1 means zoom in
// if scale < 1 means zoom out
matrix.postScale(scale, scale, mid.x, mid.y);
}
invalidate();
}
break;
case MotionEvent.ACTION_UP:
if (mode == ERASE_MODE || mode == UNERASE_MODE) {
touch_up();
is_from_init = false;
temt_x = event.getX();
temp_y = event.getY();
Log.d(TAG, "add to stack");
addToStack(false);
}
invalidate();
break;
case MotionEvent.ACTION_POINTER_UP: // second finger lifted
touchMode = NONE;
Log.d(TAG, "mode=NONE");
break;
case MotionEvent.ACTION_POINTER_DOWN: // first and second finger down
oldDist = spacing(event);
if (oldDist > 5f) {
savedMatrix.set(matrix);
midPoint(mid, event);
touchMode = ZOOM;
Log.d(TAG, "mode=ZOOM");
}
break;
}
return true;
}
Its may be repeated question but i didn't get any perfect clue or answer to my question.
I am trying to drag and drop images from one view to another, and trying to apply operation like translate,zoom and rotate image view on finger touch.
I went through most of the links for the same but all I getting is image get translate, zoom and rotate within image Matrix i.e. Image get rotate within its image view.
Problem is want to re size my image view too with image. I solved this problem for translate and zoom but I am getting stuck while doing for rotation.
Following is my code.
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: //first finger down only
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
Log.d(TAG, "mode=DRAG");
mode = DRAG;
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
if (oldDist > 10f) {
savedMatrix.set(matrix);
midPoint(mid, event);
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_UP: //first finger lifted
delete.setImageResource(R.drawable.delete);
mode = NONE;
case MotionEvent.ACTION_POINTER_UP: //second finger lifted
mode = NONE;
Log.d(TAG, "mode=NONE");
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
delete.setImageResource(R.drawable.share);
matrix.set(savedMatrix);
matrix.postTranslate(event.getX() - start.x, event.getY()- start.y);
float[] matrixValues = new float[9];
matrix.getValues(matrixValues);
float TransX = matrixValues[Matrix.MTRANS_X];
float TransY = matrixValues[Matrix.MTRANS_Y];
draggedImg.setX(draggedImg.getX()+TransX);
draggedImg.setY(draggedImg.getY()+TransY);
draggedImg.setPivotX(draggedImg.getX()+TransX+draggedImg.getWidth()/2);
draggedImg.setPivotY(draggedImg.getY()+TransY-draggedImg.getHeight()/2);
} else if (mode == ZOOM && event.getPointerCount() == 2) {
Log.d(TAG, "In Zoom Mode");
float newDist = spacing(event);
matrix.set(savedMatrix);
if (newDist > 10f) {
Drawable drawing = draggedImg.getDrawable();
Bitmap bitmap = ((BitmapDrawable)drawing).getBitmap();
scale = newDist / oldDist;
matrix.postScale(scale, scale, mid.x, mid.y);
float[] matrixValues = new float[9];
matrix.getValues(matrixValues);
scaleX = matrixValues[Matrix.MSCALE_X];
scaleY = matrixValues[Matrix.MSCALE_Y];
params.width = (int)(bitmap.getWidth()*scaleX);
params.height =(int)(bitmap.getHeight()*scaleY);
float TransX = matrixValues[Matrix.MTRANS_X];
float TransY = matrixValues[Matrix.MTRANS_Y];
draggedImg.setPivotX(mid.x);
draggedImg.setPivotY(mid.y);
draggedImg.setScaleType(ImageView.ScaleType.FIT_CENTER);
draggedImg.setImageMatrix(matrix);
draggedImg.setLayoutParams(params);
}
if (lastEvent != null) {
Drawable drawing = draggedImg.getDrawable();
Bitmap bitmap = ((BitmapDrawable)drawing).getBitmap();
float newRot = rotation(event);
float r = newRot - d;
draggedImg.setScaleType(ImageView.ScaleType.MATRIX);
matrix.postRotate(r, v.getMeasuredWidth() / 2,v.getMeasuredHeight() / 2);
float[] matrixValues = new float[9];
matrix.getValues(matrixValues);
}
}
}
return true; // indicate event was handled
}
});
Please try MultiTouchLibrary . It will fulfill your need
This library provide you below functionality
Rotate
Scale Image (Pinch to zoom)
I have one main and big ImageView and 4-5 small ImageView on it. The small ImageView can utilize zoom-in-out, drag to change place, and rotate, all by an onTouchEvent that I built in a custom ImageView.
Since use of onTouch event for dragging (moving) ImageView, I cant use OnDragListener for deleting the small ImageView. I want to delete by moving to corner of the screen or over a specific button.
Please help me find a solution.
public boolean onTouchEvent(MotionEvent event) {
this.setScaleType(ImageView.ScaleType.MATRIX);
float scale;
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: //first finger down only
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
Log.d("amin", "mode=DRAG");
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
if (oldDist > 10f) {
savedMatrix.set(matrix);
midPoint(mid, event);
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_UP: //first finger lifted
case MotionEvent.ACTION_POINTER_UP: //second finger lifted
mode = NONE;
Log.d("amin", "mode=NONE" );
break;
case MotionEvent.ACTION_MOVE:
if (mode == ZOOM && event.getPointerCount() == 2) {
float newDist = spacing(event);
matrix.set(savedMatrix);
if (newDist > 10f) {
scale = newDist / oldDist;
matrix.postScale(scale, scale, mid.x, mid.y);
}
if (lastEvent != null) {
newRot = rotation(event);
float r = newRot - d;
matrix.postRotate(r, this.getMeasuredWidth() / 2,
this.getMeasuredHeight() / 2);
}
}else{
mode = DRAG;
// matrix.set(savedMatrix);
//matrix.postTranslate(event.getX() - start.x, event.getY()-start.y);
//this.layout((int)(start.x- (this.getWidth())),(int)(start.y- (this.getHeight())),(int) start.x, (int) start.y);
RelativeLayout.LayoutParams layoutPrarms = (RelativeLayout.LayoutParams)this.getLayoutParams();
int left = layoutPrarms.leftMargin + (int)(event.getX() - start.x);
int top = layoutPrarms.topMargin + (int)(event.getY() - start.y);
layoutPrarms.leftMargin = left;
layoutPrarms.topMargin = top;
this.setLayoutParams(layoutPrarms);
Log.d("amin", "mode=Drag" );
}
break;
}
// Perform the transformation
this.setImageMatrix(matrix);
return true;
}
In my xml file, I have declared two imageview and later I add image in it by using setImageBitmap() method and also apply separate touch event.
But here problem is that I can't move first image when second image is added into setImageBitmap() and only second image is moveable now.
Thanks in advance and sorry for english.
working_bitmap = BitmapFactory.decodeFile(file_location+"cropped_image.jpg");
outBitmap=Bitmap.createBitmap(
working_bitmap.getWidth(),
working_bitmap.getHeight(),Bitmap.Config.ARGB_8888);
Canvas canvas1 = new Canvas(outBitmap);
canvas1.drawBitmap(working_bitmap, new Matrix(), null);
if(downloaded_shirt_image != null)
{
int width = downloaded_shirt_image.getWidth();
int height = downloaded_shirt_image.getHeight();
int halfWidth = width/3;
int halfHeight = height/3;
//Half Scaled
Bitmap bmHalf = Bitmap.createScaledBitmap(downloaded_shirt_image,
halfWidth, halfHeight, false);
proimg =(ImageView)findViewById(R.id.pro_img);
proimg.setDrawingCacheEnabled(true);
proimg.setImageBitmap(bmHalf);
proimg.setVisibility(View.VISIBLE);
proimg.setOnTouchListener(new View.OnTouchListener()
{
public boolean onTouch(View v, MotionEvent event) {
ImageView view = (ImageView) v;
int rotation = 25;
// Dump touch event to log
dumpEvent(event);
// Handle touch events here...
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
Log.d(TAG, "mode=DRAG");
mode = DRAG;
proimg.invalidate();
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
// ...
matrix.set(savedMatrix);
matrix.postTranslate(event.getX() - start.x,
event.getY() - start.y);
}
else if (mode == ZOOM) {
float newDist = spacing(event);
Log.d(TAG, "newDist=" + newDist);
if (newDist > 10f) {
matrix.set(savedMatrix);
float scale = newDist / oldDist;
matrix.postScale(scale, scale, mid.x, mid.y);
}
}
proimg.invalidate();
break;
}
view.setImageMatrix(matrix);
return true; // indicate event was handled
}
});
canvas1.drawBitmap(downloaded_shirt_image, 50, 192, null);
}
if(downloaded_pant_image != null)
{
int width = downloaded_pant_image.getWidth();
int height = downloaded_pant_image.getHeight();
int halfWidth = width/3;
int halfHeight = height/3;
//Half Scaled
Bitmap bmHalf = Bitmap.createScaledBitmap(downloaded_pant_image,
halfWidth, halfHeight, false);
pantimg =(ImageView)findViewById(R.id.pro_img_down);
pantimg.setDrawingCacheEnabled(true);
pantimg.setImageBitmap(bmHalf);
pantimg.setVisibility(View.VISIBLE);
pantimg.setOnTouchListener(new View.OnTouchListener()
{
public boolean onTouch(View v, MotionEvent event) {
ImageView view = (ImageView) v;
int rotation = 25;
// Dump touch event to log
dumpEvent(event);
// Handle touch events here...
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
Log.d(TAG, "mode=DRAG");
mode = DRAG;
pantimg.invalidate();
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
// ...
matrix.set(savedMatrix); matrix.postTranslate(event.getX() - start.x,
event.getY() - start.y);
}
else if (mode == ZOOM) {
float newDist = spacing(event);
Log.d(TAG, "newDist=" + newDist);
if (newDist > 10f) {
matrix.set(savedMatrix);
float scale = newDist / oldDist;
matrix.postScale(scale, scale, mid.x, mid.y);
}
}
pantimg.invalidate();
break;
}
view.setImageMatrix(matrix);
return true; // indicate event was handled
}
}
);
canvas1.drawBitmap(downloaded_pant_image, 110, 565, null);
}
Please see following link's Answer and Comments also, it may help you.
Drag & Drop Two Images
I had implemented an image view with the zoom and drag functionalities.I am using matrix for the zoom and drag functionality.But my problem is that I cant set my minimum and maximum zoom level and the drag space limit.can any on e tell me how I can do that.
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 float spacing(PointF start,PointF end)
{
float x = end.x- start.x;
float y = end.y -start.y;
return FloatMath.sqrt(x * x + y * y);
}
#Override
public boolean onTouch(View v, MotionEvent event)
{
ImageView view = (ImageView) v;
view.setScaleType(ImageView.ScaleType.MATRIX);
float scale;
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
Log.d(TAG, "mode=DRAG" );
mode = DRAG;
break;
case MotionEvent.ACTION_UP:
if(mode==DRAG)
{
PointF end=new PointF();
end.set(event.getX(), event.getY());
Log.d("Fling", "Inside the Action Key UP"+spacing(start, end));
float []x = new float[9],org=new float[9];
matrix.getValues(x);
orgMatrix.getValues(org);
Log.d("Fling", "matrixValue"+matrix);
Log.d("Fling", "OrgmatrixValue"+orgMatrix);
float matrixSizex=x[Matrix.MSCALE_X];
float matrixSizey=x[Matrix.MSCALE_Y];
float matrixOrgSizex=org[Matrix.MSCALE_X];
float matrixOrgSizey=org[Matrix.MSCALE_Y];
if(Math.abs(matrixOrgSizex-matrixSizex)<0.17f&&Math.abs(matrixOrgSizey-matrixSizey)<0.17f)
{
Log.d("Fling", "Current Size is equal");
if(spacing(start, end)>30.f)
{
if((start.x>end.x+30)&&(Math.abs(start.y-end.y)<50.0f))
{
Log.d("Fling", "Is From Right To left");
loadedimage.setImageMatrix(orgMatrix);
leftSwipe();
view.setScaleType(ImageView.ScaleType.FIT_XY);
}
else if((end.x>start.x+30)&&(Math.abs(end.y-start.y)<50.0f))
{
Log.d("Fling", "Is From Left To Right");
loadedimage.setImageMatrix(orgMatrix);
rightSwipe();
view.setScaleType(ImageView.ScaleType.FIT_XY);
}
}
}
}
case MotionEvent.ACTION_POINTER_UP: //second finger lifted
mode = NONE;
Log.d(TAG, "mode=NONE" );
break;
case MotionEvent.ACTION_POINTER_DOWN: //second finger down
oldDist = spacing(event);
Log.d(TAG, "oldDist=" + oldDist);
if (oldDist > 5f) {
savedMatrix.set(matrix);
midPoint(mid, event);
mode = ZOOM;
Log.d(TAG, "mode=ZOOM" );
}
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
//movement of first finger
PointF end=new PointF();
end.set(event.getX(), event.getY());
Log.d("Fling", "Inside the Action Key UP"+spacing(start, end));
float []x = new float[9],org=new float[9];
matrix.getValues(x);
orgMatrix.getValues(org);
Log.d("Fling", "matrixValue"+matrix);
Log.d("Fling", "OrgmatrixValue"+orgMatrix);
float matrixSizex=x[Matrix.MSCALE_X];
float matrixSizey=x[Matrix.MSCALE_Y];
float matrixOrgSizex=org[Matrix.MSCALE_X];
float matrixOrgSizey=org[Matrix.MSCALE_Y];
if(Math.abs(matrixOrgSizex-matrixSizex)>=0.17f&&Math.abs(matrixOrgSizey-matrixSizey)>=0.17f)
{
matrix.set(savedMatrix);
if (view.getLeft() >= 0)
{
matrix.postTranslate(event.getX() - start.x, event.getY() - start.y);
}
}
}
else if (mode == ZOOM) { //pinch zooming
float newDist = spacing(event);
Log.d(TAG, "newDist=" + newDist);
if (newDist > 5f) {
matrix.set(savedMatrix);
scale = newDist / oldDist; //thinking i need to play around with this value to limit it**
matrix.postScale(scale, scale, mid.x, mid.y);
}
}
break;
}
// Perform the transformation
view.setImageMatrix(matrix);
return true; // indicate event was handled
}
My Sample code is here can any one help me in setting minimum and maximum zoom and drag level.I also have problem when I touch in the image view it automatically get zoomed when converted to the matrix I cant make it stay fit in the device screen.I am totally stuck here........
To restrict zooming I compare the zoomed matrix with the identity matrix and don't assign it to my ImageView if it's smaller than the identity matrix, in which case I reset the scaled matrix back to the identity matrix. I'm using Mono for Android but I guess it will be almost the same in Java:
//check that zoom is not too small
if (Utils.SmallerThanIdentity(matrix))
{
ResetView(v);
}
Where SmallerThanIdentity is implemented:
public static bool SmallerThanIdentity(Android.Graphics.Matrix m)
{
float[] values = new float[9];
m.GetValues(values);
return ((values[0] < 1.0) || (values[4] < 1.0) || (values[8] < 1.0));
}
And here's ResetView. I have Java code for that:
public void resetView(View v)
{
ImageView view = (ImageView)v;
matrix = new Matrix();
view.setScaleType(ImageView.ScaleType.MATRIX);
view.setImageMatrix(matrix);
}
Regarding scroll, I use the ShouldScroll method below before translating the matrix to the area where I want to restrict the scrolling.
private bool ShouldScroll(Android.Graphics.Matrix matrix)
{
float[] values = new float[9];
matrix.GetValues(values);
float[] oldValues = new float[9];
oldMatrix.GetValues(oldValues);
float zoomPercentX = values[0] / oldValues[0];
float zoomPercentY = values[4] / oldValues[4];
float tmpW = -(this.Drawable.IntrinsicWidth / 2) * zoomPercentX;
float tmpH = -(this.Drawable.IntrinsicHeight / 2) * zoomPercentY;
return (values[2] < 0.0f) && (values[2] > tmpW) && //horizontal coordinates
(values[5] < 0.0f) && (values[5] > tmpH); //vertical coordinates
}