سؤال

I now draw a dot on the canvas, which may be zoomed in or out.

As far as I know, the drawing function, canvas.drawCircle() takes in the coordinates in canvas coordinate system. Furthermore, the co-ordinates remain unchanged when the canvas is zoomed.

E.g. previously you draw a dot at (50, 50) in the canvas coordinate system, and then you zoom in the canvas, the dot's coordinates in the canvas still remain (50, 50). But obviously, the dot has been moved w.r.t. the screen.

When the canvas is zoomed, the dot should be kept at the same position on the screen. *i.e. After the dot moves w.r.t. to the screen, I want to move it back to its original position w.r.t. the screen.*

My onDraw() function is as follows:

@Override
public void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    canvasWidth = canvas.getWidth();
    canvasHeight = canvas.getHeight();

    canvas.save();
    canvas.translate(mPosX, mPosY);
    canvas.scale(mScaleFactor, mScaleFactor);
    mImage.draw(canvas); // draw the map as the background

            Paint PointStyle = new Paint();
    PointStyle.setColor(Color.BLUE);
    PointStyle.setStyle(Paint.Style.FILL_AND_STROKE);
    PointStyle.setStrokeWidth(2);

    canvas.drawCircle(Constant.INITIAL_X, Constant.INITIAL_Y, 3, PointStyle);

    canvas.restore();
}

I tried the following method to move the dot back on screen after the scaling.

private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
    @Override
    public boolean onScale(ScaleGestureDetector detector) {

        mScaleFactor *= detector.getScaleFactor(); // accumulate the scale factors
        // Don't let the object get too small or too large.
        mScaleFactor = Math.max(1f, Math.min(mScaleFactor, 10.0f)); // 1 ~ 10

        float pinToCornerOnScreenXDistance = 0;
        float pinToCornerOnScreenYDistance = 0;
        float canvasToScreenDiffRatioX = 0;
        float canvasToScreenDiffRatioY = 0;
        float pinOnCanvasX = 0;
        float pinOnCanvasY = 0;

        // pin's location on the screen --> S:(336, 578)
        pinToCornerOnScreenXDistance = 336 - canvasLeftTopCornerOnScreenX;
        pinToCornerOnScreenYDistance = 578 - canvasLeftTopCornerOnScreenY;
        Log.d("Screen Diff", "X: " + pinToCornerOnScreenXDistance + " Y: " + pinToCornerOnScreenYDistance);

        canvasToScreenDiffRatioX = canvasWidth * mScaleFactor / 720; // screen of HTC One X --> 720*1280
        canvasToScreenDiffRatioY = canvasHeight * mScaleFactor / 1280;
        Log.d("Ratio", canvasToScreenDiffRatioX + " " + canvasToScreenDiffRatioY);

        pinOnCanvasX = 0 + pinToCornerOnScreenXDistance * canvasToScreenDiffRatioX; // canvas left top corner is the origin (0, 0)
        pinOnCanvasY = 0 + pinToCornerOnScreenYDistance * canvasToScreenDiffRatioY;
        Log.d("Pin on Canvas", "X: " + pinOnCanvasX + " Y: " + pinOnCanvasY);

        Constant.setInitialX(pinOnCanvasX);
        Constant.setInitialY(pinOnCanvasY);
        historyXSeries.set(0, Constant.INITIAL_X);
        historyYSeries.set(0, Constant.INITIAL_Y);

        invalidate();
        return true;
    }
}

The idea is based on the fact that I notice when I zoom in or out the canvas, its left top corner never moves. That is, the canvas' let top corner is the zooming center.

Then I successfully keep updating the zooming center's co-ordinates when the canvas is moved. So in this way, no matter I move the canvas or zoom it, I always have the zooming center's coordinates in my canvasLeftTopCornerOnScreenX and canvasLeftTopCornerOnScreenY.

Then I try to utilize the distance between the never-moved-during-scaling zooming center and the desired position where I hope to place my dot, (336, 578) here. I calculate it as pinToCornerOnScreenDistance.

As its name suggests, it is the on-screen distance. I have to scale it into canvas distance so that I can draw the dot, since the drawing function is based on the canvas coordinate system instead of the screen coordinate system. Currently, the code is device-specific, i.e. it is only for HTC One X, which has a 1280*720 screen, for now. So I do the scaling as follows:

canvasToScreenDiffRatioX = canvasWidth * mScaleFactor / 720;
canvasToScreenDiffRatioY = canvasHeight * mScaleFactor / 1280;

Then, finally I calculate the new on-canvas coordinates of on-screen point (336, 578) and then draw the dot there.

But the result is not correct. When I zoom the canvas, the dot I draw fails to remain at (336, 578) on screen.

Can anybody tell me where goes wrong? Or propose another way of doing this?

Any comment or Answer will be greatly appreciated!

هل كانت مفيدة؟

المحلول

Is your app using full-screen? Otherwise, it is not 1280 for the height. But I don't think that is your problem.

Based on my understanding, I will change the code to

pinOnCanvasX = 336 / mScaleFactor;
pinOnCanvasY = 578 / mScaleFactor;

which I assume when mScaleFactor == 1, the pin that on Canvas are the same on screen

نصائح أخرى

Try this one, I think this can be useful for your issue,

pinOnCanvasX = (336 * mScaleFactor) + trans_x;

pinOnCanvasY = (578 * mScaleFactor) + trans_y;

here, trans_x and trans_y is translation of canvas on x and y axis. here in your case should be mPosX, mPosY but not sure what these points means for.

The way I would like to share you is scale and translate your starting point of the circle as much as canvas does. That means if your canvas scale and translate with some value then apply the same for your starting point too.

hope this will solve your problem.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top