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
Related
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
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;
}
}
I've created a zoom function which is working zooming for LinearLayout and FrameLayout but not working for RecyclerView.
XML file :
<com.nm.esign.entities.ZoomLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/zoom_main1"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/fl_zoom"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view_template_img"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="30dp"
android:scrollbars="vertical" />
</LinearLayout>
</com.nm.esign.entities.ZoomLayout>
Main activity:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_template);
// prevBtn=(android.support.v7.widget.AppCompatButton)findViewById(R.id.btnDocumentPrev_temp);
// nextBtn=(android.support.v7.widget.AppCompatButton)findViewById(R.id.btnDocumentNxt_temp);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view_template_img);
LinearLayout fl_zoom=(LinearLayout)findViewById(R.id.fl_zoom)
ZoomLayout myZoomView = new ZoomLayout(ViewTemplate.this);
fl_zoom.addView(myZoomView); // working fine
// recyclerView.addView(myZoomView); // not working
....................................
}
ZoomLayout class:
/**
* Created by Lenovo5 on 3/16/2017.
*/
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);
}
}
Error coming for:-
recyclerView.addView(myZoomView);
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.nm.esign, PID: 6028
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.nm.esign/com.nm.esign.activity.ViewTemplate}: java.lang.IllegalStateException: RecyclerView has no LayoutManager
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3319)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3415)
at android.app.ActivityThread.access$1100(ActivityThread.java:229)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1821)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:7331)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
Caused by: java.lang.IllegalStateException: RecyclerView has no LayoutManager
at android.support.v7.widget.RecyclerView.generateDefaultLayoutParams(RecyclerView.java:3869)
at android.view.ViewGroup.addView(ViewGroup.java:4431)
at android.view.ViewGroup.addView(ViewGroup.java:4409)
at com.nm.esign.activity.ViewTemplate.onCreate(ViewTemplate.java:112)
at android.app.Activity.performCreate(Activity.java:6904)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1136)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3266)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3415)
at android.app.ActivityThread.access$1100(ActivityThread.java:229)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1821)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:7331)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
You must have this line in your code
LinearLayoutManager manager = new LinearLayoutManager(this);
manager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(manager);
The solution was simple, you can create the layout manager manager manually.
Code snippet from https://github.com/kanytu/example-parallaxrecycler/blob/master/app/src/main/java/com/poliveira/apps/exampleparallaxrecycler/MainActivity.java#L24
Add this
LinearLayoutManager manager = new LinearLayoutManager(this);
manager.setOrientation(LinearLayoutManager.VERTICAL);
myRecycler.setLayoutManager(manager);
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.
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>