Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 7497613
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 29, 20262026-05-29T19:09:35+00:00 2026-05-29T19:09:35+00:00

I have a SurfaceView that is resposible for drawing a Bitmap as a background

  • 0

I have a SurfaceView that is resposible for drawing a Bitmap as a background and another one that will be used as an overlay. So I’ve decided to do all transformations using a Matrix that can be used for both bitmaps as it is (I think) one of the fastest ways to do it without using OpenGL.

I’ve been able to implement panning around and zooming but I have some problems with what I’ve came with:

  • I wasn’t able to find a way how to focus on the center of the two
    fingers while zooming, the image always resets to its initial state
    (that is, without panning nor scalling) before the new scale being
    applied. Besides looking wrong, that doesn’t allow the user to zoom
    out to see the whole image and then zoom in on the part that is
    important.
  • After the scalling operation the image won’t be at the
    same place after the new draw pass because the translation value will
    be different.

Is there a way to achieve that using a Matrix or is there another solution?

Code is below (I use a SurfaceHolder in a separate thread do lock the SurfaceView canvas and call its doDraw method):

public class MapSurfaceView extends SurfaceView implements SurfaceHolder.Callback {

    public void doDraw(Canvas canvas) {
        canvas.drawColor(Color.BLACK);
        canvas.drawBitmap(mBitmap, mTransformationMatrix, mPaintAA);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_POINTER_DOWN: {
                if (event.getPointerCount() == 2) {
                    mOriginalDistance = MathUtils.distanceBetween(event.getX(0), event.getX(1), event.getY(0), event.getY(1));
                    mScreenMidpoint = MathUtils.midpoint(event.getX(0), event.getX(1), event.getY(0), event.getY(1));
                    mImageMidpoint = MathUtils.midpoint((mXPosition+event.getX(0))/mScale, (mXPosition+event.getX(1))/mScale, (mYPosition+event.getY(0))/mScale, (mYPosition+event.getY(1))/mScale);
                    mOriginalScale = mScale;
                }
            }
            case MotionEvent.ACTION_DOWN: {
                mOriginalTouchPoint = new Point((int)event.getX(), (int)event.getY());
                mOriginalPosition = new Point(mXPosition, mYPosition);
                break;
            }
            case MotionEvent.ACTION_MOVE: {
                if (event.getPointerCount() == 2) {
                    final double currentDistance = MathUtils.distanceBetween(event.getX(0), event.getX(1), event.getY(0), event.getY(1));
                    if (mIsZooming || currentDistance - mOriginalDistance > mPinchToZoomTolerance || mOriginalDistance - currentDistance > mPinchToZoomTolerance) {
                        final float distanceRatio = (float) (currentDistance / mOriginalDistance);
                        float tempZoom = mOriginalScale * distanceRatio;

                        mScale = Math.min(10, Math.max(Math.min((float)getHeight()/(float)mBitmap.getHeight(), (float)getWidth()/(float)mBitmap.getWidth()), tempZoom));
                        mScale = (float) MathUtils.roundToDecimals(mScale, 1);
                        mIsZooming = true;
                        mTransformationMatrix = new Matrix();
                        mTransformationMatrix.setScale(mScale, mScale);//, mImageMidpoint.x, mImageMidpoint.y);
                    } else {
                        System.out.println("Dragging");
                        mIsZooming = false;
                        final int deltaX = (int) ((int) (mOriginalTouchPoint.x - event.getX()));
                        final int deltaY = (int) ((int) (mOriginalTouchPoint.y - event.getY()));
                        mXPosition = mOriginalPosition.x + deltaX;  
                        mYPosition = mOriginalPosition.y + deltaY;
                        validatePositions();
                        mTransformationMatrix = new Matrix();
                        mTransformationMatrix.setScale(mScale, mScale);
                        mTransformationMatrix.postTranslate(-mXPosition, -mYPosition);
                    }
                }
                break;
            }
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP: {
                mIsZooming = false;
                validatePositions();
                mTransformationMatrix = new Matrix();
                mTransformationMatrix.setScale(mScale, mScale);
                mTransformationMatrix.postTranslate(-mXPosition, -mYPosition);
            }
        }
        return true;
    }

    private void validatePositions() {
        // Lower right corner
        mXPosition = Math.min(mXPosition, (int)((mBitmap.getWidth() * mScale)-getWidth()));
        mYPosition = Math.min(mYPosition, (int)((mBitmap.getHeight() * mScale)-getHeight()));
        // Upper left corner
        mXPosition = Math.max(mXPosition, 0);
        mYPosition = Math.max(mYPosition, 0);
        // Image smaller than the container, should center it
        if (mBitmap.getWidth() * mScale <= getWidth()) {
            mXPosition = (int) -((getWidth() - (mBitmap.getWidth() * mScale))/2);
        }
        if (mBitmap.getHeight() * mScale <= getHeight()) { 
            mYPosition = (int) -((getHeight() - (mBitmap.getHeight() * mScale))/2);
        }
    }
}
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-29T19:09:36+00:00Added an answer on May 29, 2026 at 7:09 pm

    Instead of resetting the transformation matrix every time using new Matrix(), try updating it using post*(). This way, you do only operations relative to the screen. It is easier to think in terms: “zoom to this point on the screen”.

    Now some code. Having calculated mScale in zooming part:

    ...
    mScale = (float) MathUtils.roundToDecimals(mScale, 1);
    float ratio = mScale / mOriginalScale;
    mTransformationMatrix.postScale(ratio, ratio, mScreenMidpoint.x, mScreenMidpoint.y);
    

    It might be even better to recalculate mScreenMidpoint on each zooming touch event. This would allow user to change the focus point a bit while zooming. For me, it is more natural than having the focus point frozen after first two finger touch.

    During dragging, you translate using deltaX and deltaY instead of absolute points:

    mTransformationMatrix.postTranslate(-deltaX, -deltaY);
    

    Of course now you have to change your validatePositions() method to:

    • ensure deltaX and deltaY do not make image move too much, or
    • use transformation matrix to check if image is off screen and then move it to counter that

    I will describe the second method, as it is more flexible and allows to validate zooming as well.

    We calculate how much image is off screen and then move it using those values:

    void validate() {
    
        mTransformationMatrix.mapRect(new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight()));
    
        float height = rect.height();
        float width = rect.width();
    
        float deltaX = 0, deltaY = 0;
    
        // Vertical delta
        if (height < mScreenHeight) {
            deltaY = (mScreenHeight - height) / 2 - rect.top;
        } else if (rect.top > 0) {
            deltaY = -rect.top;
        } else if (rect.bottom < mScreenHeight) {
            deltaY = mScreenHeight - rect.bottom;
        }
    
        // Horziontal delta
        if (width < mScreenWidth) {
            deltaX = (mScreenWidth - width) / 2 - rect.left;
        } else if (rect.left > 0) {
            deltaX = -rect.left;
        } else if (rect.right < mScreenWidth) {
            deltaX = mScreenWidth - rect.right;
        }
    
        mTransformationMatrix.postTranslate(deltaX, deltaY)
    }
    
    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I have a SurfaceView that is being used to draw images, and I would
I'm having a problem getting a bitmap to rotate properly. I have a SurfaceView
When you have a surfaceView class, used for animation/game/... you have the onTouchEvent() method
I have a SurfaceView which is currently drawing a series of Bitmaps. These bitmaps
HI! I have a surfaceView inside a horizontal scrollview that I want to fill
I currently have a SurfaceView (named BoardView) that is being stored in a FrameLayout.
Ok so I have a class that extends SurfaceView and overrides surfaceChanged - just
I have an app that uses a Class with an extension SurfaceView to draw
Ok, Just some background... I have a fully functioning game that uses the canvas
I have an Activity with a SurfaceView that is managed by a game engine

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.