Android pinch zoom, ZoomOut and ZoomIn, in limit - android

I have already searched alot around here but haven't found a solution yet. So the thing is , I have a image 3000*3000 px, I set it to scale type matrix. Now I have only the left corner showing of the image. Now I don't want people to zoom in at this stage, only zoom out to a maximum of 'x' (Still have to see how much).
It's the imageview "kaart" that has to be zoomed.
question : How to let people only zoom out in the beginning and don't let them pass x ?and afterwards, don't let them zoom in more than the original size
i've tried with negative values , but that didn't work
Here is my code:
// These matrices will be used to move and zoom image
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();
// max and min zoom
//private static final float MIN_ZOOM = 1.0f;
//private static final float MAX_ZOOM = 5.0f;
//scale = Math.max(MIN_ZOOM, Math.min(scale, MAX_ZOOM));
// We can be in one of these 3 states
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;
// Remember some things for zooming
PointF start = new PointF();
PointF mid = new PointF();
float oldDist = 1f;
String savedItemClicked;
ImageView view;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.kaart);
view = (ImageView) findViewById(R.id.kaart);
view.setOnTouchListener(this);
}
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
ImageView view = (ImageView) v;
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;
}
limitZoom(matrix);
limitDrag(matrix);
view.setImageMatrix(matrix);
return true;
}
#SuppressWarnings("deprecation")
private void dumpEvent(MotionEvent event) {
String names[] = { "DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE",
"POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?" };
StringBuilder sb = new StringBuilder();
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
sb.append("event ACTION_").append(names[actionCode]);
if (actionCode == MotionEvent.ACTION_POINTER_DOWN
|| actionCode == MotionEvent.ACTION_POINTER_UP) {
sb.append("(pid ").append(
action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
sb.append(")");
}
sb.append("[");
for (int i = 0; i < event.getPointerCount(); i++) {
sb.append("#").append(i);
sb.append("(pid ").append(event.getPointerId(i));
sb.append(")=").append((int) event.getX(i));
sb.append(",").append((int) event.getY(i));
if (i + 1 < event.getPointerCount())
sb.append(";");
}
sb.append("]");
// Log.d(TAG, sb.toString());
}
/** 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);
}
}
Hope someone can help me. I would really apreciate it.
EDIT
I have added 2 functions i found somewhere.
private void limitZoom(Matrix m) {
float[] values = new float[9];
m.getValues(values);
float scaleX = values[Matrix.MSCALE_X];
float scaleY = values[Matrix.MSCALE_Y];
if(scaleX > MAX_ZOOM) {
scaleX = MAX_ZOOM;
} else if(scaleX < MIN_ZOOM) {
scaleX = MIN_ZOOM;
}
if(scaleY > MAX_ZOOM) {
scaleY = MAX_ZOOM;
} else if(scaleY < MIN_ZOOM) {
scaleY = MIN_ZOOM;
}
values[Matrix.MSCALE_X] = scaleX;
values[Matrix.MSCALE_Y] = scaleY;
m.setValues(values);
}
and
private void limitDrag(Matrix m) {
float[] values = new float[9];
m.getValues(values);
float transX = values[Matrix.MTRANS_X];
float transY = values[Matrix.MTRANS_Y];
float scaleX = values[Matrix.MSCALE_X];
float scaleY = values[Matrix.MSCALE_Y];
ImageView iv = (ImageView)findViewById(R.id.kaart);
Rect bounds = iv.getDrawable().getBounds();
int viewWidth = getResources().getDisplayMetrics().widthPixels;
int viewHeight = getResources().getDisplayMetrics().heightPixels;
int width = bounds.right - bounds.left;
int height = bounds.bottom - bounds.top;
float minX = (-width + 20) * scaleX;
float minY = (-height + 20) * scaleY;
if(transX > (viewWidth - 20)) {
transX = viewWidth - 20;
} else if(transX < minX) {
transX = minX;
}
if(transY > (viewHeight - 80)) {
transY = viewHeight - 80;
} else if(transY < minY) {
transY = minY;
}
values[Matrix.MTRANS_X] = transX;
values[Matrix.MTRANS_Y] = transY;
m.setValues(values);
}
I have to call these functions right after the switch case and just before is set the imagematrix on my view. (I have added this to my previous code here above)
maybe this isn't the best solution , but it works .

If I understand your code, you keep saving the matrix as a way to remember what was the state of the zoom and scroll (which could be done by one matrix alone, but maybe you're doing other manipulations).
This way, every action effects are relative to the last state, and it's difficult to set an absolute limit to the zoom (and scroll too): to get their absolute values, you'd have to call matrix.getValues() and pick the values from the array it returns.
Try instead saving the current x-scroll, y-scroll and zoom, and at every action check if their updated values (that now remain absolute) would be inside your limits; if so actually update them, then reset the matrix and set the new scroll/zoom values.
(also, consider the use of GestureDetector and ScaleGestureDetector)

Related

Moving image with it's touch event in android?

I am trying to make that one image is moving automatically in whole screen as per resolution of mobile device. Now, i want its click or touch event. I have refereed many things but couldn't find any relevant answer. Is there any example related it so i can refer in android.
Use following code to move image.I am using it and it is working fine for me.
import android.graphics.Matrix;
import android.graphics.PointF;
import android.util.FloatMath;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView;
public class Touch implements OnTouchListener {
// These matrices will be used to move and zoom image
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();
// We can be in one of these 3 states
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;
// Remember some things for zooming
PointF start = new PointF();
PointF mid = new PointF();
float oldDist = 1f;
#Override
public boolean onTouch(View v, MotionEvent event) {
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());
mode = DRAG;
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
if (oldDist > 10f) {
savedMatrix.set(matrix);
midPoint(mid, event);
mode = ZOOM;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
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);
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
}
/** Show an event in the LogCat view, for debugging */
private void dumpEvent(MotionEvent event) {
String names[] = { "DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE", "POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?" };
StringBuilder sb = new StringBuilder();
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
sb.append("event ACTION_").append(names[actionCode]);
if (actionCode == MotionEvent.ACTION_POINTER_DOWN || actionCode == MotionEvent.ACTION_POINTER_UP) {
sb.append("(pid ").append(action >> MotionEvent.ACTION_POINTER_INDEX_SHIFT);
sb.append(")");
}
sb.append("[");
for (int i = 0; i < event.getPointerCount(); i++) {
sb.append("#").append(i);
sb.append("(pid ").append(event.getPointerId(i));
sb.append(")=").append((int) event.getX(i));
sb.append(",").append((int) event.getY(i));
if (i + 1 < event.getPointerCount())
sb.append(";");
}
sb.append("]");
}
/** 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);
}
}
note->
from your activity attach this listener by doing
imageview.setOnTouchListener(new Touch());
public class MainActivity extends Activity implements View.OnTouchListener {
// these matrices will be used to move and zoom image
private Matrix matrix = new Matrix();
private Matrix savedMatrix = new Matrix();
// we can be in one of these 3 states
private static final int NONE = 0;
private static final int DRAG = 1;
private static final int ZOOM = 2;
private int mode = NONE;
// remember some things for zooming
private PointF start = new PointF();
private PointF mid = new PointF();
private float oldDist = 1f;
private float d = 0f;
private float newRot = 0f;
private float[] lastEvent = null;
private ImageView view, fin;
private Bitmap bmap;
FrameLayout frm;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
view = (ImageView) findViewById(R.id.imageView);
fin = (ImageView) findViewById(R.id.imageView1);
view.setOnTouchListener(this);
frm=(FrameLayout)findViewById(R.id.frm);
}
public boolean onTouch(View v, MotionEvent event) {
// handle touch events here
view = (ImageView) v;
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);
}
if (lastEvent != null && event.getPointerCount() == 2 || 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 = (view.getWidth() / 2) * sx;
float yc = (view.getHeight() / 2) * sx;
matrix.postRotate(r, tx + xc, ty + yc);
}
}
break;
}
view.setBackgroundResource(R.mipmap.ic_launcher);
view.setImageMatrix(matrix);
bmap= Bitmap.createBitmap(view.getWidth(), view.getHeight(), Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bmap);
view.draw(canvas);
//fin.setImageBitmap(bmap);
return true;
}
public void ButtonClick(View v){
fin.setImageBitmap(bmap);
}
/**
* 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);
float s=x * x + y * y;
return (float)Math.sqrt(s);
}
/**
* 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);
}
/**
* Calculate the degree to be rotated by.
*
* #param event
* #return Degrees
*/
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);
return (float) Math.toDegrees(radians);
}
}
-> Try this.

Android ImageView zoom, rotate, position change prob

I am using a the following code for a image, which will rotate, zoom in-out with pinch. User can make its position by dragging it.
activity_img_match.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#drawable/brokenbone"
android:gravity="center_vertical|center_horizontal" >
<ImageView
android:id="#+id/bonestruct"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="matrix"
android:src="#drawable/bone" />
</FrameLayout>
ImgMatchActivity.java
public class ImgMatchActivity extends Activity implements OnTouchListener {
private static final String TAG = "Touch";
// These matrices will be used to move and zoom image
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();
Matrix savedMatrix2 = new Matrix();
// We can be in one of these 3 states
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
private static final float MAX_ZOOM = 5.0f;
private static final float MIN_ZOOM = 0.15f;
int mode = NONE;
// Remember some things for zooming
PointF start = new PointF();
PointF mid = new PointF();
float oldDist = 1f;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_img_match);
ImageView bonestruct = (ImageView) findViewById(R.id.bonestruct);
bonestruct.setOnTouchListener(this);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_img_match, menu);
return true;
}
public boolean onTouch(View selectedView, MotionEvent event) {
// TODO Auto-generated method stub
ImageView view = (ImageView) selectedView;
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;
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:
mode = NONE;
Log.d(TAG, "mode=NONE");
savedMatrix.set(matrix);
//matrix.postRotate(90);
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);
fixing();
view.setImageMatrix(savedMatrix2);
return true; // indicate event was handled
}
public void fixing() {
float[] value = new float[9];
matrix.getValues(value);
float[] savedValue = new float[9];
savedMatrix.getValues(savedValue);
WindowManager mWinMgr = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
int width = mWinMgr.getDefaultDisplay().getWidth();
int height = mWinMgr.getDefaultDisplay().getHeight();
Drawable d = this.getResources().getDrawable(R.drawable.bone);
if (d == null)
return;
int imageWidth = d.getIntrinsicWidth();
int imageHeight = d.getIntrinsicHeight();
int scaleWidth = (int) (imageWidth * value[0]);
int scaleHeight = (int) (imageHeight * value[4]);
// don't let the image go outside
if (value[2] > width - 1)
value[2] = width - 10;
else if (value[5] > height - 1)
value[5] = height - 10;
else if (value[2] < -(scaleWidth - 1))
value[2] = -(scaleWidth - 10);
else if (value[5] < -(scaleHeight - 1))
value[5] = -(scaleHeight - 10);
//Log.d("value[0]", "value[0]: "+value[0]);
//Log.d("value[4]", "value[4]: "+value[4]);
// maximum zoom ratio: MAX
if (value[0] > MAX_ZOOM || value[4] > MAX_ZOOM) {
value[0] = MAX_ZOOM;
value[4] = MAX_ZOOM;
// value[2] = savedValue[2];
// value[5] = savedValue[5];
}
// minimum zoom ratio: MIN
/*if (value[0] < MIN_ZOOM || value[4] < MIN_ZOOM) {
value[0] = MIN_ZOOM;
value[4] = MIN_ZOOM;
// value[2] = savedValue[2];
// value[5] = savedValue[5];
}*/
matrix.setValues(value);
savedMatrix2.set(matrix);
}
/** Show an event in the LogCat view, for debugging */
private void dumpEvent(MotionEvent event) {
String names[] = { "DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE",
"POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?" };
StringBuilder sb = new StringBuilder();
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
sb.append("event ACTION_").append(names[actionCode]);
if (actionCode == MotionEvent.ACTION_POINTER_DOWN
|| actionCode == MotionEvent.ACTION_POINTER_UP) {
sb.append("(pid ").append(
action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
sb.append(")");
}
sb.append("[");
for (int i = 0; i < event.getPointerCount(); i++) {
sb.append("#").append(i);
sb.append("(pid ").append(event.getPointerId(i));
sb.append(")=").append((int) event.getX(i));
sb.append(",").append((int) event.getY(i));
if (i + 1 < event.getPointerCount())
sb.append(";");
}
sb.append("]");
Log.d(TAG, sb.toString());
}
/** 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);
}
}
Now I can drag the image, zoom it-out by pinch action. But there is no rotation at all. How to get the rotation action and get rotation angle?
Try with below code its working for me.
float[] lastEvent = null;
float d = 0f;
float newRot = 0f;
public boolean onTouch(View v, MotionEvent event) {
ImageView view = (ImageView) v;
// Handle touch events here...
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);
matrix.postTranslate(event.getX() - start.x, event.getY()
- start.y);
} else if (mode == ZOOM && event.getPointerCount() == 2) {
float newDist = spacing(event);
matrix.set(savedMatrix);
if (newDist > 10f) {
float scale = newDist / oldDist;
matrix.postScale(scale, scale, mid.x, mid.y);
}
if (lastEvent != null) {
newRot = rotation(event);
float r = newRot - d;
matrix.postRotate(r, view.getMeasuredWidth() / 2,
view.getMeasuredHeight() / 2);
}
}
break;
}
view.setImageMatrix(matrix);
return true;
}
//For rotate image on multi-touch.
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);
return (float) Math.toDegrees(radians);
}

Zoom & Drag an Image in android

I need to drag and zoom an image. Here is my code ...
//instance variables
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;
//OnTouchListener for imageview
OnTouchListener touchAction = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
ImageView i = (ImageView)v;
dragAndZoom(i, event);
return true;
}
};
//performing drag&zoom operations
private void dragAndZoom(View v, MotionEvent event)
{
ImageView view = (ImageView) v;
view.setScaleType(ImageView.ScaleType.MATRIX);
float scale;
// Handle touch events here...
switch (event.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
// first finger down only
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
mode = DRAG;
break;
case MotionEvent.ACTION_UP: // first finger lifted
case MotionEvent.ACTION_POINTER_UP: // second finger lifted
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);
mode = ZOOM;
}
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)
{
// pinch zooming
float newDist = spacing(event);
if (newDist > 5f)
{
matrix.set(savedMatrix);
scale = newDist / oldDist;
matrix.postScale(scale, scale, mid.x, mid.y);
}
}
break;
}
view.setImageMatrix(matrix); // display the transformation on screen
}
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);
}
It works okay. but sometimes the image is going out of screen boundaries on touch of it. How can i keep the image in UI all the times? Please help me. Thanks in advance.
private void limitDrag(Matrix m, ImageView view) {
float[] values = new float[9];
m.getValues(values);
float transX = values[Matrix.MTRANS_X];
float transY = values[Matrix.MTRANS_Y];
float scaleX = values[Matrix.MSCALE_X];
float scaleY = values[Matrix.MSCALE_Y];
Rect bounds = view.getDrawable().getBounds();
int viewWidth = getResources().getDisplayMetrics().widthPixels;
int viewHeight = getResources().getDisplayMetrics().heightPixels;
if(viewHeight<=480)
{
_y_up=0;
}
if(viewHeight>480&&viewHeight<980)
{
_y_up=140;
}
int width = bounds.right - bounds.left;
int height = bounds.bottom - bounds.top;
int __width=width;
int __height=height;
width = viewWidth / 2;
height = viewHeight / 2;
//height = 200 ;
float minX = (-width) ;//* scaleX;
float minY = (-height) ;//* scaleY;
if ((transX) > (viewWidth)) {
//_x_left
transX = viewWidth;
} else if (transX < minX) {
transX = minX;
}
if ((-transX) > (viewWidth)) {
// _x_right
transX = -(viewWidth);
} else if (-transX < minX) {
transX = -(minX+30);
}
if ((transY) > (viewHeight)) {
// _y_up
transY =( viewHeight);
} else if (transY < minY) {
transY = (minY+_y_up);
}
if ((-transY) > (viewHeight)) {
// _y_down
transY = -(viewHeight);
} else if (-transY < minY) {
transY = -(minY+170);
}
values[Matrix.MTRANS_X] = transX;
values[Matrix.MTRANS_Y] = transY;
m.setValues(values);
}
Call this function limitDrag(matrix,view) just above your view.setImageMatrix(matrix) to restrict the movement inside the screen
Note:- IM doing some ugly hardcoded calculation inside it. you can change it according to your need.Just run this and check, remember its not the best solution still something is better than nothing.Happy coding!

My Android Finger paint app sends paint to irregular places on image

In my Android Finger Paint app I used two view's imageview backside and paintview on top, it's working fine for painting and erasing. But after saving the real painting position is changed.
removed dead links to images
Code is
public MyView(Context c) {
super(c);
//mBitmap = Bitmap.createScaledBitmap(originalBitmap,bw,bh,true);
mBitmap = Bitmap.createBitmap(bw,bh,Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
mBitmapPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath(mPath, mPaint);
}
myimage.setOnTouchListener(this);
public boolean onTouch(View v, MotionEvent event) {
ImageView myimage = (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) {
/////// limiting the panning
matrix.getValues(matrixValues);
float currentY = matrixValues[Matrix.MTRANS_Y];
float currentX = matrixValues[Matrix.MTRANS_X];
float currentScale = matrixValues[Matrix.MSCALE_X];
float currentHeight = height * currentScale;
float currentWidth = width * currentScale;
float dx = event.getX() - start.x;
float dy = event.getY() - start.y;
float newX = currentX+dx;
float newY = currentY+dy;
RectF drawingRect = new RectF(newX, newY, newX+currentWidth, newY+currentHeight);
float diffUp = Math.min(viewRect.bottom-drawingRect.bottom, viewRect.top-drawingRect.top);
float diffDown = Math.max(viewRect.bottom-drawingRect.bottom, viewRect.top-drawingRect.top);
float diffLeft = Math.min(viewRect.left-drawingRect.left, viewRect.right-drawingRect.right);
float diffRight = Math.max(viewRect.left-drawingRect.left, viewRect.right-drawingRect.right);
if(diffUp > 0 ){ dy +=diffUp; }
if(diffDown < 0){ dy +=diffDown; }
if( diffLeft> 0){ dx += diffLeft; }
if(diffRight < 0){dx += diffRight; }
matrix.postTranslate(dx, dy);
}
else if (mode == ZOOM) {
float newDist = spacing(event);
Log.d(TAG, "newDist=" + newDist);
if (newDist > 10f) {
matrix.set(savedMatrix);
float scale = newDist / oldDist;
matrix.getValues(matrixValues);
float currentScale = matrixValues[Matrix.MSCALE_X];
// limit zoom
if (scale * currentScale > maxZoom) {
scale = maxZoom / currentScale;
}
else if(scale * currentScale < minZoom){
scale = minZoom / currentScale;
}
matrix.postScale(scale, scale, mid.x, mid.y);
}
}
break;
}
myimage.setImageMatrix(matrix);
return true; // indicate event was handled
}
// Show an event in the LogCat view, for debugging
private void dumpEvent(MotionEvent event) {
String names[] = { "DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE",
"POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?" };
StringBuilder sb = new StringBuilder();
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
sb.append("event ACTION_").append(names[actionCode]);
if (actionCode == MotionEvent.ACTION_POINTER_DOWN || actionCode == MotionEvent.ACTION_POINTER_UP) {
sb.append("(pid ").append(action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
sb.append(")");
}
sb.append("[");
for (int i = 0; i < event.getPointerCount(); i++) {
sb.append("#").append(i);
sb.append("(pid ").append(event.getPointerId(i));
sb.append(")=").append((int) event.getX(i));
sb.append(",").append((int) event.getY(i));
if (i + 1 < event.getPointerCount())
sb.append(";");
}
sb.append("]");
Log.d(TAG, sb.toString());
}
//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);
}
}
The position does not change! If you look closely at your linked images, you'll see that your fingerpainting does not move relative to the corner of the photo. In other words, you are saving the drawings' positions relative to the screen rather than relative to the current scroll position of the photo.
When you save, you have to take into account the fact that the background photo is cropped and/or scaled.
Maintain a paint/erase mode. When in paint/erase mode, disallow panning and zooming. When paint/erase is turned off, get the drawing cache and set it to imageview. Now the changes are permanently in the bitmap. So you can pan/zoom now and again enter the paint/erase mode to edit further.

android imageView: setting drag and pinch zoom parameters

I am currently developing for Android (my first app) an application which lets users see the subway map and be able to pinch zoom and drag around.
I am currently modifying the code found in Hello Android, 3rd Edition and got the pinch zooming and dragging to work. I'm using Matrix as my layout scale.
However I now have 3 problems:
I tried many things to limit the drag parameters but I can't seem to stop it being dragged off the parent view (and can actually disappear from view). I've tried setting layout parameters in the XML file and it just doesn't work.
I can pinch zoom fine but I have trouble, again, limiting the amount of zoom. I'm trying to play around with setting a max_zoom and min_zoom to limit the scaling value (i will post my code after)
I also have trouble trying to map a coordinate on my image so that people can click on certain parts (the whole point of this is to let users click a station on the map and view information about it)
I have a feeling i'm having trouble because I'm using the matrix scale.
Here is my current code:
Touch.java
package org.example.touch;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.os.Bundle;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.GridView;
import android.widget.ImageView;
public class Touch extends Activity implements OnTouchListener {
private static final String TAG = "Touch";
private static final float MIN_ZOOM = 1.0f;
private static final float MAX_ZOOM = 5.0f;
// These matrices will be used to move and zoom image
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();
// We can be in one of these 3 states
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;
// Remember some things for zooming
PointF start = new PointF();
PointF mid = new PointF();
float oldDist = 1f;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ImageView view = (ImageView) findViewById(R.id.imageView);
//view.setLayoutParams(new GridView.LayoutParams(85, 85));
view.setScaleType(ImageView.ScaleType.FIT_CENTER);
view.setOnTouchListener(this);
}
public boolean onTouch(View v, MotionEvent event) {
ImageView view = (ImageView) v;
view.setScaleType(ImageView.ScaleType.MATRIX);
float scale;
// Dump touch event to log
dumpEvent(event);
// Handle touch events here...
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_UP: //first finger lifted
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
matrix.set(savedMatrix);
if (view.getLeft() >= -392){
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
}
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);
}
/** Show an event in the LogCat view, for debugging */
private void dumpEvent(MotionEvent event) {
String names[] = { "DOWN" , "UP" , "MOVE" , "CANCEL" , "OUTSIDE" ,
"POINTER_DOWN" , "POINTER_UP" , "7?" , "8?" , "9?" };
StringBuilder sb = new StringBuilder();
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
sb.append("event ACTION_" ).append(names[actionCode]);
if (actionCode == MotionEvent.ACTION_POINTER_DOWN
|| actionCode == MotionEvent.ACTION_POINTER_UP) {
sb.append("(pid " ).append(
action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
sb.append(")" );
}
sb.append("[" );
for (int i = 0; i < event.getPointerCount(); i++) {
sb.append("#" ).append(i);
sb.append("(pid " ).append(event.getPointerId(i));
sb.append(")=" ).append((int) event.getX(i));
sb.append("," ).append((int) event.getY(i));
if (i + 1 < event.getPointerCount())
sb.append(";" );
}
sb.append("]" );
Log.d(TAG, sb.toString());
}
}
main.xml (rather simple nothing really complicated):
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<ImageView android:id="#+id/imageView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:src="#drawable/map"
android:scaleType="matrix" >
</ImageView>
</FrameLayout>
AndroidManifest.xml (only added the theme so there is no title bar and is full screen)
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.example.touch"
android:versionCode="7"
android:versionName="1.0" >
<application android:icon="#drawable/icon" android:label="#string/app_name" android:theme="#android:style/Theme.NoTitleBar.Fullscreen" >
<activity android:name=".Touch"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="3" android:targetSdkVersion="7" />
</manifest>
I just created this:
https://github.com/jasonpolites/gesture-imageview
Might be useful for someone...
Another option that might work for some is to use a WebView, which has built in zoom controls.
WebView webView = new WebView(this);
webView.setBackgroundColor(0xff000000);
webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
webView.getSettings().setBuiltInZoomControls(true);
webView.getSettings().setSupportZoom(true);
//webView.getSettings().setDisplayZoomControls(false); // API 11
webView.loadDataWithBaseURL(null, getHtml(), "text/html", "UTF-8", null);
mainView.addView(webView, -1, -2);
I know this is old but I was looking into doing this and have a solution that works pretty well. Right after your switch statement and before you set the matrix, you can limit the zoom like so:
private void limitZoom(Matrix m) {
float[] values = new float[9];
m.getValues(values);
float scaleX = values[Matrix.MSCALE_X];
float scaleY = values[Matrix.MSCALE_Y];
if(scaleX > MAX_ZOOM) {
scaleX = MAX_ZOOM;
} else if(scaleX < MIN_ZOOM) {
scaleX = MIN_ZOOM;
}
if(scaleY > MAX_ZOOM) {
scaleY = MAX_ZOOM;
} else if(scaleY < MIN_ZOOM) {
scaleY = MIN_ZOOM;
}
values[Matrix.MSCALE_X] = scaleX;
values[Matrix.MSCALE_Y] = scaleY;
m.setValues(values);
}
I'm still working out how to limit the translation but this should work for the zoom limiting.
EDIT: Here's a solution for limiting the translation. Just as a note, I'm doing this for a full screen image view which is why I use the display width and height in my limiting factors but you could just as easily use the width and height of your view instead.
private void limitDrag(Matrix m) {
float[] values = new float[9];
m.getValues(values);
float transX = values[Matrix.MTRANS_X];
float transY = values[Matrix.MTRANS_Y];
float scaleX = values[Matrix.MSCALE_X];
float scaleY = values[Matrix.MSCALE_Y];
ImageView iv = (ImageView)findViewById(R.id.photo_view);
Rect bounds = iv.getDrawable().getBounds();
int viewWidth = getResources().getDisplayMetrics().widthPixels;
int viewHeight = getResources().getDisplayMetrics().heightPixels;
int width = bounds.right - bounds.left;
int height = bounds.bottom - bounds.top;
float minX = (-width + 20) * scaleX;
float minY = (-height + 20) * scaleY;
if(transX > (viewWidth - 20)) {
transX = viewWidth - 20;
} else if(transX < minX) {
transX = minX;
}
if(transY > (viewHeight - 80)) {
transY = viewHeight - 80;
} else if(transY < minY) {
transY = minY;
}
values[Matrix.MTRANS_X] = transX;
values[Matrix.MTRANS_Y] = transY;
m.setValues(values);
}
Once again, this would go right after your switch statement and right before you set the matrix for the image in the view. I broke out the zoom limiting into a function as well and it is reflected above.
Download the source code.
So you can also get this a) tap, b) drag, and c) pinch zoom.
http://pragprog.com/titles/eband3/source_code
Here is the complete code for pinch zoom and pan (Touch.java with some modifications that can be used practically)
public class Touch implements OnTouchListener {
// These matrices will be used to move and zoom image
public static Matrix matrix = new Matrix();
public static Matrix savedMatrix = new Matrix();
// We can be in one of these 3 states
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
private static final float MAX_ZOOM = (float) 3;
private static final float MIN_ZOOM = 1;
int mode = NONE;
// Remember some things for zooming
PointF start = new PointF();
PointF mid = new PointF();
float oldDist = 1f;
int width,height;
#Override
public boolean onTouch(View v, MotionEvent event) {
ImageView view = (ImageView) v;
Rect bounds = view.getDrawable().getBounds();
width = bounds.right - bounds.left;
height = bounds.bottom - bounds.top;
// 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());
mode = DRAG;
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
if (oldDist > 10f) {
savedMatrix.set(matrix);
midPoint(mid, event);
mode = ZOOM;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
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);
if (newDist > 10f) {
matrix.set(savedMatrix);
float scale = newDist / oldDist;
matrix.postScale(scale, scale, mid.x, mid.y);
}
}
break;
}
//----------------------------------------------------
limitZoom(matrix);
limitDrag( matrix);
//----------------------------------------------------
view.setImageMatrix(matrix);
return true; // indicate event was handled
}
/** Show an event in the LogCat view, for debugging */
private void dumpEvent(MotionEvent event) {
String names[] = { "DOWN", "UP", "MOVE", "CANCEL", "OUTSIDE",
"POINTER_DOWN", "POINTER_UP", "7?", "8?", "9?" };
StringBuilder sb = new StringBuilder();
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
sb.append("event ACTION_").append(names[actionCode]);
if (actionCode == MotionEvent.ACTION_POINTER_DOWN
|| actionCode == MotionEvent.ACTION_POINTER_UP) {
sb.append("(pid ").append(
action >> MotionEvent.ACTION_POINTER_ID_SHIFT);
sb.append(")");
}
sb.append("[");
for (int i = 0; i < event.getPointerCount(); i++) {
sb.append("#").append(i);
sb.append("(pid ").append(event.getPointerId(i));
sb.append(")=").append((int) event.getX(i));
sb.append(",").append((int) event.getY(i));
if (i + 1 < event.getPointerCount())
sb.append(";");
}
sb.append("]");
}
/** 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);
}
private void limitZoom(Matrix m) {
float[] values = new float[9];
m.getValues(values);
float scaleX = values[Matrix.MSCALE_X];
float scaleY = values[Matrix.MSCALE_Y];
if(scaleX > MAX_ZOOM) {
scaleX = MAX_ZOOM;
} else if(scaleX < MIN_ZOOM) {
scaleX = MIN_ZOOM;
}
if(scaleY > MAX_ZOOM) {
scaleY = MAX_ZOOM;
} else if(scaleY < MIN_ZOOM) {
scaleY = MIN_ZOOM;
}
values[Matrix.MSCALE_X] = scaleX;
values[Matrix.MSCALE_Y] = scaleY;
m.setValues(values);
}
private void limitDrag(Matrix m) {
float[] values = new float[9];
m.getValues(values);
float transX = values[Matrix.MTRANS_X];
float transY = values[Matrix.MTRANS_Y];
float scaleX = values[Matrix.MSCALE_X];
float scaleY = values[Matrix.MSCALE_Y];
//--- limit moving to left ---
float minX = (-width + 0) * (scaleX-1);
float minY = (-height + 0) * (scaleY-1);
//--- limit moving to right ---
float maxX=minX+width*(scaleX-1);
float maxY=minY+height*(scaleY-1);
if(transX>maxX){transX = maxX;}
if(transX<minX){transX = minX;}
if(transY>maxY){transY = maxY;}
if(transY<minY){transY = minY;}
values[Matrix.MTRANS_X] = transX;
values[Matrix.MTRANS_Y] = transY;
m.setValues(values);
}
}
Use the code in the comment by Phyxdevel in the below link
ZDNET Pinch Zoom Example.
He has the code to restrict the pan and zoom level.
you can use following code to limit bottom and right
float maxX = minX+viewWidth;
int offsetY = 80;
float maxY = minY+viewHeight-offsetY;
if(x>maxX){
mPosX = maxX;
}
if(x<minX){
mPosX = minX;
}
if(y>maxY){
mPosY = maxY;
}
if(y<minY){
mPosY = minY;
}

Categories

Resources