Pinch zoom on SurfaceView - android

I'm trying to implement pinch zoom on a SurfaceView. I've done a lot of research regarding this and I found this class to implement pinch zoom. Here is how I modified it:
public class ZoomLayout extends FrameLayout implements ScaleGestureDetector.OnScaleGestureListener {
private SurfaceView mSurfaceView;
private enum Mode {
NONE,
DRAG,
ZOOM
}
private static final String TAG = "ZoomLayout";
private static final float MIN_ZOOM = 1.0f;
private static final float MAX_ZOOM = 4.0f;
private Mode mode;
private float scale = 1.0f;
private float lastScaleFactor = 0f;
// Where the finger first touches the screen
private float startX = 0f;
private float startY = 0f;
// How much to translate the canvas
private float dx = 0f;
private float dy = 0f;
private float prevDx = 0f;
private float prevDy = 0f;
public ZoomLayout(Context context) {
super(context);
init(context);
}
public ZoomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ZoomLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
final ScaleGestureDetector scaleDetector = new ScaleGestureDetector(context, this);
setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View view, MotionEvent motionEvent) {
ZoomLayout.this.mSurfaceView = (SurfaceView) view.findViewById(R.id.mSurfaceView);
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
Log.i(TAG, "DOWN");
if (scale > MIN_ZOOM) {
mode = Mode.DRAG;
startX = motionEvent.getX() - prevDx;
startY = motionEvent.getY() - prevDy;
}
break;
case MotionEvent.ACTION_MOVE:
if (mode == Mode.DRAG) {
dx = motionEvent.getX() - startX;
dy = motionEvent.getY() - startY;
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
mode = Mode.ZOOM;
break;
case MotionEvent.ACTION_POINTER_UP:
mode = Mode.NONE;
break;
case MotionEvent.ACTION_UP:
Log.i(TAG, "UP");
mode = Mode.NONE;
prevDx = dx;
prevDy = dy;
break;
}
scaleDetector.onTouchEvent(motionEvent);
if ((mode == Mode.DRAG && scale >= MIN_ZOOM) || mode == Mode.ZOOM) {
getParent().requestDisallowInterceptTouchEvent(true);
float maxDx = (child().getWidth() - (child().getWidth() / scale)) / 2 * scale;
float maxDy = (child().getHeight() - (child().getHeight() / scale))/ 2 * scale;
dx = Math.min(Math.max(dx, -maxDx), maxDx);
dy = Math.min(Math.max(dy, -maxDy), maxDy);
Log.i(TAG, "Width: " + child().getWidth() + ", scale " + scale + ", dx " + dx
+ ", max " + maxDx);
applyScaleAndTranslation();
}
return true;
}
});
}
// ScaleGestureDetector
public boolean onScaleBegin(ScaleGestureDetector scaleDetector) {
Log.i(TAG, "onScaleBegin");
return true;
}
public boolean onScale(ScaleGestureDetector scaleDetector) {
float scaleFactor = scaleDetector.getScaleFactor();
Log.i(TAG, "mode:" + this.mode + ", onScale:" + scaleFactor);
if (this.lastScaleFactor == 0.0f || Math.signum(scaleFactor) == Math.signum(this.lastScaleFactor)) {
this.scale *= scaleFactor;
this.scale = Math.max(MIN_ZOOM, Math.min(this.scale, MAX_ZOOM));
this.lastScaleFactor = scaleFactor;
} else {
this.lastScaleFactor = 0.0f;
}
if (this.mSurfaceView != null) {
int orgWidth = getWidth();
int _width = (int) (((float) orgWidth) * this.scale);
int _height = (int) (((float) getHeight()) * this.scale);
LayoutParams params = (LayoutParams) this.mSurfaceView.getLayoutParams();
params.height = _height;
params.width = _width;
this.mSurfaceView.setLayoutParams(params);
child().setScaleX(this.scale);
child().setScaleY(this.scale);
}
return true;
}
#Override
public void onScaleEnd(ScaleGestureDetector scaleDetector) {
Log.i(TAG, "onScaleEnd");
}
private void applyScaleAndTranslation() {
child().setScaleX(scale);
child().setScaleY(scale);
child().setTranslationX(dx);
child().setTranslationY(dy);
}
private View child() {
return getChildAt(0);
}
and I implement it into my layout like this:
<pacageName.control.ZoomLayout
android:id="#+id/mZoomLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:measureAllChildren="true">
<SurfaceView
android:id="#+id/mSurfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</pacageName.control.ZoomLayout>
The problem I'm having is that the zoom is not 'smooth' and the behavior of the zoom is different in different devices (Samsung S4 (Lollipop) and J7Pro (Nougat)).
I'm not sure why pinch zoom is "glitchy" and why it doesn't zoom the same way on the two different devices.
Edit 1: Memory and CPU consumption image added below -
EDIT 2: After trying something else I'm facing a new issue -
I changed my ZoomLayout completely to the following:
public class ZoomableSurfaceView extends SurfaceView {
private ScaleGestureDetector SGD;
private Context context;
private boolean isSingleTouch;
private float width, height = 0;
private float scale = 1f;
private float minScale = 1f;
private float maxScale = 5f;
int left, top, right, bottom;
public ZoomableSurfaceView(Context context) {
super(context);
this.context = context;
init();
}
public ZoomableSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
init();
}
public ZoomableSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
init();
}
private void init() {
setOnTouchListener(new MyTouchListeners());
SGD = new ScaleGestureDetector(context, new ScaleListener());
this.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
}
});
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (width == 0 && height == 0) {
width = ZoomableSurfaceView.this.getWidth();
height = ZoomableSurfaceView.this.getHeight();
this.left = left;
this.right = right;
this.top = top;
this.bottom = bottom;
}
}
private class MyTouchListeners implements View.OnTouchListener {
float dX, dY;
MyTouchListeners() {
super();
}
#Override
public boolean onTouch(View view, MotionEvent event) {
SGD.onTouchEvent(event);
if (event.getPointerCount() > 1) {
isSingleTouch = false;
} else {
if (event.getAction() == MotionEvent.ACTION_UP) {
isSingleTouch = true;
}
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
dX = ZoomableSurfaceView.this.getX() - event.getRawX();
dY = ZoomableSurfaceView.this.getY() - event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
if (isSingleTouch) {
ZoomableSurfaceView.this.animate()
.x(event.getRawX() + dX)
.y(event.getRawY() + dY)
.setDuration(0)
.start();
checkDimension(ZoomableSurfaceView.this);
}
break;
default:
return true;
}
return true;
}
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
//Log.e("onGlobalLayout: ", scale + " " + width + " " + height);
scale *= detector.getScaleFactor();
scale = Math.max(minScale, Math.min(scale, maxScale));
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams((int) (width * scale), (int) (height * scale));
Log.e("onGlobalLayout: ", (int) (width * scale) + " " + (int) (height * scale));
ZoomableSurfaceView.this.setLayoutParams(params);
checkDimension(ZoomableSurfaceView.this);
return true;
}
}
private void checkDimension(View vi) {
if (vi.getX() > left) {
vi.animate()
.x(left)
.y(vi.getY())
.setDuration(0)
.start();
}
if ((vi.getWidth() + vi.getX()) < right) {
vi.animate()
.x(right - vi.getWidth())
.y(vi.getY())
.setDuration(0)
.start();
}
if (vi.getY() > top) {
vi.animate()
.x(vi.getX())
.y(top)
.setDuration(0)
.start();
}
if ((vi.getHeight() + vi.getY()) < bottom) {
vi.animate()
.x(vi.getX())
.y(bottom - vi.getHeight())
.setDuration(0)
.start();
}
}
}
The problem I'm facing now is that it zooms to X - 0 and Y - 0 meaning that it zooms to the top-left of the screen instead of zooming to the point between my fingers.. I think it could be related to the following:
#Override
public boolean onScale(ScaleGestureDetector detector) {
//Log.e("onGlobalLayout: ", scale + " " + width + " " + height);
scale *= detector.getScaleFactor();
scale = Math.max(minScale, Math.min(scale, maxScale));
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams((int) (width * scale), (int) (height * scale));
Log.e("onGlobalLayout: ", (int) (width * scale) + " " + (int) (height * scale));
ZoomableSurfaceView.this.setLayoutParams(params);
checkDimension(ZoomableSurfaceView.this);
return true;
}
I have an idea that I should detect the centre point of the 2 fingers and add that to OnScale. How should I proceed?
EDIT 3:
Ok I finally got the zoom working perfectly on my J7 Pro running Nougat. But the problem now, is that my SurfaceView in my S4 running Lollipop doesn't get zoomed in, instead my SurfaceView gets moved to the top left corner. I have tested placing a ImageView inside my custom zoomview and the image gets zoomed perfectly like expected. This is how my custom zoom class looks like now:
public class ZoomLayout extends FrameLayout implements ScaleGestureDetector.OnScaleGestureListener {
private enum Mode {
NONE,
DRAG,
ZOOM
}
private static final String TAG = "ZoomLayout";
private static final float MIN_ZOOM = 1.0f;
private static final float MAX_ZOOM = 4.0f;
private Mode mode = Mode.NONE;
private float scale = 1.0f;
private float lastScaleFactor = 0f;
// Where the finger first touches the screen
private float startX = 0f;
private float startY = 0f;
// How much to translate the canvas
private float dx = 0f;
private float dy = 0f;
private float prevDx = 0f;
private float prevDy = 0f;
public ZoomLayout(Context context) {
super(context);
init(context);
}
public ZoomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ZoomLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
final ScaleGestureDetector scaleDetector = new ScaleGestureDetector(context, this);
setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
Log.i(TAG, "DOWN");
if (scale > MIN_ZOOM) {
mode = Mode.DRAG;
startX = motionEvent.getX() - prevDx;
startY = motionEvent.getY() - prevDy;
}
break;
case MotionEvent.ACTION_MOVE:
if (mode == Mode.DRAG) {
dx = motionEvent.getX() - startX;
dy = motionEvent.getY() - startY;
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
mode = Mode.ZOOM;
break;
case MotionEvent.ACTION_POINTER_UP:
mode = Mode.NONE; // changed from DRAG, was messing up zoom
break;
case MotionEvent.ACTION_UP:
Log.i(TAG, "UP");
mode = Mode.NONE;
prevDx = dx;
prevDy = dy;
break;
}
scaleDetector.onTouchEvent(motionEvent);
if ((mode == Mode.DRAG && scale >= MIN_ZOOM) || mode == Mode.ZOOM) {
getParent().requestDisallowInterceptTouchEvent(true);
float maxDx = child().getWidth() * (scale - 1); // adjusted for zero pivot
float maxDy = child().getHeight() * (scale - 1); // adjusted for zero pivot
dx = Math.min(Math.max(dx, -maxDx), 0); // adjusted for zero pivot
dy = Math.min(Math.max(dy, -maxDy), 0); // adjusted for zero pivot
Log.i(TAG, "Width: " + child().getWidth() + ", scale " + scale + ", dx " + dx
+ ", max " + maxDx);
applyScaleAndTranslation();
}
return true;
}
});
}
// ScaleGestureDetector
#Override
public boolean onScaleBegin(ScaleGestureDetector scaleDetector) {
Log.i(TAG, "onScaleBegin");
return true;
}
#Override
public boolean onScale(ScaleGestureDetector scaleDetector) {
float scaleFactor = scaleDetector.getScaleFactor();
Log.i(TAG, "onScale(), scaleFactor = " + scaleFactor);
if (lastScaleFactor == 0 || (Math.signum(scaleFactor) == Math.signum(lastScaleFactor))) {
float prevScale = scale;
scale *= scaleFactor;
scale = Math.max(MIN_ZOOM, Math.min(scale, MAX_ZOOM));
lastScaleFactor = scaleFactor;
float adjustedScaleFactor = scale / prevScale;
// added logic to adjust dx and dy for pinch/zoom pivot point
Log.d(TAG, "onScale, adjustedScaleFactor = " + adjustedScaleFactor);
Log.d(TAG, "onScale, BEFORE dx/dy = " + dx + "/" + dy);
float focusX = scaleDetector.getFocusX();
float focusY = scaleDetector.getFocusY();
Log.d(TAG, "onScale, focusX/focusy = " + focusX + "/" + focusY);
dx += (dx - focusX) * (adjustedScaleFactor - 1);
dy += (dy - focusY) * (adjustedScaleFactor - 1);
Log.d(TAG, "onScale, dx/dy = " + dx + "/" + dy);
} else {
lastScaleFactor = 0;
}
return true;
}
#Override
public void onScaleEnd(ScaleGestureDetector scaleDetector) {
Log.i(TAG, "onScaleEnd");
}
private void applyScaleAndTranslation() {
child().setScaleX(scale);
child().setScaleY(scale);
child().setPivotX(0f); // default is to pivot at view center
child().setPivotY(0f); // default is to pivot at view center
child().setTranslationX(dx);
child().setTranslationY(dy);
}
private View child() {
return getChildAt(0);
}
Edit 4: Added video of behaviour:
Please see how the current zoom looks like after updating my zoomLayout class to edit 3 -
Video of SurfaceView zoom behaviour

Instead of using SurfaceView, use TextureView which has more features and is preferred over SurfaceView. See TextureView Documentation
Your code is indeed right and should work but in some cases, The MediaPlayer drawn on the SurfaceView does not resize and makes an illusion of the zoom happening at the position 0,0 of the phone. If you set a size smaller than the parent for the SurfaceView and set a background for the parent, you will notice this
SOLUTION IN CODE
Sample Project: Pinch Zoom
The project extends the TextureView and implements the touch to create the zoom effect.
IMAGE: DEMONSTRATION

scaling SurfaceView by scaling its parent FrameLayout will work only in Nougat (API 24) and above
this scaling does not change the SurfaceView resolution i.e. it is much safer and smooth than scaling SurfaceView by changing its LayoutParams (but resolution is fixed). This is the only option for custom view e.g. GStreamerSurfaceView

Related

How you can have min and max values for zooming into layout

I have the following which allows me to zoom in and out indefinitely, however I am trying to set a min and max value for zoom so that the layout is not too large or too small. I have tried to use but I just don't understand where to place this in the code.
private static final float MIN_ZOOM = 1.0f;
private static final float MAX_ZOOM = //set Maximum zooming level
float scale = Math.max(MIN_ZOOM, Math.min(scale, MAX_ZOOM));
public class ZoomableView extends RelativeLayout {
// States.
private static final byte NONE = 0;
private static final byte DRAG = 1;
private static final byte ZOOM = 2;
private byte mode = NONE;
// Matrices used to move and zoom image.
private Matrix matrix = new Matrix();
private Matrix matrixInverse = new Matrix();
private Matrix savedMatrix = new Matrix();
// Parameters for zooming.
private PointF start = new PointF();
private PointF mid = new PointF();
private float oldDist = 1f;
private float[] lastEvent = null;
private long lastDownTime = 0l;
private float[] mDispatchTouchEventWorkingArray = new float[2];
private float[] mOnTouchEventWorkingArray = new float[2];
#Override
public boolean dispatchTouchEvent(MotionEvent ev) {
mDispatchTouchEventWorkingArray[0] = ev.getX();
mDispatchTouchEventWorkingArray[1] = ev.getY();
mDispatchTouchEventWorkingArray = screenPointsToScaledPoints(mDispatchTouchEventWorkingArray);
ev.setLocation(mDispatchTouchEventWorkingArray[0], mDispatchTouchEventWorkingArray[1]);
return super.dispatchTouchEvent(ev);
}
public ZoomableView(Context context) {
super(context);
init(context);
}
public ZoomableView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ZoomableView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
}
/**
* 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);
}
private float[] scaledPointsToScreenPoints(float[] a) {
matrix.mapPoints(a);
return a;
}
private float[] screenPointsToScaledPoints(float[] a) {
matrixInverse.mapPoints(a);
return a;
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != GONE) {
child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
}
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (child.getVisibility() != GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
}
}
#Override
protected void dispatchDraw(Canvas canvas) {
float[] values = new float[9];
matrix.getValues(values);
canvas.save();
canvas.translate(values[Matrix.MTRANS_X], values[Matrix.MTRANS_Y]);
canvas.scale(values[Matrix.MSCALE_X], values[Matrix.MSCALE_Y]);
super.dispatchDraw(canvas);
canvas.restore();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// handle touch events here
mOnTouchEventWorkingArray[0] = event.getX();
mOnTouchEventWorkingArray[1] = event.getY();
mOnTouchEventWorkingArray = scaledPointsToScreenPoints(mOnTouchEventWorkingArray);
event.setLocation(mOnTouchEventWorkingArray[0], mOnTouchEventWorkingArray[1]);
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
mode = DRAG;
lastEvent = null;
long downTime = event.getDownTime();
if (downTime - lastDownTime < 300l) {
float density = getResources().getDisplayMetrics().density;
if (Math.max(Math.abs(start.x - event.getX()), Math.abs(start.y - event.getY())) < 40.f * density) {
savedMatrix.set(matrix);
mid.set(event.getX(), event.getY());
mode = ZOOM;
lastEvent = new float[4];
lastEvent[0] = lastEvent[1] = event.getX();
lastEvent[2] = lastEvent[3] = event.getY();
}
lastDownTime = 0l;
} else {
lastDownTime = downTime;
}
start.set(event.getX(), event.getY());
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);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
lastEvent = null;
break;
case MotionEvent.ACTION_MOVE:
final float density = getResources().getDisplayMetrics().density;
if (mode == DRAG) {
matrix.set(savedMatrix);
float dx = event.getX() - start.x;
float dy = event.getY() - start.y;
matrix.postTranslate(dx, dy);
matrix.invert(matrixInverse);
if (Math.max(Math.abs(start.x - event.getX()), Math.abs(start.y - event.getY())) > 20.f * density) {
lastDownTime = 0l;
}
} else if (mode == ZOOM) {
if (event.getPointerCount() > 1) {
float newDist = spacing(event);
if (newDist > 10f * density) {
matrix.set(savedMatrix);
float scale = (newDist / oldDist);
matrix.postScale(scale, scale, mid.x, mid.y);
matrix.invert(matrixInverse);
}
} else {
matrix.set(savedMatrix);
float scale = event.getY() / start.y;
matrix.postScale(scale, scale, mid.x, mid.y);
matrix.invert(matrixInverse);
}
}
break;
}
invalidate();
return true;
}
}
You need to make a class extending RelativeLayout , there you can set it by overriding the getMaxZoomLevel() and getMinZoomLevel(), like this:
public class ZoomableView extends RelativeLayout {
#Override
public int getMaxZoomLevel() {
return 22;
}
#Override
public int getMinZoomLevel() {
return 12;
}
}

Android Zooming functionality using Scaling operation lags(not smooth) on High Pixel density devices

i am working on this android project, which has this typical UI like 3D chess board grid. Which has Zoom Pan/drag feature on this screen.
I have implemented this functionality by creating custom frame layout class.
Snippet :
public class ZoomLayout extends FrameLayout implements ScaleGestureDetector.OnScaleGestureListener {
float x1=0,y1=0,x2,y2;
ScaleGestureDetector scaleDetector;
Context mContext;
private enum Mode {
NONE,
DRAG,
ZOOM
}
private static final String TAG = "ZoomLayout";
private static final float MIN_ZOOM = 1.0f;
private static final float MAX_ZOOM = 6.0f;
private Mode mode = Mode.NONE;
private float scale = 1.0f;
private float lastScaleFactor = 0f;
// Where the finger first touches the screen
private float startX = 0f;
private float startY = 0f;
// How much to translate the canvas
private float dx = 0f;
private float dy = 0f;
private float prevDx = 0f;
private float prevDy = 0f;
ApiCommunicationListener apiListener;
Activity mActivity;
public ZoomLayout(Context context, ApiCommunicationListener apiListener, Activity activity) {
super(context);
this.apiListener = apiListener;
this.mActivity = activity;
init(context);
}
public ZoomLayout(Context context) {
super(context);
init(context);
}
public ZoomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ZoomLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
mContext = context;
scaleDetector = new ScaleGestureDetector(context, this);
}
#Override
public boolean onTouchEvent(final MotionEvent motionEvent) {
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
x1 = motionEvent.getX();
y1 = motionEvent.getY();
// System.out.println("TOUCH DOWN : X:" + x1 + " Y:" + y1);
if (scale > MIN_ZOOM) {
mode = Mode.DRAG;
startX = motionEvent.getX() - prevDx;
startY = motionEvent.getY() - prevDy;
}
break;
case MotionEvent.ACTION_MOVE:
if (mode == Mode.DRAG) {
dx = motionEvent.getX() - startX;
dy = motionEvent.getY() - startY;
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
mode = Mode.ZOOM;
break;
case MotionEvent.ACTION_POINTER_UP:
//mode = Mode.DRAG;
break;
case MotionEvent.ACTION_UP:
x2 = motionEvent.getX();
y2 = motionEvent.getY();
// System.out.println("TOUCH UP : X:" + x2 + " Y:" + y2);
mode = Mode.NONE;
prevDx = dx;
prevDy = dy;
if (x1 == x2 && y1 == y2 && ApocketApp.mViewClicked != null) {
Utilities.sysOut("-------------In ZoomLayout");
if (ApocketApp.mViewClicked.getTag().toString().contains("A"))
apiListener.onSuccess("onAvatarClick");
else
apiListener.onSuccess("onBuildingClick");
}
break;
}
scaleDetector.onTouchEvent(motionEvent);
if ((mode == Mode.DRAG && scale >= MIN_ZOOM) || mode == Mode.ZOOM) {
getParent().requestDisallowInterceptTouchEvent(true);
float maxDx = (child().getWidth() - (child().getWidth() / scale)) / 2 * scale;
float maxDy = (child().getHeight() - (child().getHeight() / scale)) / 2 * scale;
dx = Math.min(Math.max(dx, -maxDx), maxDx);
dy = Math.min(Math.max(dy, -maxDy), maxDy);
/*Log.i(TAG, "Width: " + child().getWidth() + ", scale " + scale + ", dx " + dx
+ ", max " + maxDx);*/
applyScaleAndTranslation();
}
return true;
}
#Override
public boolean onScaleBegin(ScaleGestureDetector scaleDetector) {
// Log.i(TAG, "onScaleBegin");
// setLayerType(View.LAYER_TYPE_HARDWARE, null);
return true;
}
#Override
public boolean onScale(ScaleGestureDetector scaleDetector) {
float scaleFactor = scaleDetector.getScaleFactor();
//Log.i(TAG, "onScale" + scaleFactor);
if (lastScaleFactor == 0 || (Math.signum(scaleFactor) == Math.signum(lastScaleFactor))) {
scale *= scaleFactor;
scale = Math.max(MIN_ZOOM, Math.min(scale, MAX_ZOOM));
lastScaleFactor = scaleFactor;
} else {
lastScaleFactor = 0;
}
return true;
}
#Override
public void onScaleEnd(ScaleGestureDetector scaleDetector) {
//Log.i(TAG, "onScaleEnd");
// setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
private void applyScaleAndTranslation() {
child().setScaleX(scale);
child().setScaleY(scale);
child().setTranslationX(dx);
child().setTranslationY(dy);
}
private View child() {
return getChildAt(0);
}
public void animateToCentre(final RelativeLayout relativeLayout){
Resources res = mContext.getResources();
TypedValue outValue = new TypedValue();
res.getValue(R.dimen.zoom_centre_level_max, outValue, true);
final float zoomEnd = outValue.getFloat();
ObjectAnimator oaX = new ObjectAnimator();
oaX.setDuration(res.getInteger(R.integer.zoom_centre_duration));
oaX.setFloatValues(new float[]{1.0f, zoomEnd});
oaX.setPropertyName("scaleX");
oaX.setFrameDelay(10);
ObjectAnimator oaY = new ObjectAnimator();
oaY.setDuration(res.getInteger(R.integer.zoom_centre_duration));
oaY.setFloatValues(new float[]{1.0f, zoomEnd});
oaY.setPropertyName("scaleY");
oaY.setFrameDelay(10);
ObjectAnimator oaTy = new ObjectAnimator();
oaTy.setDuration(res.getInteger(R.integer.zoom_centre_duration));
oaTy.setFloatValues(new float[]{0.0f, res.getDimension(R.dimen.v_5) * 3});
oaTy.setPropertyName("translationY");
oaTy.setFrameDelay(10);
AnimatorSet zoomAnimation = new AnimatorSet();
zoomAnimation.playTogether(new ObjectAnimator[]{oaX, oaY, oaTy});
//AnimatorSet zoomAnimation = (AnimatorSet) AnimatorInflater.loadAnimator(mContext, R.anim.zoom_in_out);
zoomAnimation.setTarget(child());
zoomAnimation.setInterpolator(new FastOutLinearInInterpolator());
zoomAnimation.start();
zoomAnimation.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
#Override
public void onAnimationEnd(Animator animation) {
//setLayerType(View.LAYER_TYPE_HARDWARE, null);
scale = zoomEnd;
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
}
}
The zooming works fine,smoothly on hdpi or xhdpi devices but it lags on xxhdpi and xxxhdpi devices.Hi have tried using HardwareAccelaration = true in manifest, but doesn't make much difference.
So please can any body suggest some approach to resolve this issue.
Thanks in advance.

Pinch Zoom layout in Android

This is my code which performs my pinch zoom. It's working only if I use it in the layout where I don't have any widgets which have implemented onClickListner. I don't know how to make zoom in my layout if I have my fingers over those widgets.
package com.example.cosmin.catanadvice;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.FrameLayout;
/**
* Layout that provides pinch-zooming of content. This view should have exactly one child
* view containing the content.
*/
public class ZoomLayout extends FrameLayout implements ScaleGestureDetector.OnScaleGestureListener {
private enum Mode {
NONE,
DRAG,
ZOOM
}
private static final String TAG = "ZoomLayout";
private static final float MIN_ZOOM = 1.0f;
private static final float MAX_ZOOM = 4.0f;
private Mode mode = Mode.NONE;
private float scale = 1.0f;
private float lastScaleFactor = 0f;
// Where the finger first touches the screen
private float startX = 0f;
private float startY = 0f;
// How much to translate the canvas
private float dx = 0f;
private float dy = 0f;
private float prevDx = 0f;
private float prevDy = 0f;
public ZoomLayout(Context context) {
super(context);
init(context);
}
public ZoomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ZoomLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
final ScaleGestureDetector scaleDetector = new ScaleGestureDetector(context, this);
this.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
Log.i(TAG, "DOWN");
if (scale > MIN_ZOOM) {
mode = Mode.DRAG;
startX = motionEvent.getX() - prevDx;
startY = motionEvent.getY() - prevDy;
}
break;
case MotionEvent.ACTION_MOVE:
if (mode == Mode.DRAG) {
dx = motionEvent.getX() - startX;
dy = motionEvent.getY() - startY;
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
mode = Mode.ZOOM;
break;
case MotionEvent.ACTION_POINTER_UP:
mode = Mode.DRAG;
break;
case MotionEvent.ACTION_UP:
Log.i(TAG, "UP");
mode = Mode.NONE;
prevDx = dx;
prevDy = dy;
break;
}
scaleDetector.onTouchEvent(motionEvent);
if ((mode == Mode.DRAG && scale >= MIN_ZOOM) || mode == Mode.ZOOM) {
getParent().requestDisallowInterceptTouchEvent(true);
float maxDx = (child().getWidth() - (child().getWidth() / scale)) / 2 * scale;
float maxDy = (child().getHeight() - (child().getHeight() / scale))/ 2 * scale;
dx = Math.min(Math.max(dx, -maxDx), maxDx);
dy = Math.min(Math.max(dy, -maxDy), maxDy);
Log.i(TAG, "Width: " + child().getWidth() + ", scale " + scale + ", dx " + dx
+ ", max " + maxDx);
applyScaleAndTranslation();
}
return true;
}
});
}
// ScaleGestureDetector
#Override
public boolean onScaleBegin(ScaleGestureDetector scaleDetector) {
Log.i(TAG, "onScaleBegin");
return true;
}
#Override
public boolean onScale(ScaleGestureDetector scaleDetector) {
float scaleFactor = scaleDetector.getScaleFactor();
Log.i(TAG, "onScale" + scaleFactor);
if (lastScaleFactor == 0 || (Math.signum(scaleFactor) == Math.signum(lastScaleFactor))) {
scale *= scaleFactor;
scale = Math.max(MIN_ZOOM, Math.min(scale, MAX_ZOOM));
lastScaleFactor = scaleFactor;
} else {
lastScaleFactor = 0;
}
return true;
}
#Override
public void onScaleEnd(ScaleGestureDetector scaleDetector) {
Log.i(TAG, "onScaleEnd");
}
private void applyScaleAndTranslation() {
child().setScaleX(scale);
child().setScaleY(scale);
child().setTranslationX(dx);
child().setTranslationY(dy);
}
private View child() {
return getChildAt(0);
}
}
You can use one of the 3rd party libraries which already implemented this one, supporting variety of gestures:
https://github.com/chrisbanes/PhotoView

Zoom and perform onTouch event in FrameLayout

I Have a Frame Layout having 4 images in it image1,image2,image3,image4. I am able to zoom/pinch this Frame layout.I am able to perform on touch event for images by using X,Y position without zoom.
Question : How to achieve images on touch functionality individually after zoom.
public class ZoomLayout extends FrameLayout implements caleGestureDetector.OnScaleGestureListener {
private enum Mode {
NONE,
DRAG,
ZOOM
}
private static final String TAG = "ZoomLayout";
private static final float MIN_ZOOM = 1.0f;
private static final float MAX_ZOOM = 4.0f;
private Mode mode = Mode.NONE;
private float scale = 1.0f;
private float lastScaleFactor = 0f;
// Where the finger first touches the screen
private float startX = 0f;
private float startY = 0f;
// How much to translate the canvas
private float dx = 0f;
private float dy = 0f;
private float prevDx = 0f;
private float prevDy = 0f;
public ZoomLayout(Context context) {
super(context);
init(context);
}
public ZoomLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ZoomLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
#SuppressLint("NewApi")
private void init(final Context context) {
final ScaleGestureDetector scaleDetector = new ScaleGestureDetector(context, this);
this.setOnTouchListener(new View.OnTouchListener() {
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
#SuppressLint("NewApi")
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
com.example.target.ZoomLayout test=(com.example.target.ZoomLayout) view;
System.out.println( "iddd : "+test.getId());
switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
Log.i(TAG, "DOWN");
if (scale > MIN_ZOOM) {
mode = Mode.DRAG;
startX = motionEvent.getX() - prevDx;
startY = motionEvent.getY() - prevDy;
}
FrameLayout vv=(FrameLayout) child();
System.out.println("count= "+vv.getChildCount());
int count=vv.getChildCount();
for (int i = 1; i <count; i++) {
float startx,starty,endx,endy;
ImageView iv=(ImageView) vv.getChildAt(i);
startx=iv.getX()+startX;
starty=iv.getY()+startY;
endx=iv.getX()+iv.getWidth();
endy=iv.getY()+iv.getHeight();
if(motionEvent.getX()>( startx ) && motionEvent.getX()<(endx ) && motionEvent.getY()>(( starty )+getStatusBarHeight() ) && motionEvent.getY()<(( endy)+getStatusBarHeight()))
{
System.out.println("count= "+iv.getId());
Toast.makeText(context, "i = "+i, 3000).show();
}
}
break;
case MotionEvent.ACTION_MOVE:
if (mode == Mode.DRAG) {
dx = motionEvent.getX() - startX;
dy = motionEvent.getY() - startY;
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
mode = Mode.ZOOM;
break;
case MotionEvent.ACTION_POINTER_UP:
mode = Mode.DRAG;
break;
case MotionEvent.ACTION_UP:
Log.i(TAG, "UP");
mode = Mode.NONE;
prevDx = dx;
prevDy = dy;
break;
}
scaleDetector.onTouchEvent(motionEvent);
if ((mode == Mode.DRAG && scale >= MIN_ZOOM) || mode == Mode.ZOOM) {
getParent().requestDisallowInterceptTouchEvent(true);
float maxDx = (child().getWidth() - (child().getWidth() / scale)) / 2 * scale;
float maxDy = (child().getHeight() - (child().getHeight() / scale))/ 2 * scale;
dx = Math.min(Math.max(dx, -maxDx), maxDx);
dy = Math.min(Math.max(dy, -maxDy), maxDy);
Log.i(TAG, "Width: " + child().getWidth() + ", scale " + scale + ", dx " + dx
+ ", max " + maxDx);
applyScaleAndTranslation();
}
return true;
}
});
}
// ScaleGestureDetector
#Override
public boolean onScaleBegin(ScaleGestureDetector scaleDetector) {
Log.i(TAG, "onScaleBegin");
return true;
}
#Override
public boolean onScale(ScaleGestureDetector scaleDetector) {
float scaleFactor = scaleDetector.getScaleFactor();
Log.i(TAG, "onScale" + scaleFactor);
if (lastScaleFactor == 0 || (Math.signum(scaleFactor) == Math.signum(lastScaleFactor))) {
scale *= scaleFactor;
scale = Math.max(MIN_ZOOM, Math.min(scale, MAX_ZOOM));
lastScaleFactor = scaleFactor;
} else {
lastScaleFactor = 0;
}
return true;
}
#Override
public void onScaleEnd(ScaleGestureDetector scaleDetector) {
Log.i(TAG, "onScaleEnd");
}
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
#SuppressLint("NewApi")
private void applyScaleAndTranslation() {
child().setScaleX(scale);
child().setScaleY(scale);
child().setTranslationX(dx);
child().setTranslationY(dy);
}
private View child() {
return getChildAt(0);
}
public float getStatusBarHeight() {
float result = 0;
float resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize((int) resourceId);
}
return result;
}
}
public class MainActivity extends ActionBarActivity {
com.example.target.ZoomLayout zoomerframe;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
zoomerframe=(com.example.target.ZoomLayout)findViewById(R.id.zoomerframe);
ImageView iv=new ImageView(getApplicationContext());
LayoutParams params=new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
FrameLayout frame=new FrameLayout(getApplicationContext());
iv.setLayoutParams(params);
iv.setLayoutParams(params);
iv.setImageResource(R.drawable.notifications_bg);
frame.addView(iv);
params = new FrameLayout.LayoutParams(
200,
200);
params.leftMargin = (int) 100;
params.topMargin = (int) 100;
ImageView iv2=new ImageView(getApplicationContext());
iv2.setLayoutParams(params);
iv2.setImageResource(R.drawable.trnspr);
frame.addView(iv2);
params = new FrameLayout.LayoutParams(
50,
50);
params.leftMargin = (int) 500;
params.topMargin = (int) 500;
ImageView iv3=new ImageView(getApplicationContext());
iv3.setLayoutParams(params);
iv3.setImageResource(R.drawable.trnspr);
frame.addView(iv3);
zoomerframe.addView(frame);
}
}
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
tools:context="com.example.target.MainActivity"
tools:ignore="MergeRootFrame">
<com.example.target.ZoomLayout
android:id="#+id/zoomerframe"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_centerInParent="true"
></com.example.target.ZoomLayout >
</RelativeLayout>

Move/drag/zoom an imageview in Android

I am developing an app which takes a picture from camera and then applies an imageview on top of it.
Now, I need to move and resize (with pinch) the imageview above the captured picture.
First I've implemented a simple drag behavior which worked well (I cannot use the drag event since min sdk level), but problems have came when I tried to add a pinch-to-zoom action too.
Right now I've tried several solutions found on stackoverflow, but none of them has worked for my scenario.
Here is the custom view that I'm using (found after some searchs)
https://dl.dropboxusercontent.com/u/230145/PanZoomView.java
but it has a weird behavior since when I try to do a zoom or drag it, the object gets crazy on the screen.
Anyone has an idea or solution for this?
EDIT
Finally I've solved with the following code:
Code from Java Class:
private ImageView mOrologio;
private FrameLayout mPhotoBox;
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;
...
...
private void bindViews(View rootView) {
...
mPhotoBox = (FrameLayout) rootView.findViewById(R.id.fotoBox);
mOrologio = (ImageView) rootView.findViewById(R.id.imgOrologio);
mOrologio.setOnTouchListener(this);
...
}
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);
}
#Override
public boolean onTouch(View v, MotionEvent event) {
ImageView view = (ImageView) v;
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
}
}
Code from XML Layout:
<FrameLayout
android:id="#+id/fotoBox"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="#+id/prev_list" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/imgOrologio"
android:scaleType="matrix"
android:layout_gravity="right|center_vertical" />
</FrameLayout>
So basically I've learned this lesson:
FrameLayout is needed as rootView if you want to move/zoom your ImageView
If you want to move/zoom an ImageView like in my scenario you don't need to override the ImageView class but only handle the touch events.
Thank you all for replies and hope this solution will be usefull =)
You can use a custom image view above the current screen using a frame layout. You can use the custom class here will may help you
public class CustomZoomableImageView extends ImageView {
private Paint borderPaint = null;
private Paint backgroundPaint = null;
private float mPosX = 0f;
private float mPosY = 0f;
private float mLastTouchX;
private float mLastTouchY;
private static final int INVALID_POINTER_ID = -1;
private static final String LOG_TAG = "TouchImageView";
// The ‘active pointer’ is the one currently moving our object.
private int mActivePointerId = INVALID_POINTER_ID;
public CustomZoomableImageView (Context context) {
this(context, null, 0);
}
public CustomZoomableImageView (Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.f;
// Existing code ...
public CustomZoomableImageView (Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// Create our ScaleGestureDetector
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
borderPaint = new Paint();
borderPaint.setARGB(255, 255, 128, 0);
borderPaint.setStyle(Paint.Style.STROKE);
borderPaint.setStrokeWidth(4);
backgroundPaint = new Paint();
backgroundPaint.setARGB(32, 255, 255, 255);
backgroundPaint.setStyle(Paint.Style.FILL);
}
#Override
public boolean onTouchEvent(MotionEvent ev) {
// Let the ScaleGestureDetector inspect all events.
mScaleDetector.onTouchEvent(ev);
final int action = ev.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
mLastTouchX = x;
mLastTouchY = y;
mActivePointerId = ev.getPointerId(0);
break;
}
case MotionEvent.ACTION_MOVE: {
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
// Only move if the ScaleGestureDetector isn't processing a gesture.
if (!mScaleDetector.isInProgress()) {
final float dx = x - mLastTouchX;
final float dy = y - mLastTouchY;
mPosX += dx;
mPosY += dy;
invalidate();
}
mLastTouchX = x;
mLastTouchY = y;
break;
}
case MotionEvent.ACTION_UP: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_CANCEL: {
mActivePointerId = INVALID_POINTER_ID;
break;
}
case MotionEvent.ACTION_POINTER_UP: {
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastTouchX = ev.getX(newPointerIndex);
mLastTouchY = ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
break;
}
}
return true;
}
/*
* (non-Javadoc)
*
* #see android.view.View#draw(android.graphics.Canvas)
*/
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
canvas.drawRect(0, 0, getWidth() - 1, getHeight() - 1, borderPaint);
}
#Override
public void onDraw(Canvas canvas) {
canvas.drawRect(0, 0, getWidth() - 1, getHeight() - 1, backgroundPaint);
if (this.getDrawable() != null) {
canvas.save();
canvas.translate(mPosX, mPosY);
Matrix matrix = new Matrix();
matrix.postScale(mScaleFactor, mScaleFactor, pivotPointX,
pivotPointY);
// canvas.setMatrix(matrix);
canvas.drawBitmap(
((BitmapDrawable) this.getDrawable()).getBitmap(), matrix,
null);
// this.getDrawable().draw(canvas);
canvas.restore();
}
}
/*
* (non-Javadoc)
*
* #see
* android.widget.ImageView#setImageDrawable(android.graphics.drawable.Drawable
* )
*/
#Override
public void setImageDrawable(Drawable drawable) {
// Constrain to given size but keep aspect ratio
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
mLastTouchX = mPosX = 0;
mLastTouchY = mPosY = 0;
int borderWidth = (int) borderPaint.getStrokeWidth();
mScaleFactor = Math.min(((float) getLayoutParams().width - borderWidth)
/ width, ((float) getLayoutParams().height - borderWidth)
/ height);
pivotPointX = (((float) getLayoutParams().width - borderWidth) - (int) (width * mScaleFactor)) / 2;
pivotPointY = (((float) getLayoutParams().height - borderWidth) - (int) (height * mScaleFactor)) / 2;
super.setImageDrawable(drawable);
}
float pivotPointX = 0f;
float pivotPointY = 0f;
private class ScaleListener extends
ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
pivotPointX = detector.getFocusX();
pivotPointY = detector.getFocusY();
Log.d(LOG_TAG, "mScaleFactor " + mScaleFactor);
Log.d(LOG_TAG, "pivotPointY " + pivotPointY + ", pivotPointX= "
+ pivotPointX);
mScaleFactor = Math.max(0.05f, mScaleFactor);
invalidate();
return true;
}
}
And call the setImageDrawableMethod in the custom imageView
Are you trying to crop the image. I can not comment on your post but just answer. If so then let me know because you can use CROP intent to for what you are looking for. I have implemented it and it basically takes picture with camera... crops,zooms and drag .
Try this.
Something like below will do it.
#Override public boolean onTouch(View v,MotionEvent e)
{
tap=tap2=drag=pinch=none;
int mask=e.getActionMasked();
posx=e.getX();posy=e.getY();
float midx= img.getWidth()/2f;
float midy=img.getHeight()/2f;
int fingers=e.getPointerCount();
switch(mask)
{
case MotionEvent.ACTION_POINTER_UP:
tap2=1;break;
case MotionEvent.ACTION_UP:
tap=1;break;
case MotionEvent.ACTION_MOVE:
drag=1;
}
if(fingers==2){nowsp=Math.abs(e.getX(0)-e.getX(1));}
if((fingers==2)&&(drag==0)){ tap2=1;tap=0;drag=0;}
if((fingers==2)&&(drag==1)){ tap2=0;tap=0;drag=0;pinch=1;}
if(pinch==1)
{
if(nowsp>oldsp)scale+=0.1;
if(nowsp<oldsp)scale-=0.1;
tap2=tap=drag=0;
}
if(tap2==1)
{
scale-=0.1;
tap=0;drag=0;
}
if(tap==1)
{
tap2=0;drag=0;
scale+=0.1;
}
if(drag==1)
{
movx=posx-oldx;
movy=posy-oldy;
x+=movx;
y+=movy;
tap=0;tap2=0;
}
m.setTranslate(x,y);
m.postScale(scale,scale,midx,midy);
img.setImageMatrix(m);img.invalidate();
tap=tap2=drag=none;
oldx=posx;oldy=posy;
oldsp=nowsp;
return true;
}
public void onCreate(Bundle b)
{
super.onCreate(b);
img=new ImageView(this);
img.setScaleType(ImageView.ScaleType.MATRIX);
img.setOnTouchListener(this);
path=Environment.getExternalStorageDirectory().getPath();
path=path+"/DCIM"+"/behala.jpg";
byte[] bytes;
bytes=null;
try{
FileInputStream fis;
fis=new FileInputStream(path);
BufferedInputStream bis;
bis=new BufferedInputStream(fis);
bytes=new byte[bis.available()];
bis.read(bytes);
if(bis!=null)bis.close();
if(fis!=null)fis.close();
}
catch(Exception e)
{
ret="Nothing";
}
Bitmap bmp=BitmapFactory.decodeByteArray(bytes,0,bytes.length);
img.setImageBitmap(bmp);
setContentView(img);
}
For viewing complete program see here: Program to zoom image in android

Categories

Resources