I have a task to pinch and zoom the imageview, I have made an app where I am swiping the images and it is working fine; now I want to Pinch and Zoom the imageview on the same class ;but I am not able to do this.I have gone through various example but it seems nothing is helping in this case.Below is my code,or provide me some another example for this problem.
imageView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent event) {
bitmap = BitmapFactory.decodeResource(context.getResources(), image_id[position]);
bmpWidth = bitmap.getWidth();
bmpHeight = bitmap.getHeight();
distCurrent = 1;
dist0 = 1;
drawMatrix();
float distx, disty;
switch(event.getAction() & MotionEvent.ACTION_MASK){
case MotionEvent.ACTION_DOWN:
//A pressed gesture has started, the motion contains the initial starting location.
touchState = TOUCH;
break;
case MotionEvent.ACTION_POINTER_UP:
//A non-primary pointer has gone up.
touchState = TOUCH;
break;
case MotionEvent.ACTION_POINTER_DOWN:
//A non-primary pointer has gone down.
touchState = PINCH;
//Get the distance when the second pointer touch
distx = event.getX(0) - event.getX(1);
disty = event.getY(0) - event.getY(1);
dist0 = FloatMath.sqrt(distx * distx + disty * disty);
break;
case MotionEvent.ACTION_MOVE:
//A change has happened during a press gesture (between ACTION_DOWN and ACTION_UP).
if(touchState == PINCH){
//Get the current distance
distx = event.getX(0) - event.getX(1);
disty = event.getY(0) - event.getY(1);
distCurrent = FloatMath.sqrt(distx * distx + disty * disty);
drawMatrix();
}
break;
case MotionEvent.ACTION_UP:
//A pressed gesture has finished.
touchState = IDLE;
break;
}
touchState = IDLE;
return true;
}
});
private void drawMatrix(){
float curScale = distCurrent/dist0;
if (curScale < 0.1){
curScale = 0.1f;
}
Bitmap resizedBitmap;
int newHeight = (int) (bmpHeight * curScale);
int newWidth = (int) (bmpWidth * curScale);
System.out.println("new width: "+newWidth+" new heigt: "+newHeight);
resizedBitmap = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, false);
System.out.println("resized bitmap: "+resizedBitmap);
imageView.setImageBitmap(resizedBitmap);
}
I have to integrate the same functionality into the my application.
But as we know zoomable ImageView is not possible in android.
I have use the following library:
https://github.com/sephiroth74/ImageViewZoom
Try this. May be helps you...:)
Related
I need to click on particular item on the canvas while zooming and moving functionalities also enable for canvas. I can calculate the rectangle position while moving the canvas. There I just calculate the touch movement distance by (CurrenTouchXPosition - StartXPosition).
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
int x = (int) event.getX();
int y = (int) event.getY();
float moveOffsetX = (event.getX() - start.x);
float moveOffsetY = (event.getY() - start.y);
Then,
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
mode = DRAG;
break;
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "action up");
secondRectUpperX = secondRectUpperX + moveOffsetX;
secondRectBottomX = secondRectBottomX + moveOffsetX;
secondRectUpperY = secondRectUpperY + moveOffsetY;
secondRectBottomY = secondRectBottomY + moveOffsetY;
This can identify the new canvas position of the rectangle. This works perfectly. I can identify the touch event of particular item while moving the canvas by this logic.
But now i need to calculate the rectangle position relative to the canvas, after zoom the canvas. Whats the maths behind the zooming. If anyone knows please help in this.
Thank you.
Finally I come up with a solution.
I translate my touch position screen coordinates to canvas coordinates.
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
...
break;
case MotionEvent.ACTION_POINTER_DOWN:
...
break;
case MotionEvent.ACTION_UP:
float []m = new float[9];
matrix.getValues(m);
float transX = m[Matrix.MTRANS_X] * -1;
float transY = m[Matrix.MTRANS_Y] * -1;
float scaleX = m[Matrix.MSCALE_X];
float scaleY = m[Matrix.MSCALE_Y];
lastTouchX = (int) ((event.getX() + transX) / scaleX);
lastTouchY = (int) ((event.getY() + transY) / scaleY);
lastTouchX = Math.abs(lastTouchX);
lastTouchY = Math.abs(lastTouchY);
Thanks for Andres Cardenas Pardo's answer here
I could able to get the touch position coordinates according to the canvas coordinates. Since i know the coordinates of my drawn object, i check whether the touch position is within the range of drawn object.
if((lastTouchX>=firstRectUpperX && firstRectBottomX>=lastTouchX) && (lastTouchY>=firstRectUpperY && firstRectBottomY>=lastTouchY)) {
isbtn1Clicked = true;
}
In my application i want to get image x and y coordinates after drag and zoom the image. So I am using the following code.In my xml i have taken relative layout.This is relative layout.
<RelativeLayout
android:id="#+id/image"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="#+id/buttonlayout"
android:layout_marginTop="0dp" >
</RelativeLayout>
In this relative layout i want to drag the image and zoom the image.So that's why I have created Image view programatically and apply the touch listener to that image view.This is the oncreate code.
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.inventory_submit_images_activity);
relImage=(RelativeLayout) findViewById(R.id.image);
imgMarker = new ImageView(this);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.FILL_PARENT,
RelativeLayout.LayoutParams.FILL_PARENT);
params.leftMargin = 0;
params.topMargin = 0;
relImage.addView(imgMarker, params);
imgMarker.setScaleType(ImageView.ScaleType.MATRIX);
bmdragImage=BitmapFactory.decodeResource(getResources(), R.drawable.image_marker);
height = bmdragImage.getHeight();
width = bmdragImage.getWidth();
imgMarker.setImageBitmap(bmdragImage);
imgMarker.setOnTouchListener(this);
}
And this is the ontouch method and related methods.
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
ImageView view = (ImageView) v;
view.setScaleType(ImageView.ScaleType.MATRIX);
float scale = 0;
layoutParamss = (RelativeLayout.LayoutParams)
v.getLayoutParams();
// Handle touch events here...
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: // first finger down only
x=(int) event.getX();
y=(int) event.getY();
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
Log.d(TAG, "mode=DRAG"); // write to LogCat
mode1 = DRAG1;
break;
case MotionEvent.ACTION_UP: // first finger lifted
x_cordinate=(int) event.getX();
y_cordinate=(int) event.getY();
Log.e("x_cordinate==",""+x_cordinate+" "+bmdragImage.getHeight());
Log.e("y_cordinate==",""+y_cordinate+" "+bmdragImage.getWidth());
break;
case MotionEvent.ACTION_POINTER_UP: // second finger lifted
mode1 = NONE1;
Log.d(TAG, "mode1=NONE1");
break;
case MotionEvent.ACTION_POINTER_DOWN: //second finger down..
oldDist = spacing(event);
Log.d(TAG, "oldDist=" + oldDist);
if (oldDist > 10f) {
savedMatrix.set(matrix);
midPoint(mid, event);
mode1 = ZOOM1;
Log.d(TAG, "mode=ZOOM" );
}
break;
case MotionEvent.ACTION_MOVE:
Log.e("ACTION_MOVE","ACTION_MOVE");
if (mode1 == DRAG1){
matrix.set(savedMatrix);
matrix.postTranslate(event.getX() - start.x, event.getY() - start.y); // create the transformation in the matrix of points
}
else if (mode1 == ZOOM1){
// pinch zooming
float newDist = spacing(event);
Log.d(TAG, "newDist=" + newDist);
if (newDist > 5f){
matrix.set(savedMatrix);
scale = newDist / oldDist; // setting the scaling of the image..
matrix.postScale(scale, scale, mid.x, mid.y);
height=(int) (bmdragImage.getHeight()*scale);
width=(int) (bmdragImage.getWidth()*scale);
Log.e("height int===="+scale,""+height);
Log.e("width int====="+scale,""+width);
}
}
break;
}
view.setImageMatrix(matrix); // display the transformation on screen
return true; // indicate event was handled
}
/** 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);
}
This code is dragging and zooming is working fine. But my problem is getting x coordinate and y coordinate after drag the image.These x and y values are getting wrong. Because where ever i touch the image with in the relative layout, dragging is working.Because of these code in oncreate of image view params. So x and y values are getting wrong.
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.FILL_PARENT,
RelativeLayout.LayoutParams.FILL_PARENT);
If i change the image layout params to wrap contnet like the bellow,the image dragging is with in that area only. And zooming also not working properly. But my requirement is drag the image total relative layout.And get the x and y coordinates of image view.
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
Suggestions are appreciated..
I am trying to move an ImageView (not rotate). The movement is supposed to be on the edge of a circle. This circle is also an image view.
based on the onTouch, ACTION_MOVE event, I am trying to move it.
Noe the Dilema is that the use may not move the finger in a perfectly circular fashion but I would like to make sure that the image still moves around edge of this circle.
I am currently using the following inside ACTION_MOVE:
mCurrTempIndicator.setTranslationX(event.getX());
mCurrTempIndicator.setTranslationY(event.getY());
But this will not move in a perfect circle.
could someone please help.
UPDATE: code
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mInitialX = event.getX();
mInitialY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
mEndX = event.getX();
mEndY = event.getY();
float deltaX = mEndX - mInitialX;
float deltaY = mEndY - mInitialY;
double angleInDegrees = Math.atan(deltaY / deltaX) * 180 / Math.PI;
mInitialX = mEndX;
mInitialY = mEndY;
mCurrTempIndicator.setRotation((float)angleInDegrees);
mCurrTempIndicator.setTranslationX((float)(310*(Math.cos(angleInDegrees))));
mCurrTempIndicator.setTranslationY((float)(310*(Math.sin(angleInDegrees))));
break;
case MotionEvent.ACTION_UP:
allowRotating = true;
break;
}
return true;
}
calculate the center Point of the Circle
get the current touch point
calculate the angle between center and new touch point
Calculate the point on the circle using angle and radius of circle (x = r * cos(angle), y = r * sin(angle)).
Reset the image position to the new point.
To get the angle use the below equation
deltaY = P2_y - P1_y
deltaX = P2_x - P1_x
angleInDegrees = arctan(deltaY / deltaX) * 180 / PI
//Code inside ACTION_MOVE case
mInitialX = event.getX();
mInitialY = event.getY();
float deltaX = circleCenter.x - mInitialX;
float deltaY = circleCenter.y - mInitialY;
double angleInRadian = Math.atan2(yDiff, xDiff);
PointF pointOnCircle = new PointF();
pointOnCircle.x = circleCenter.x + ((float)(circleRadius*(Math.cos(angleInRadian))));
pointOnCircle.y = circleCenter.y + ((float)(circleRadius*(Math.cos(angleInRadian))));
I have an ImageView to which I added an OnTouchListener so that I can zoom in and out the latter using pinch gesture with two fingers. I'm using the code below, but I'm having a problem with resizing the actual Bitmap. The latter gets blurred, stays the same size, and gets overlayed with a resized version of the image, as if it just puts the new image on top of the old one instead of replacing it. I'm thinking there's a problem with the drawMatrix method...
int touchState;
final int IDLE = 0;
final int TOUCH = 1;
final int PINCH = 2;
float dist0, distCurrent;
public boolean onTouch(View view, MotionEvent event) {
boolean handledHere = false;
float distx, disty;
final int action = event.getAction();
switch(action & MotionEvent.ACTION_MASK){
case MotionEvent.ACTION_DOWN:
//A pressed gesture has started, the motion contains the initial starting location.
touchState = TOUCH;
break;
case MotionEvent.ACTION_POINTER_DOWN:
//A non-primary pointer has gone down.
touchState = PINCH;
//Get the distance when the second pointer touch
distx = event.getX(0) - event.getX(1);
disty = event.getY(0) - event.getY(1);
dist0 = FloatMath.sqrt(distx * distx + disty * disty);
break;
case MotionEvent.ACTION_MOVE:
//A change has happened during a press gesture (between ACTION_DOWN and ACTION_UP).
if(touchState == PINCH){
//Get the current distance
distx = event.getX(0) - event.getX(1);
disty = event.getY(0) - event.getY(1);
distCurrent = FloatMath.sqrt(distx * distx + disty * disty);
drawMatrix((ImageView) view);
}
break;
case MotionEvent.ACTION_UP:
//A pressed gesture has finished.
touchState = IDLE;
break;
case MotionEvent.ACTION_POINTER_UP:
//A non-primary pointer has gone up.
touchState = TOUCH;
break;
}
return handledHere;
}
private void drawMatrix(ImageView view){
float curScale = distCurrent/dist0;
if (curScale < 0.1){
curScale = 0.1f;
}
view.buildDrawingCache();
Bitmap originalBitmap = view.getDrawingCache();
Bitmap resizedBitmap;
int newHeight = (int) (view.getHeight() * curScale);
int newWidth = (int) (view.getWidth() * curScale);
resizedBitmap = Bitmap.createScaledBitmap(originalBitmap, newWidth, newHeight, false);
view.setImageBitmap(resizedBitmap);
}
When I zoom in and out several times, here's how it becomes...
Thanks for any help I can get.
Actualy you are calling the draw function in onmove so it keeps on calling it when you move your finger.So the images generated with the new height and width are been setting to imageview which is alredy set der. Try removing all the views before setting the resized image to the imageview.Hope this will solve your problem.
private void drawMatrix(ImageView view){
float curScale = distCurrent/dist0;
if (curScale < 0.1){
curScale = 0.1f;
}
view.buildDrawingCache();
Bitmap originalBitmap = view.getDrawingCache();
Bitmap resizedBitmap;
int newHeight = (int) (view.getHeight() * curScale);
int newWidth = (int) (view.getWidth() * curScale);
resizedBitmap = Bitmap.createScaledBitmap(originalBitmap, newWidth, newHeight, false);
view.removeallviews();
view.setImageBitmap(resizedBitmap);
}
You may use the TouchImageView class [https://github.com/MikeOrtiz/TouchImageView/blob/master/src/com/example/touch/TouchImageView.java].
Which gives nice Zoom in and zoom out functionality for image view.
MY imageview does not move when ontouch event is called...Though I can see ACTION_UP and ACTION_MOVE happening from the log traces.
Here is my code:-
public boolean onTouch(View v, MotionEvent motion) {
float dy ;
float dx;
double r = 0;
float angle = 0;
switch(v.getId())
{
case R.id.arrow:
switch(motion.getActionMasked())
{
case MotionEvent.ACTION_MOVE:
Log.w(this.getClass().getName(),"In MOTION MOVE");
mX1 = motion.getX();
mY1 = motion.getY();
break;
case MotionEvent.ACTION_UP:
Log.w(this.getClass().getName(),"In MOTION UP");
mX2 = motion.getX();
mY2 = motion.getY();
dy = -( mY2-mY1);
dx = -(mX2-mX1);
r = Math.atan2(dy, dx);
angle = (int)Math.toDegrees(r);
updateRotation(angle);
break;
default:
break;
}
Log.d(this.getClass().getName(), "Value of angle in ontouch: " + Double.toString(angle));
break;
default:
break;
}
return true;
}
Basically I am trying to calculate the angular displacement when user touches the imageview and changes its position.
======================================
Edit:- Below is my update rotation function:-
public void updateRotation(double angle)
{
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.arrow);
int w = bitmap.getWidth();
int h = bitmap.getHeight();
String filePath = null;
Matrix mtx = new Matrix();
Log.d(this.getClass().getName(), "Value: " + Double.toString(angle));
if(angle > 90)
angle = 90;
mtx.postRotate((float) angle);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, true);
Drawable draw =new BitmapDrawable(bitmap);
mImageView.setBackgroundDrawable(draw);
}
=========================================================
Edit 2: Modified function
public boolean onTouch(View v, MotionEvent motion) {
float dy ;
float dx;
double r = 0;
switch(v.getId())
{
case R.id.arrow:
switch(motion.getActionMasked())
{
case MotionEvent.ACTION_DOWN:
mX1 = motion.getX();
mY1 = motion.getY();
break;
case MotionEvent.ACTION_MOVE:
Log.w(this.getClass().getName(),"In MOTION MOVE");
mX1 = motion.getX();
mY1 = motion.getY();
break;
case MotionEvent.ACTION_UP:
Log.w(this.getClass().getName(),"In MOTION UP");
mX2 = motion.getX();
mY2 = motion.getY();
dy = -( mY2-mY1);
dx = -(mX2-mX1);
r = Math.atan2(dy, dx);
mAngle = (int)Math.toDegrees(r);
updateRotation();
break;
default:
break;
}
Log.d(this.getClass().getName(), "Value of angle in ontouch: " + Float.toString(mAngle));
break;
default:
break;
}
return true;
}
=============================================
Edit 3:- Strange thing is that if i modify my onTouch event and perform all the calculations in ACTION_MOVE the imageview responds for each touch event, but this is not what I want as I cannot do the proper angluar rotation in ACTION_MOVE event
Ruchira,
From looking at your code and working with many onTouch innovations myself, I can tell you that the problem is definitely in your onTouchEvent(). You record the mX1 and mY1 in your ACTION_MOVE without recording the original ACTION_DOWN event. This means that every time you move your finger, the onTouchEvent() method thinks that this is the original position. Try this instead:
public boolean onTouch(View v, MotionEvent motion) {
float dy ;
float dx;
double r = 0;
float angle = 0;
switch(v.getId())
{
case R.id.arrow:
switch(motion.getActionMasked())
{
case MotionEvent.ACTION_DOWN:
mX1 = motion.getX();
mY1 = motion.getY();
break;
case MotionEvent.ACTION_MOVE:
Log.w(this.getClass().getName(),"In MOTION MOVE");
//Just to support real time rotation, you could:
mX2 = motion.getX();
mY2 = motion.getY();
//... some rotation code.
break;
case MotionEvent.ACTION_UP:
Log.w(this.getClass().getName(),"In MOTION UP");
// All of this should be fine.
mX2 = motion.getX();
mY2 = motion.getY();
dy = -( mY2-mY1);
dx = -(mX2-mX1);
r = Math.atan2(dy, dx);
angle = (int)Math.toDegrees(r);
updateRotation(angle);
break;
default:
break;
}
Log.d(this.getClass().getName(), "Value of angle in ontouch: " + Double.toString(angle));
break;
default:
break;
}
return true;
}
That should at least track your numbers correctly, so that they may actually be used.
Additional Considerations
The most common mistake is to make sure that you have not set any of your tracking variables to final. If this is the case, the code will set it once, but not again...
When dealing with Drawables, it is important to get rid of the original Drawable and its callback. The reason for this is a well known issue that was never resolved in Android because a decision couldn't be made as to when exactly it was necessary and when it wasn't. Essentially, Android will eventually run out of memory, but will not necessarily tell you and just fail to update the image. This is how you do this (when replacing an image).
Drawable trash = mImageView.getBackgroundDrawable();
if (trash != null)
{ trash.setCallback(null);
trash.recycle();
trash = null;
}
mImageView.setBackgroundDrawable(draw);
Finally, you have to let the image know that it has to redraw itself. This isn't always the case, but is true more often than not. mImageView.invalidate(); will often resolve the issue, and should be placed on the last line in your updateRotation() method.
FuzzicalLogic