如何在放大时找到点击的绝对位置

时间:2011-11-18 09:42:35

标签: android math zooming

请参阅下面的每个部分,以三种不同的方式描述我的问题。希望能帮助人们回答。

问题:如果您只有一个以缩放图像表示的坐标,在给定原始比例点&时,如何找到在画布/用户空间中表示的一对坐标?比例因子?

实践中的问题:

我正在尝试复制应用程序中使用的缩放功能,例如图库/地图,当您可以缩放缩放/缩小时缩放向捏合的中点移动。

开启时,我保存缩放的中心点(基于当前屏幕的X,Y坐标)。然后我在检测到“缩放”手势时执行此功能:

class ImageScaleGestureDetector extends SimpleOnScaleGestureListener {
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        if(mScaleAllowed) 
            mCustomImageView.scale(detector.getScaleFactor(), mCenterX, mCenterY);
        return true;
    }
}   

CustomImageView的缩放功能如下所示:

public boolean scale(float scaleFactor, float focusX, float focusY) {
    mScaleFactor *= scaleFactor;

    // Don't let the object get too small or too large.
    mScaleFactor = Math.max(MINIMUM_SCALE_VALUE, Math.min(mScaleFactor, 5.0f));

    mCenterScaleX = focusX;
    mCenterScaleY = focusY;

    invalidate();

    return true;
}

通过覆盖onDraw方法来实现缩放图像的绘制,该方法在中心周围缩放画布并将图像绘制到其中。

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

    canvas.save();
    canvas.translate(mCenterScaleX, mCenterScaleY);
    canvas.scale(mScaleFactor, mScaleFactor);
    canvas.translate(-mCenterScaleX, -mCenterScaleY);
    mIcon.draw(canvas);
    canvas.restore();
}

从ScaleFactor 1缩放时,这一切都正常,这是因为初始mCenterX和mCenterY是基于设备屏幕的坐标。 10,10在设备上是10,10在画布上。

然而,在你已经缩放之后,那么下次你点击位置10,10时它将不再对应于画布中的10,10,因为缩放&改造已经完成的工作。

抽象问题:

下图是中心点A周围的缩放操作示例。每个框表示视图在该比例因子(1,2,3,4,5)处的位置和大小。

Example

在示例中,如果您在A附近按比例缩放2然后单击位置B,则报告为B的X,Y将基于屏幕位置 - 而不是相对于初始值0,0的位置画布。

我需要找到一种获得B的绝对位置的方法。

3 个答案:

答案 0 :(得分:14)

所以,在重新绘制问题后,我找到了我正在寻找的解决方案。它经历了几次迭代,但这是我如何解决的问题:

Solution Description

  

B - 点,缩放操作的中心

     

A1,A2,A3 - 点数,用户空间相等但画布空间不同。

您知道BxBy的值,因为它们总是不变的,无论比例因子是什么(您在画布空间和用户空间都知道这个值)。

你知道Ax&在用户空间中Ay,这样您就可以找到AxBxAyBy之间的距离。此测量位于用户空间中,将其转换为画布空间测量,只需将其除以比例因子即可。 (一旦转换为画布空间,您可以看到红色,橙色和黄色的这些线条。)

由于点B是常量,它与边之间的距离是恒定的(这些由蓝线表示)。该值在用户空间和画布空间中相等。

你知道画布空间中画布的宽度,所以从总数中减去这两个画布空间测量值(AxBxBxEdge)您在画布空间中留下A点坐标的宽度:

public float[] getAbsolutePosition(float Ax, float Ay) {

    float fromAxToBxInCanvasSpace = (mCenterScaleX - Ax) / mScaleFactor;
    float fromBxToCanvasEdge = mCanvasWidth - Bx;
    float x = mCanvasWidth - fromAxToBxInCanvasSpace - fromBxToCanvasEdge;

    float fromAyToByInCanvasSpace = (mCenterScaleY - Ay) / mScaleFactor;
    float fromByToCanvasEdge = mCanvasHeight - By;
    float y = mCanvasHeight - fromAyToByInCanvasSpace - fromByToCanvasEdge;

    return new float[] { x, y };
}

上述代码和图片描述了当您点击原始中心的左上角时。我使用相同的逻辑来找到A,无论它位于哪个象限并重构为以下内容:

public float[] getAbsolutePosition(float Ax, float Ay) {

    float x = getAbsolutePosition(mBx, Ax);
    float y = getAbsolutePosition(mBy, Ay); 

    return new float[] { x, y };
}

private float getAbsolutePosition(float oldCenter, float newCenter, float mScaleFactor) {
    if(newCenter > oldCenter) {
        return oldCenter + ((newCenter - oldCenter) / mScaleFactor);
    } else {
        return oldCenter - ((oldCenter - newCenter) / mScaleFactor);
    }
}

答案 1 :(得分:2)

以下是基于Graeme答案的解决方案:

public float[] getAbsolutePosition(float Ax, float Ay) {
    MatrixContext.drawMatrix.getValues(mMatrixValues);

    float x = mWidth - ((mMatrixValues[Matrix.MTRANS_X] - Ax) / mMatrixValues[Matrix.MSCALE_X])
            - (mWidth - getTranslationX());

    float y = mHeight - ((mMatrixValues[Matrix.MTRANS_Y] - Ay) / mMatrixValues[Matrix.MSCALE_X])
            - (mHeight - getTranslationY());

    return new float[] { x, y };
}

参数Ax和Ay是用户通过onTouch()触摸的点,我在MatrixContext类中拥有我的静态矩阵实例来保存以前的缩放/翻译值。

答案 2 :(得分:1)

真的很遗憾这是一个简短的回答,匆匆忙忙。但我最近也在考虑这个问题 - 我发现http://code.google.com/p/android-multitouch-controller/可以做你想做的事(我想 - 我不得不浏览你的帖子)。希望这可以帮助。如果这没有帮助,我今晚会看一看,看看能不能帮助我。