【问题标题】:How to find absolute position of click while zoomed in如何在放大时找到点击的绝对位置
【发布时间】:2012-01-01 02:24:22
【问题描述】:

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

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

实践中的问题:

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

向下保存缩放的中心点(基于当前屏幕的 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)下视图的位置和大小。

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

我需要想办法得到B的绝对位置。

【问题讨论】:

  • Android的标准View缩放+设置轴心点不行吗?
  • 不确定“标准视图缩放和枢轴点设置”是什么意思?如果您可以扩展它,那就太好了。
  • 我的意思是使用setScaleX()setScaleY(() 进行缩放,使用setPivotX()setPivotY() 设置缩放中心。它们都是 View 的方法,但我刚刚读到这些方法的 minSdkVersion 是 11(Honeycomb),所以如果您针对早期版本,这些信息可能对您毫无用处。
  • 不幸的是 2.3 :( 不过感谢您的建议!希望将来能对某人有所帮助!
  • Sony-Ericson 对缩放有一定的了解,这可能会有所帮助。 blogs.sonyericsson.com/wp/2010/05/18/…

标签: android math zooming


【解决方案1】:

这是我基于 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】:

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

    B - 点,刻度操作的中心

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

    您知道BxBy 的值,因为无论比例因子如何,它们始终是恒定的(您在画布空间和用户空间都知道这个值)。

    您知道用户空间中的AxAy,因此您可以找到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);
        }
    }
    

    【讨论】:

    • 嘿 Graeme 我面临着类似的问题?这是从过去 1 个月开始吃掉我的头。最后我发现了一个与我面临同样问题的问题。我需要你的帮助来破解解决方案?我有点理解您的上述逻辑,但需要您的更多帮助...我应该在哪里与您联系?提前致谢
    • 我是Android 聊天室的所有者之一 - 我会在那里。
    • 但在聊天中只有所有者指定的用户才能聊天?请接受请求..谢谢
    • stackoverflow.com/questions/9989170/… 请检查一下,因为我无法进入聊天室我发布了这个问题
    • 嗨@Graeme,你能解释一下最后一段代码的用途吗?比如第一种方法,这里不调用同一个方法:“float x = getAbsolutePosition(mBx, Ax);”吗?我也没有得到与您上面解释的第一种方法的关系
    【解决方案3】:

    真的很抱歉,这是一个简短的回答,很匆忙。但我最近也一直在看这个 - 我发现 http://code.google.com/p/android-multitouch-controller/ 可以做你想做的事(我想 - 我不得不略读你的帖子)。希望这可以帮助。如果这没有帮助,我今晚会好好看看,看看我是否可以提供进一步的帮助。

    【讨论】:

    • 不得不原谅我,因为此时我已经很累了,但是在这个家伙的源代码中,他使用了名为“MultiTouchObjectCanvas”的界面。在这个界面中有一个叫做“getPositionAndScale”的方法,描述为“获取被拖动对象原点的屏幕坐标,并使用缩放倍数将屏幕坐标转换为obj坐标”。这正是我想要的——不幸的是我看不到这个接口是在哪里实现的......
    猜你喜欢
    • 1970-01-01
    • 2021-06-11
    • 2016-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-31
    • 2012-07-16
    • 1970-01-01
    相关资源
    最近更新 更多