I'm trying to draw some points on an ImageView which also features DRAG and ZOOM capabilities.
My implementation works fine if the image is not modified ( zoomed or dragged ), but if I apply these types of transformations, it won't draw correctly anymore.
Instead it will hold the initial frame as reference and will draw the dots as if it weren't modified.
This is what the image looks like right after choosing it from the gallery and drawing a green dot by tapping on it.
This is what the image looks like after zooming it in and dragging it to the side. As you can see, if I tap inside the initial frame, it will draw the dot inside the image as if it were unscaled.
Bellow is the code (select image from gallery, onTouch method and the actual drawing).
public void onClick(View v) {
if (v.getId()==R.id.putPoint){
drawPath=true;
}
else
if (v == choosePicture) {
Intent choosePictureIntent = new Intent(
Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(choosePictureIntent, 0);
}
}
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
if (resultCode == RESULT_OK) {
Uri imageFileUri = intent.getData();
try {
BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
bmpFactoryOptions.inJustDecodeBounds = true;
bmp = BitmapFactory.decodeStream(getContentResolver().openInputStream(
imageFileUri), null, bmpFactoryOptions);
bmpFactoryOptions.inJustDecodeBounds = false;
bmp = BitmapFactory.decodeStream(getContentResolver().openInputStream(
imageFileUri), null, bmpFactoryOptions);
alteredBitmap = Bitmap.createBitmap(bmp.getWidth(), bmp
.getHeight(), bmp.getConfig());
canvas = new Canvas(alteredBitmap);
paint = new Paint();
paint.setColor(Color.GREEN);
paint.setStrokeWidth(5);
matrix = new Matrix();
canvas.drawBitmap(bmp, matrix, paint);
choosenImageView.setImageBitmap(alteredBitmap);
choosenImageView.setOnTouchListener(this);
} catch (Exception e) {
Log.v("ERROR", e.toString());
}
}
}
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
ImageView view = (ImageView) v;
if(drawPath) {
//I use this flag to enable drawing mode
switch (action) {
case MotionEvent.ACTION_DOWN:
downx = event.getX();
downy = event.getY();
canvas.drawCircle(downx, downy, radius,paint);
choosenImageView.invalidate();
drawPath=false;
break;
}
return true;
}
else{
//if the drawing mode is not enable I can zoom and drag the image
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
mode = DRAG;
lastEvent = null;
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:
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
lastEvent = null;
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
matrix.set(savedMatrix);
float dx = event.getX() - start.x;
float dy = event.getY() - start.y;
matrix.postTranslate(dx, dy);
} else if (mode == ZOOM) {
float newDist = spacing(event);
if (newDist > 10f) {
matrix.set(savedMatrix);
float scale = (newDist / oldDist);
matrix.postScale(scale, scale, mid.x, mid.y);
}
}
break;
}
view.setImageMatrix(matrix);
return true;
}
}
}
My goal is to be able to tap on the image(zommed or dragged) and see the points drawn correctly.
See: How do I get touch coordinates with respect to canvas after scaling and translating?
The easiest and most consistent way to do this is to apply the inverse matrix you used to modify the view modify the MotionEvent. There's two commands you need to know:
event.transform(invertMatrix);
and:
invertMatrix = new Matrix(viewMatrix);
invertMatrix.invert(invertMatrix);
Basically you do the opposite of what you did to the view to the touches. So the touches go to the scene space.
Related
I'm trying to implement a camera preview where user can insert images and drag/scale/rotate them as overlays. I'd also need to support zoom in/out on the camera preview which is in the background. User would be able to pinch to zoom the camera when pressing outside of the image.
The images which the user can add are ImageViews that have match_parent width and height and scale type matrix. I need to allow the touch events to pass thru to the background camera view if they're not directly touching the current image.
I found some code that allows me to perform the various image manipulations:
// ImageView drag, zoom, rotate related variable
// These matrices will be used to move and zoom image
Matrix mMatrix = new Matrix();
Matrix mSavedMatrix = new Matrix();
// We can be in one of these 3 states
public enum ImageTouchMode {
NONE,
DRAG,
ZOOM
}
ImageTouchMode mMode = ImageTouchMode.NONE;
// Remember some things for zooming
PointF mStartPoint = new PointF();
PointF mMidPoint = new PointF();
float mOldDist = 1f;
float[] mLastTouchEvent = null;
float mRotationDegrees = 0f;
float mBrightness = 0f;
public boolean onTouchEvent(MotionEvent event) {
if (!hitTestInImageMatrix(event)) {
// only handle touch events inside the transformed image matrix, not the whole image view
return false;
}
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: //first finger down only
mSavedMatrix.set(mMatrix);
mStartPoint.set(event.getX(), event.getY());
Log.d(TAG, "mode=DRAG");
mMode = ImageTouchMode.DRAG;
break;
case MotionEvent.ACTION_POINTER_DOWN:
mOldDist = spacing(event);
if (mOldDist > 10f) {
mSavedMatrix.set(mMatrix);
midPoint(mMidPoint, event);
mMode = ImageTouchMode.ZOOM;
}
mLastTouchEvent = new float[4];
mLastTouchEvent[0] = event.getX(0);
mLastTouchEvent[1] = event.getX(1);
mLastTouchEvent[2] = event.getY(0);
mLastTouchEvent[3] = event.getY(1);
mRotationDegrees = rotation(event);
break;
case MotionEvent.ACTION_UP: //first finger lifted
case MotionEvent.ACTION_POINTER_UP: //second finger lifted
mMode = ImageTouchMode.NONE;
Log.d(TAG, "mode=NONE");
break;
case MotionEvent.ACTION_MOVE:
if (mMode == ImageTouchMode.DRAG) {
float xDelta = event.getX() - mStartPoint.x;
float yDelta = event.getY() - mStartPoint.y;
mMatrix.set(mSavedMatrix);
mMatrix.postTranslate(xDelta, yDelta);
} else if (mMode == ImageTouchMode.ZOOM && event.getPointerCount() == 2) {
float newDist = spacing(event);
mMatrix.set(mSavedMatrix);
if (newDist > 10f) {
float scale = newDist / mOldDist;
Log.d(TAG, "Scale to " + scale);
mMatrix.postScale(scale, scale, mMidPoint.x, mMidPoint.y);
}
if (mLastTouchEvent != null) {
float newRot = rotation(event);
float r = newRot - mRotationDegrees;
mMatrix.postRotate(r, getMeasuredWidth() / 2,
getMeasuredHeight() / 2);
}
}
break;
}
// Perform the transformation
setImageMatrix(mMatrix);
return true; // indicate event was handled
}
private boolean hitTestInImageMatrix(MotionEvent event) {
float[] values = new float[9];
mMatrix.getValues(values);
float globalX = values[Matrix.MTRANS_X];
float globalY = values[Matrix.MTRANS_Y];
float width = values[Matrix.MSCALE_X] * getDrawable().getIntrinsicWidth();
float height = values[Matrix.MSCALE_Y] * getDrawable().getIntrinsicHeight();
float x = event.getX();
float y = event.getY();
if (x > globalX && x < (globalX + width) && y > globalY && y <
(globalY + height)) {
Log.d(TAG, "Hit test inside!");
return true;
} else {
Log.d(TAG, "Hit test outside");
return false;
}
}
This works pretty good initially, but as soon as the image is rotated to some degree the hit test no longer works correctly.
How could I correctly detect that the touch event happens inside the transformed matrix after rotation?
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 am trying to create an app where user can touch an image and zoom using multi touch gesture. I searched over the net and found few solutions to do so. I even implemented those but all my efforts are in vain. First my code was not able to detect multitouch for which i realized that we also need to add MotionEvent.ACTION_MASK in switch case. Then now when i try to zoom the using multi touch no effect takes place. Please correct me.
TIA.
OnTouchListener onTouchListener = new OnTouchListener() {
int prevX,prevY;
float scale;
float mCurrentScale = 1.0f;
#Override
public boolean onTouch(View v, MotionEvent event) {
//view.setScaleType(ImageView.ScaleType.MATRIX);
scaleGestureDetector.onTouchEvent(event);
ImageView view = (ImageView) v;
v.bringToFront();
final RelativeLayout.LayoutParams par=(RelativeLayout.LayoutParams)v.getLayoutParams();
switch(event.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_UP:
par.topMargin+=(int)event.getRawY()-prevY;
par.leftMargin+=(int)event.getRawX()-prevX;
v.setLayoutParams(par);
break;
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
lastEvent = null;
break;
case MotionEvent.ACTION_DOWN:
prevX=(int)event.getRawX();
prevY=(int)event.getRawY();
par.bottomMargin=-2*view.getHeight();
par.rightMargin=-2*view.getWidth();
v.setLayoutParams(par);
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
Log.d("hi","outside if DRAG");
mode = DRAG;
lastEvent = null;
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
Log.d("hi","Checking");
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_MOVE:
if(mode == DRAG){
par.topMargin+=(int)event.getRawY()-prevY;
prevY=(int)event.getRawY();
par.leftMargin+=(int)event.getRawX()-prevX;
prevX=(int)event.getRawX();
v.setLayoutParams(par);
}
if (mode == ZOOM) {
float newDist = spacing(event);
if (newDist > 10f) {
matrix.set(savedMatrix);
float scale = (newDist / oldDist);
matrix.postScale(scale, scale, mid.x, mid.y);
Log.e("hi", "TEsting");
}
if (lastEvent != null && event.getPointerCount() == 3) {
newRot = rotation(event);
float r = newRot - d;
float[] values = new float[9];
matrix.getValues(values);
float tx = values[2];
float ty = values[5];
float sx = values[0];
float xc = (v.getWidth() / 2) * sx;
float yc = (v.getHeight() / 2) * sx;
matrix.postRotate(r, tx + xc, ty + yc);
}
}
break;
}
//ImageView view = (ImageView) v;
view.setImageMatrix(matrix);
v.invalidate();
return true;
}
};
You may need to use the piece of code:
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
public class MultiTouchActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main);
TouchImageView img = new TouchImageView(this);
img.setImageResource(R.drawable.ice_age_2);
img.setMaxZoom(4f);
setContentView(img);
}
}
For reference use the link
here
hi am new to android platform
in my code only dragging is happening and zooming not occurs please help me
here is the code that am using
#Override
public void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
canvas.drawBitmap(image, getBounds().left,getBounds().top, new Paint());
}
/* (non-Javadoc)
* #see com.simplogics.surfaceview.Views.BaseView#onTouchEvent(android.view.MotionEvent)
*/
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;
#Override
public boolean onTouchEvent(MotionEvent event) {
boolean handled = false;
Log.d("SimpEditText", "(int)event.getX() " + (int)event.getX());
Log.d("SimpEditText", "(int)event.getY() " + (int)event.getY());
if(isTouchedInBounds((int)event.getX(), (int)event.getY())){
Log.d("SimpEditText", "Touched in Bounds");
handled = true;
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
lastPosition.x = getBounds().left;
lastPosition.y = getBounds().top;
// initialRotation = rotation(event);
Log.d("VerticalLabelView", "mode=DRAG");
mode = DRAG;
break;
case MotionEvent.ACTION_POINTER_DOWN:
mode = ZOOM;
initialScale = spacing(event);
midPoint(mid, event);
Log.d("VerticalLabelView", "mode=ZOOM");
initialRotation = rotation(event);
break;
case MotionEvent.ACTION_UP:
mode = NONE;
break;
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
Log.d("VerticalLabelView", "mode=NONE");
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
int x = (int)(event.getX() - start.x);
int y = (int)(event.getY() - start.y);
setPosition((int)lastPosition.x + x, (int)lastPosition.y + y);
}else if(mode == ZOOM){
currentScale = spacing(event);
float scale =currentScale/initialScale;
currentRotation = rotation(event);
float angle = currentRotation - initialRotation;
if(getImage().getWidth()> 100 && image.getWidth() <900){
/*Matrix testMatrix = new Matrix();
testMatrix.postScale(scale, scale,mid.x,mid.y);*/
/* rotatedImage = Bitmap.createBitmap(getImage(), 0, 0, (int)(image.getWidth()*scale),
(int)(image.getHeight()*scale), testMatrix, true);
*/
rotatedImage = Bitmap.createScaledBitmap(getImage(), (int)(image.getWidth()*scale),
(int)(image.getHeight()*scale), true);
setScale(getBounds().left,getBounds().top
, (int)(getImage().getWidth()*scale), (int)(getImage().getHeight()*scale));
/*rotatedImage =rotate(angle, image);
Matrix testMatrix = new Matrix();
testMatrix.postScale(scale, scale,mid.x,mid.y);
this.image = rotatedImage;
rotatedImage = Bitmap.createScaledBitmap(image, (int)(image.getWidth()*scale),
(int)(image.getHeight()*scale), true);
this.image = rotatedImage;*/
/*Matrix testMatrix = new Matrix();
testMatrix.postScale(scale, scale,mid.x,mid.y);*/
// this.image = rotatedImage;
/*rotatedImage = Bitmap.createScaledBitmap(image, (int)(image.getWidth()*scale),
(int)(image.getHeight()*scale), true);
this.image = rotatedImage;*/
}
this.image = rotatedImage;
}
break;
}
}
return handled;
}
this is the onDraw and touchevent methods that i used
please help me...
Hy try this tutorial and find where is your problem:
with this tutorial you can learn many feature like:
drag an image
Zoom in with pinch zoom (two fingers)
Zoom out with pinch zoom (two fingers)
Hope that's what you are looking for
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