I'm using opengl to render an image as texture.I want to zoom the texture,so i use ScaleGestureDetector.
Here is my code:
public void scale(float scale, float focusX, float focusY) {
Matrix.setIdentityM(uMVPMatrix, 0);
Matrix.translateM(uMVPMatrix, 0, focusX, focusY, 0f);
Matrix.scaleM(uMVPMatrix, 0, scale, scale, 0);
Matrix.translateM(uMVPMatrix, 0, -focusX, -focusY, 0f);
}
private float scale = 1f;
private float focusX;
private float focusY;
#Override
public boolean onScale(ScaleGestureDetector detector) {
renderer.scale(detector.getScaleFactor() * scale,focusX,focusY);
return false;
}
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
focusX = (detector.getFocusX() - ScreenDimensionHelper.getScreenWidth(context)/2) / ((float) ScreenDimensionHelper.getScreenWidth(context)/2);
focusY = (ScreenDimensionHelper.getScreenHeight(context)/2- detector.getFocusY()) / ((float) ScreenDimensionHelper.getScreenHeight(context)/2);
return true;
}
#Override
public void onScaleEnd(ScaleGestureDetector detector) {
this.scale = detector.getScaleFactor() * scale;
}
I want to zoom around the position where my finger touch.But I found that while I pinch the image,the image moves as well.Is there some better way to do this?
Related
First of all this is a follow up question originally asked here, Pan, Zoom and Scale a custom View for Canvas drawing in Android
Since there was no answer yet, I finally solved my issue using the android-gesture-detectors
After applying the zoom/scale gesture I found that, the canvas drawing coordinates are still pointing to the old position (before applying the zoom) and not drawing on the exact same touch coordinates. Basically, I can't get the correct canvas coordinates after scaling or dragging the canvas.
Before zooming,
After zooming out the touch points are drawing on the previous location. I want it to draw on the current touch location,
Sample code,
public class DrawingView extends View {
private void setupDrawing() {
mScaleDetector = new ScaleGestureDetector(getContext(), new ScaleListener());
mgd = new MoveGestureDetector(ctx, mgl);
sgd = new ScaleGestureDetector(ctx, sgl);
rgd = new RotateGestureDetector(ctx, rgl);
}
class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
// Don't let the object get too small or too large.
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
invalidate();
return true;
}
}
MoveGestureDetector.SimpleOnMoveGestureListener mgl = new MoveGestureDetector.SimpleOnMoveGestureListener() {
#Override
public boolean onMove(MoveGestureDetector detector) {
PointF delta = detector.getFocusDelta();
matrix.postTranslate(delta.x, delta.y);
invalidate();
return true;
}
};
ScaleGestureDetector.SimpleOnScaleGestureListener sgl = new ScaleGestureDetector.SimpleOnScaleGestureListener() {
#Override
public boolean onScale(ScaleGestureDetector detector) {
float scale = detector.getScaleFactor();
matrix.postScale(scale, scale, detector.getFocusX(), detector.getFocusY());
invalidate();
return true;
}
};
RotateGestureDetector.SimpleOnRotateGestureListener rgl = new RotateGestureDetector.SimpleOnRotateGestureListener() {
#Override
public boolean onRotate(RotateGestureDetector detector) {
matrix.postRotate(-detector.getRotationDegreesDelta(), detector.getFocusX(), detector.getFocusY());
invalidate();
return true;
}
};
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
//view given size
super.onSizeChanged(w, h, oldw, oldh);
canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
drawCanvas = new Canvas(canvasBitmap);
}
private void touch_start(float x, float y) {
undonePaths.clear();
drawPath.reset();
drawPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y, float x2, float y2) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
/* QUad to curves using a quadratic line (basically an ellipse of some sort).
LineTo is a straight line. QuadTo will smooth out jaggedies where they turn.
*/
drawPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void touch_up() {
drawPath.lineTo(mX, mY);
// commit the path to our offscreen
drawCanvas.drawPath(drawPath, drawPaint);
// kill this so we don't double draw
paths.add(drawPath);
drawPath = new Path();
drawPath.reset();
invalidate();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (isZoomable) {
mgd.onTouchEvent(event);
sgd.onTouchEvent(event);
rgd.onTouchEvent(event);
}
if (!isTouchable) {
return super.onTouchEvent(event);
} else {
//detect user touch
float x = event.getX();
float y = event.getY();
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
if (!isZoomable) {
touch_start(x, y);
}
invalidate();
break;
case MotionEvent.ACTION_MOVE:
if (!isZoomable) {
//mPositions.add(new Vector2(x - mBitmapBrushDimensions.x / 2, y - mBitmapBrushDimensions.y / 2));
if (isCustomBrush && mBitmapBrushDimensions != null) {
mPositions = new Vector2(x - mBitmapBrushDimensions.x / 2, y - mBitmapBrushDimensions.y / 2);
touch_move(x, y, x - mBitmapBrushDimensions.x / 2, y - mBitmapBrushDimensions.y / 2);
} else {
touch_move(x, y, 0, 0);
}
}
invalidate();
break;
case MotionEvent.ACTION_UP:
if (!isZoomable) {
touch_up();
}
invalidate();
break;
}
mScaleDetector.onTouchEvent(event);
return true;
}
}
#Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.setMatrix(matrix);
for (Path p : paths) {
canvas.drawPath(p, drawPaint);
drawPaint.setColor(selectedColor);
drawPaint.setStrokeWidth(brushSize);
canvas.drawPath(drawPath, drawPaint);
}
canvas.restore();
}
}
PS: MoveGestureDetector(), ScaleGestureDetector() & RotateGestureDetector() are custom classes inherited from android-gesture-detectors
Here's what I did. Basically, you have to find the difference between the "old" and new points. Skip to the bottom for the important lines...
#Override
public boolean onScale(ScaleGestureDetector detector) {
scaleFactor *= detector.getScaleFactor();
float xDiff = initialFocalPoints[0] - currentFocalPoints[0];
float yDiff = initialFocalPoints[1] - currentFocalPoints[1];
transformMatrix.setScale(scaleFactor, scaleFactor,
currentFocalPoints[0], currentFocalPoints[1]);
transformMatrix.postTranslate(xDiff, yDiff);
child.setImageMatrix(transformMatrix);
return true;
}
#Override
public boolean onScaleBegin(ScaleGestureDetector detector){
float startX = detector.getFocusX() + getScrollX();
float startY = detector.getFocusY() + getScrollY();
initialFocalPoints = new float[]{startX, startY};
if(transformMatrix.invert(inverseTransformMatrix))
inverseTransformMatrix.mapPoints(currentFocalPoints, initialFocalPoints);
return true;
}
The lines that made the difference were the following:
float xDiff = initialFocalPoints[0] - currentFocalPoints[0];
float yDiff = initialFocalPoints[1] - currentFocalPoints[1];
transformMatrix.postTranslate(xDiff, yDiff);
The answer was as simple as figuring out the difference between the two points and translating the imageview everytime the image is scaled.
To apply any transformation you have to understand the rules of mathematics. It works both for 2dim and 3 dimensions graphics.
That is, if you work with translation (T), rotation (R), scale (S) matrices to apply any transformation, you have scale object at first (multiply coordinates xyz by this matrix S) then rotate (mult. by R) then shift the object by T.
So, you apply rotation over some point you have to move the object to zero and scale then return to base point.
That is namely in your case, before applying scale, you have to shift (decrease) all coordinates by touch position then apply scale matrix by multiplication then shift by increasing all positions by this touch against.
I want to rotate & scale in same time, but it's flickering while rotating and scaling the layout. This is my code
public class CustomRelaytivelayout extends RelativeLayout implements RotationGestureDetector.OnRotationGestureListener {
private float scale = 1.0f;
private float touchX = 0.0f;
private float touchY = 0.0f;
public ScaleGestureDetector mScaleDetector;
public RotationGestureDetector mRotationDetector;
private CustomRelaytivelayout customRelaytivelayout_;
float mScaleFactor = 1;
float mPivotX;
float mPivotY;
public CustomRelaytivelayout(Context context) {
super(context);
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener(this));
mRotationDetector = new RotationGestureDetector(this);
customRelaytivelayout_ = this;
}
public CustomRelaytivelayout(Context context, AttributeSet attrs) {
super(context, attrs);
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener(this));
mRotationDetector = new RotationGestureDetector(this);
customRelaytivelayout_ = this;
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
private CustomRelaytivelayout customRelaytivelayout;
float startingSpan;
float endSpan;
float startFocusX;
float startFocusY;
public ScaleListener(CustomRelaytivelayout customRelaytivelayout_) {
customRelaytivelayout = customRelaytivelayout_;
}
#Override
public boolean onScale(ScaleGestureDetector detector) {
// Multiply scale factor
scale *= detector.getScaleFactor();
ViewHelper.setScaleX(customRelaytivelayout, scale);
ViewHelper.setScaleY(customRelaytivelayout, scale);
invalidate();
return true;
}
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
}
#Override
public void onScaleEnd(ScaleGestureDetector detector) {
}
}
private float angle = 0;
#Override
public void OnRotation(RotationGestureDetector rotationDetector) {
angle = angle + rotationDetector.getAngle();
Log.d("TAG_Angle", "" + angle);
customRelaytivelayout_.setRotation(-angle);
}
protected void dispatchDraw(Canvas canvas) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(mScaleFactor, mScaleFactor, mPivotX, mPivotY);
super.dispatchDraw(canvas);
canvas.restore();
}
public void scale(float scaleFactor, float pivotX, float pivotY) {
mScaleFactor = scaleFactor;
mPivotX = pivotX;
mPivotY = pivotY;
this.invalidate();
}
public void restore() {
mScaleFactor = 1;
this.invalidate();
}
}
Its flickering while rotating and scaling the layout.If any one have solution please share
Thanks in advance
I'm creating View that can store, move, zoom and crop a picture. I'm aware about many projects which already done this, so link to other project wouldn't be answer, but i'm not sure that it's possible to achive my request.
So basically i'm using ScaleGestureDetector with ScaleGestureDetector.SimpleOnScaleGestureListener, to scale my Canvas, here's a code:
private float MIN_SCALE_FACTOR = 0.3f;
private float MAX_SCALE_FACTOR = 10.0f;
private float mScaleFactor = 1.f;
private ScaleGestureDetector mScaleDetector;
private ScaleGestureDetector.SimpleOnScaleGestureListener mScaleListener = new ScaleGestureDetector.SimpleOnScaleGestureListener() {
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor *= detector.getScaleFactor();
mScaleFactor = Math.max(MIN_SCALE_FACTOR, Math.min(mScaleFactor, MAX_SCALE_FACTOR));
invalidate();
return super.onScale(detector);
}
};
#Override
protected void onDraw(Canvas canvas) {
if (mBitmap != null) {
canvas.save();
super.onDraw(canvas);
correctPositionValues();
canvas.scale(mScaleFactor, mScaleFactor, mPositionX, mPositionY );
canvas.drawBitmap(mBitmap, mPositionX, mPositionY, null);
canvas.restore();
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
mScaleDetector.onTouchEvent(event);
}
On my phone scale is happening almoust instantly and this looks bad.
Any suggestions highly appreciated
Scale on the previous scale factor:
private ScaleGestureDetector.SimpleOnScaleGestureListener mScaleListener = new ScaleGestureDetector.SimpleOnScaleGestureListener() {
private preScaleFactor = 1f;
#Override
public boolean onScale(ScaleGestureDetector detector) {
mScaleFactor = preScaleFactor*detector.getScaleFactor();
mScaleFactor = Math.max(MIN_SCALE_FACTOR, Math.min(mScaleFactor, MAX_SCALE_FACTOR));
invalidate();
return super.onScale(detector);
}
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
preScaleFactor = mScaleFactor;
return true;
}
};
Kotlin:
class GestureScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener() {
var multiplierScaleFactor = 0.05f
var scaleFactor = 1f
override fun onScale(detector: ScaleGestureDetector?): Boolean {
val detectorScale = (detector?.scaleFactor ?: 1f)
val realScale = detectorScale - 1f
scaleFactor = (multiplierScaleFactor * realScale) + 1
return super.onScale(detector)
}
}
For the first, need to calculate the difference between detectorScale and 1f (default scale). After that use multiplierScaleFactor (change it for your issue) value for reduce zoom speed/sensivity and return 1f (default scale) to the back.
During the past weeks I was looking for appropriate source code showing how to enable zoom and pan functionality on a custom view. All solutions that I found had some problems. For example the movement/zoom was not smooth enough or the view jumped around when releasing one finger after a scaling (two-finger) operation. So I came up with a modified solution that I want to share with you. Suggestions and enhancements are welcomed.
What’s different?
It is bad practice to calculate the difference (distance vector) on any interaction (pan, zoom) between each single events and use it to set new values. If you do so, the action does not look smooth and the view might flicker (jump around in some pixels). A better approach is to remember values when the action starts (onScaleBegin, touch-down) and calculate distances for each event in comparison to those start values.
You could handle finger indices in onTouchEvent to better distinguish between pan/move and zoom/scale interaction.
public class CanvasView extends SurfaceView implements SurfaceHolder.Callback {
final static String TAG = "CanvasView";
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
ScaleGestureDetector mScaleDetector;
InteractionMode mode;
Matrix mMatrix = new Matrix();
float mScaleFactor = 1.f;
float mTouchX;
float mTouchY;
float mTouchBackupX;
float mTouchBackupY;
float mTouchDownX;
float mTouchDownY;
Rect boundingBox = new Rect();
public CanvasView(Context context) {
super(context);
// we need to get a call for onSurfaceCreated
SurfaceHolder sh = this.getHolder();
sh.addCallback(this);
// for zooming (scaling) the view with two fingers
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
boundingBox.set(0, 0, 1024, 768);
paint.setColor(Color.GREEN);
paint.setStyle(Style.STROKE);
setFocusable(true);
// initial center/touch point of the view (otherwise the view would jump
// around on first pan/move touch
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
mTouchX = metrics.widthPixels / 2;
mTouchY = metrics.heightPixels / 2;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
mScaleDetector.onTouchEvent(event);
if (!this.mScaleDetector.isInProgress()) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
// similar to ScaleListener.onScaleEnd (as long as we don't
// handle indices of touch events)
mode = InteractionMode.None;
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "Touch down event");
mTouchDownX = event.getX();
mTouchDownY = event.getY();
mTouchBackupX = mTouchX;
mTouchBackupY = mTouchY;
// pan/move started
mode = InteractionMode.Pan;
break;
case MotionEvent.ACTION_MOVE:
// make sure we don't handle the last move event when the first
// finger is still down and the second finger is lifted up
// already after a zoom/scale interaction. see
// ScaleListener.onScaleEnd
if (mode == InteractionMode.Pan) {
Log.d(TAG, "Touch move event");
// get current location
final float x = event.getX();
final float y = event.getY();
// get distance vector from where the finger touched down to
// current location
final float diffX = x - mTouchDownX;
final float diffY = y - mTouchDownY;
mTouchX = mTouchBackupX + diffX;
mTouchY = mTouchBackupY + diffY;
CalculateMatrix(true);
}
break;
}
}
return true;
}
#Override
public void onDraw(Canvas canvas) {
int saveCount = canvas.getSaveCount();
canvas.save();
canvas.concat(mMatrix);
canvas.drawColor(Color.BLACK);
canvas.drawRect(boundingBox, paint);
canvas.restoreToCount(saveCount);
}
#Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
}
#Override
public void surfaceCreated(SurfaceHolder arg0) {
// otherwise onDraw(Canvas) won't be called
this.setWillNotDraw(false);
}
#Override
public void surfaceDestroyed(SurfaceHolder arg0) {
}
void CalculateMatrix(boolean invalidate) {
float sizeX = this.getWidth() / 2;
float sizeY = this.getHeight() / 2;
mMatrix.reset();
// move the view so that it's center point is located in 0,0
mMatrix.postTranslate(-sizeX, -sizeY);
// scale the view
mMatrix.postScale(mScaleFactor, mScaleFactor);
// re-move the view to it's desired location
mMatrix.postTranslate(mTouchX, mTouchY);
if (invalidate)
invalidate(); // re-draw
}
private class ScaleListener extends
ScaleGestureDetector.SimpleOnScaleGestureListener {
float mFocusStartX;
float mFocusStartY;
float mZoomBackupX;
float mZoomBackupY;
public ScaleListener() {
}
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
mode = InteractionMode.Zoom;
mFocusStartX = detector.getFocusX();
mFocusStartY = detector.getFocusY();
mZoomBackupX = mTouchX;
mZoomBackupY = mTouchY;
return super.onScaleBegin(detector);
}
#Override
public void onScaleEnd(ScaleGestureDetector detector) {
mode = InteractionMode.None;
super.onScaleEnd(detector);
}
#Override
public boolean onScale(ScaleGestureDetector detector) {
if (mode != InteractionMode.Zoom)
return true;
Log.d(TAG, "Touch scale event");
// get current scale and fix its value
float scale = detector.getScaleFactor();
mScaleFactor *= scale;
mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f));
// get current focal point between both fingers (changes due to
// movement)
float focusX = detector.getFocusX();
float focusY = detector.getFocusY();
// get distance vector from initial event (onScaleBegin) to current
float diffX = focusX - mFocusStartX;
float diffY = focusY - mFocusStartY;
// scale the distance vector accordingly
diffX *= scale;
diffY *= scale;
// set new touch position
mTouchX = mZoomBackupX + diffX;
mTouchY = mZoomBackupY + diffY;
CalculateMatrix(true);
return true;
}
}
}
I am trying to implement pinch zoom and drag using Android's gesture listener and scale listener. The problem is that when I perform pinch zoom, the image (which I am trying to zoom) bounces to a particular location. Also the zoom position is not centered.
The following code demonstrates what I am trying to achieve. Any idea why the image is jumping (and how to correct it) ?
public class CustomView extends View {
Bitmap image;
int screenHeight;
int screenWidth;
Paint paint;
GestureDetector gestures;
ScaleGestureDetector scaleGesture;
float scale = 1.0f;
float horizontalOffset, verticalOffset;
int NORMAL = 0;
int ZOOM = 1;
int DRAG = 2;
boolean isScaling = false;
float touchX, touchY;
int mode = NORMAL;
public CustomView(Context context) {
super(context);
//initializing variables
image = BitmapFactory.decodeResource(getResources(),
R.drawable.image_name);
//This is a full screen view
screenWidth = getResources().getDisplayMetrics().widthPixels;
screenHeight = getResources().getDisplayMetrics().heightPixels;
paint = new Paint();
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
paint.setColor(Color.WHITE);
scaleGesture = new ScaleGestureDetector(getContext(),
new ScaleListener());
gestures = new GestureDetector(getContext(), new GestureListener());
mode = NORMAL;
initialize();
}
//Best fit image display on canvas
private void initialize() {
float imgPartRatio = image.getWidth() / (float) image.getHeight();
float screenRatio = (float) screenWidth / (float) screenHeight;
if (screenRatio > imgPartRatio) {
scale = ((float) screenHeight) / (float) (image.getHeight()); // fit height
horizontalOffset = ((float) screenWidth - scale
* (float) (image.getWidth())) / 2.0f;
verticalOffset = 0;
} else {
scale = ((float) screenWidth) / (float) (image.getWidth()); // fit width
horizontalOffset = 0;
verticalOffset = ((float) screenHeight - scale
* (float) (image.getHeight())) / 2.0f;
}
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.drawColor(0, Mode.CLEAR);
canvas.drawColor(Color.WHITE);
if(mode == DRAG || mode == NORMAL) {
//This works perfectly as expected
canvas.translate(horizontalOffset, verticalOffset);
canvas.scale(scale, scale);
canvas.drawBitmap(image, getMatrix(), paint);
}
else if (mode == ZOOM) {
//PROBLEM AREA - when applying pinch zoom,
//the image jumps to a position abruptly
canvas.scale(scale, scale, touchX, touchY);
canvas.drawBitmap(image, getMatrix(), paint);
}
canvas.restore();
}
public class ScaleListener implements OnScaleGestureListener {
#Override
public boolean onScale(ScaleGestureDetector detector) {
float scaleFactorNew = detector.getScaleFactor();
if (detector.isInProgress()) {
touchX = detector.getFocusX();
touchY = detector.getFocusY();
scale *= scaleFactorNew;
invalidate(0, 0, screenWidth, screenHeight);
}
return true;
}
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
isScaling = true;
mode=ZOOM;
return true;
}
#Override
public void onScaleEnd(ScaleGestureDetector detector) {
mode = NORMAL;
isScaling = false;
}
}
public class GestureListener implements GestureDetector.OnGestureListener,
GestureDetector.OnDoubleTapListener {
#Override
public boolean onDown(MotionEvent e) {
isScaling = false;
return true;
}
#Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
if (!isScaling) {
mode = DRAG;
isScaling = false;
horizontalOffset -= distanceX;
verticalOffset -= distanceY;
invalidate(0, 0, screenWidth, screenHeight);
} else {
mode = ZOOM;
isScaling = true;
}
return true;
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
scaleGesture.onTouchEvent(event);
gestures.onTouchEvent(event);
return true;
}
}
Thanks in advance.
I have implemented this behaviour, and I used a matrix to handle all the zooming and scrolling (and rotation, in my case). It makes for neat code and works like clockwork.
Store a Matrix as a class member:
Matrix drawMatrix;
Edit: Store old focus point, used to get the focus shift during scaling.
float lastFocusX;
float lastFocusY;
Edit: Set lastFocus variables in onScaleBegin
#Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
lastFocusX = detector.getFocusX();
lastFocusY = detector.getFocusY();
return true;
}
Replace your onScale:
#Override
public boolean onScale(ScaleGestureDetector detector) {
Matrix transformationMatrix = new Matrix();
float focusX = detector.getFocusX();
float focusY = detector.getFocusY();
//Zoom focus is where the fingers are centered,
transformationMatrix.postTranslate(-focusX, -focusY);
transformationMatrix.postScale(detector.getScaleFactor(), detector.getScaleFactor());
/* Adding focus shift to allow for scrolling with two pointers down. Remove it to skip this functionality. This could be done in fewer lines, but for clarity I do it this way here */
//Edited after comment by chochim
float focusShiftX = focusX - lastFocusX;
float focusShiftY = focusY - lastFocusY;
transformationMatrix.postTranslate(focusX + focusShiftX, focusY + focusShiftY);
drawMatrix.postConcat(transformationMatrix);
lastFocusX = focusX;
lastFocusY = focusY;
invalidate();
return true;
}
Similarly in onScroll:
#Override
public boolean onScroll(MotionEvent downEvent, MotionEvent currentEvent,
float distanceX, float distanceY) {
drawMatrix.postTranslate(-distanceX, -distanceY);
invalidate();
return true;
}
in onDraw; Draw with your drawMatrix:
canvas.drawBitmap(image, drawMatrix, paint);
Happy coding!