I am trying to allow the user to touch the image and then basically a cirular magnifier will show that will allow the user to better select a certain area on the image. When the user releases the touch the magnified portion will dissapear. This is used on several photo editing apps and I am trying to implement my own version of it. The code I have below does magnify a circular portion of the imageview but does not delete or clear the zoom once I release my finger. I currently set a bitmap to a canvas using canvas = new Canvas(bitMap);
and then set the imageview using takenPhoto.setImageBitmap(bitMap);
I am not sure if I am going about it the right way. The onTouch code is below:
zoomPos = new PointF(0,0);
takenPhoto.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
zoomPos.x = event.getX();
zoomPos.y = event.getY();
matrix.reset();
matrix.postScale(2f, 2f, zoomPos.x, zoomPos.y);
shader.setLocalMatrix(matrix);
canvas.drawCircle(zoomPos.x, zoomPos.y, 20, shaderPaint);
takenPhoto.invalidate();
break;
case MotionEvent.ACTION_MOVE:
zoomPos.x = event.getX();
zoomPos.y = event.getY();
matrix.reset();
matrix.postScale(2f, 2f, zoomPos.x, zoomPos.y);
canvas.drawCircle(zoomPos.x, zoomPos.y, 20, shaderPaint);
takenPhoto.invalidate();
break;
case MotionEvent.ACTION_UP:
//clear zoom here?
break;
case MotionEvent.ACTION_CANCEL:
break;
default:
break;
}
return true;
}
});
Adapting your code, I was able to get the following approach working.
In the onTouch function, set a global point for determining where the user has touched, and set a boolean to indicate whether zooming is currently active or not:
@Override
public boolean onTouch(View view, MotionEvent event) {
int action = event.getAction();
zoomPos.x = event.getX();
zoomPos.y = event.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
zooming = true;
this.invalidate();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
zooming = false;
this.invalidate();
break;
default:
break;
}
return true;
}
Then, in the onDraw method, you use your code for drawing the zoomed in portion:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (zooming) {
matrix.reset();
matrix.postScale(2f, 2f, zoomPos.x, zoomPos.y);
mPaint.getShader().setLocalMatrix(matrix);
canvas.drawCircle(zoomPos.x, zoomPos.y, 100, mPaint);
}
}
Note that for the shader, I used a bitmap shader as described here, which was created with:
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);
mShader = new BitmapShader(mBitmap, TileMode.CLAMP, TileMode.CLAMP);
mPaint = new Paint();
mPaint.setShader(mShader);
The best way to revert any changes made to the image will be to reload the image from the source file. Or alternatively, keep the a copy original matrix variable before transformations begun, during MotionEvent.ACTION_UP
load the original matrix.
You can implement it by using a custom imageview ,Create a class CodesforMagnifierImageView.java in your package. You can see the code for respective class at CodesforMagnifier.java and simply use the following code in your layout file instead for the imageview
<com.yourpackage.CodesforMagnifierImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/your image" />
Some people asked for a fixed place magnifier position, I experimented it and came up with the solution:
// bitmapWidth is the width of bitmap used for BitmapShader
// bitmapHeight is the height of bitmap used for BitmapShader
// canvasWidth is the width of canvas where the zoom touch events are tracked (usually has the same image as shader but can be different size)
// canvasHeight is the height of canvas where the zoom touch events are tracked
// touchPoint is the point on the canvas which area should be shown in zoom circle
// fixedZoomPoint is the center of the zoom circle (different from touch point)
// ZOOM_SCALE is the zooming ratio (e.g.: 2f)
// ZOOM_RADIUS is the radius of the zoom circle
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (zooming) {
val widthRatio = bitmapWidth / canvasWidth // This can be omitted if 1.0
val heightRatio = bitmapHeight / canvasHeight // This can be omitted if 1.0
matrix.reset()
matrix.postScale(ZOOM_SCALE, ZOOM_SCALE, touchPoint.x * widthRatio, touchPoint.y * heightRatio)
matrix.postTranslate(fixedZoomPoint.x - touchPoint.x * widthRatio, fixedZoomPoint.y - touchPoint.y * heightRatio)
paint.getShader().setLocalMatrix(matrix)
drawCircle(fixedZoomPoint.x, fixedZoomPoint.y, ZOOM_RADIUS, paint)
}
}