how to restrict imageview from drag out of screen?
java file to color border of imageview
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.linear);
ShapeDrawable sd = new ShapeDrawable();
// Specify the shape of ShapeDrawable
sd.setShape(new RectShape());
// Specify the border color of shape
sd.getPaint().setColor(Color.RED);
// Set the border width
sd.getPaint().setStrokeWidth(10f);
// Specify the style is a Stroke
sd.getPaint().setStyle(Paint.Style.STROKE);
// Finally, add the drawable background to Imageview
linearLayout.setBackground(sd);
image gets out of screen i want imageview to remian in inside the layout
Touch1 class i used to move and zoom in and out imageview
package com.example.pc_4.borderingimageview;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.FrameLayout;
import android.widget.ImageView;
public class Touch1 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_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 (float) Math.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);
}
public void drag(MotionEvent event, View v) {
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) v.getLayoutParams();
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE: {
params.topMargin = (int) event.getRawY() - (v.getHeight());
params.leftMargin = (int) event.getRawX() - (v.getWidth() / 2);
v.setLayoutParams(params);
break;
}
case MotionEvent.ACTION_UP: {
params.topMargin = (int) event.getRawY() - (v.getHeight());
params.leftMargin = (int) event.getRawX() - (v.getWidth() / 2);
v.setLayoutParams(params);
break;
}
case MotionEvent.ACTION_DOWN: {
v.setLayoutParams(params);
break;
}
}
}
}
xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/rl"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".MainActivity">
<LinearLayout
android:id="#+id/linear"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:orientation="vertical">
<ImageView
android:layout_centerInParent="true"
android:id="#+id/TestImage"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:src="#drawable/logo" />
<!-- <Button
android:id="#+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/TestImage"
android:layout_centerHorizontal="true"
android:layout_marginTop="44dp"
android:text="Set border" />-->
</LinearLayout>
</RelativeLayout>
You can try below code inside onTouch method with case MotionEvent.ACTION_MOVE:
if(view.getId == R.id.TestImage) {
// do nothing
} else {
// rest of your code
}
Try to modify it and check if it works.
You can put a if condition like
case MotionEvent.ACTION_MOVE: {
params.topMargin = (int) event.getRawY() - (v.getHeight());
params.leftMargin = (int) event.getRawX() - (v.getWidth()/2);
if(v.getX()> 0) { //some kind of check
v.setLayoutParams(params);
}
break;
}
Also having this code in ACTION_MOVE seems enough.
I have created this code, and so far it works, as I see in your code, we use the same name for the variables, so you can just copy paste it:
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
matrix.set(savedMatrix);
float imgWidthRight = 0; //image.getWidth() - (image.getWidth()- 1);
float imgWidthLeft = -(image.getWidth() - (image.getWidth()/4));
float xValue;
float imgHeightUp = -(image.getHeight() - (image.getHeight()/5));
float imgHeightDown = image.getHeight() - (image.getHeight()/2);
float yValue;
float[] f_mtrx = new float[9];
float[] f_img = new float[9];
matrix.getValues(f_mtrx);
image.getImageMatrix().getValues(f_img);
if ((f_img[2] > imgWidthRight) && (event.getX() - start.x > 0)) {
xValue = 0;
} else if ((f_img[2] < imgWidthLeft) && (event.getX() - start.x < 0)) {
xValue = 0;
} else {
xValue = event.getX() - start.x;
}
if ((f_img[5] > imgHeightDown) && (event.getY() - start.y > 0)) {
yValue = 0;
} else if ((f_img[5] < imgHeightUp) && (event.getY() - start.y < 0)) {
yValue = 0;
} else {
yValue = event.getY() - start.y;
}
matrix.postTranslate(xValue, yValue);
}
Related
The problem with this code when I run it the image zoom in = Infinity!
and zoom out = Infinity!
I want to add limit for zoom in & zoom out .. help please
Regards.
This is my class code :
Map.java
public class Map extends Fragment implements View.OnTouchListener {
private static final String TAG = "Touch";
#SuppressWarnings("unused")
private static final float MIN_ZOOM = 0.1f, MAX_ZOOM = 0.1f;
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();
// The 3 states (events) which the user is trying to perform
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;
// these PointF objects are used to record the point(s) the user is touching
PointF start = new PointF();
PointF mid = new PointF();
float oldDist = 0.1f;
public Map(){
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.x_map, container, false);
ImageView view = (ImageView) v.findViewById(R.id.imageView1);
view.setOnTouchListener(this);
return v;
}
#Override
public boolean onTouch(View v, MotionEvent event) {
ImageView view = (ImageView) v;
view.setScaleType(ImageView.ScaleType.MATRIX);
float scale;
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"); // write to LogCat
mode = DRAG;
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
Log.d(TAG, "mode=NONE");
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
Log.d(TAG, "oldDist=" + oldDist);
if (oldDist > 0.1f) {
savedMatrix.set(matrix);
midPoint(mid, event);
mode = ZOOM;
Log.d(TAG, "mode=ZOOM");
}
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
matrix.set(savedMatrix);
matrix.postTranslate(event.getX() - start.x, event.getY()
- start.y); /*
* create the transformation in the matrix
* of points
*/
} else if (mode == ZOOM) {
// pinch zooming
float newDist = spacing(event);
Log.d(TAG, "newDist=" + newDist);
if (newDist > 1f) {
matrix.set(savedMatrix);
scale = newDist / oldDist;
/*
* setting the scaling of the matrix...if scale > 1 means
* zoom in...if scale < 1 means zoom out
*/
matrix.postScale(scale, scale, mid.x, mid.y);
}
}
break;
}
view.setImageMatrix(matrix); // display the transformation on screen
return true;
}
private float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return (float) Math.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 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("Touch Event", sb.toString());
}
}
and this my layout :
map.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ImageView
android:id="#+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/map" />
</FrameLayout>
I am using Zoom In/Out functionality in my application. It is working fine but sometimes It gives error pointerIndex out of range.
Please help me for this. Here, I am giving my code. I also want to set minimum width to Image which can't be reduce by user while using Zoom In/Out.
Java Code :
private static final String TAG = "Touch";
// 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;
public boolean onTouch(View v, MotionEvent event) {
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_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
}
/** Show an event in the LogCat view, for debugging */
#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 */
#SuppressLint("FloatMath")
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);
}
.xml file :
<?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"
android:gravity="center" >
<ImageView
android:id="#+id/imgImage"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:contentDescription="#string/imgdesc"
android:scaleType="matrix"
android:src="#drawable/ic_answer" />
</FrameLayout>
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);
}
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)
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;
}