23

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; 
            } 
            });
IZI_Shadow_IZI
  • 1,921
  • 4
  • 30
  • 59
  • I don't know if I understand it right but are you asking how to clear a zoom effect on an image? Why no save the original image in a separate Bitmap. When the user uses the zoom and it is time to get rid of the magnifying glass effect, use the original bitmap and re-draw it on the canvas and thereby erase the image with the zoom. – Gunnar Karlsson Dec 19 '12 at 10:27
  • no I am not getting the magnifying circle over the imageview. would you please share something more or class where is code working? – Deepika Lalra Jan 23 '13 at 09:15
  • I also have the need to zoom image in a transparent overlay frame which is in center of gallery so which ever the image pass through that get zoomed please suggest – Ravi May 27 '14 at 17:31

3 Answers3

19

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);
Community
  • 1
  • 1
qzikl
  • 651
  • 3
  • 7
  • @qzikl, please can you elaborate some more? or if you can write where to declare onDraw method and what is matrix here? – Deepika Lalra Jan 23 '13 at 05:46
  • hey thanks for the above code.Is there any way to show the magnified zoomed view at some fix position? – Iram Bukhari May 27 '14 at 11:15
  • When drawing the circle, do it like this: canvas.drawCircle(FIXED_POS_X, FIXED_POS_Y, 100, mPaint); – qzikl May 27 '14 at 18:05
  • 1
    This answer, while it indeed zooms a location of the image, does not seem to zoom at the proper location. Either that or there's more to it. I've notice a few answers/questions on this site that reference the code from this answer and many of them have the same issue where the points are OFFSET in the magnification. So if you touch at 100,100 it's actually magnifying at 200,200 (arbitrary numbers) – tronious Dec 08 '15 at 03:47
  • @tronious That is true. How to resolve that ? Have you figured it out ? – Karunesh Palekar Aug 30 '21 at 19:08
6

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.

blaffie
  • 505
  • 1
  • 10
  • 32
3

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)
    }
}
Mate Herber
  • 101
  • 1
  • 3
  • 1
    While this does seem to go in the right direction, I still experience problems with a slight offset of the image shown in the magnifying part. The offset seems to be relative and dependent on the coordinates of the point as it’s getting worse the further to the bottom of the image the touch point gets. – PattaFeuFeu May 14 '19 at 14:13