请参阅下面的每个部分,以三种不同的方式描述我的问题。希望能帮助人们回答。
问题:如果您只有一个以缩放图像表示的坐标,在给定原始比例点&时,如何找到在画布/用户空间中表示的一对坐标?比例因子?
实践中的问题:
我正在尝试复制应用程序中使用的缩放功能,例如图库/地图,当您可以缩放缩放/缩小时缩放向捏合的中点移动。
开启时,我保存缩放的中心点(基于当前屏幕的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的绝对位置的方法。
答案 0 :(得分:14)
所以,在重新绘制问题后,我找到了我正在寻找的解决方案。它经历了几次迭代,但这是我如何解决的问题:
B - 点,缩放操作的中心
A1,A2,A3 - 点数,用户空间相等但画布空间不同。
您知道Bx
和By
的值,因为它们总是不变的,无论比例因子是什么(您在画布空间和用户空间都知道这个值)。
你知道Ax
&在用户空间中Ay
,这样您就可以找到Ax
到Bx
和Ay
到By
之间的距离。此测量位于用户空间中,将其转换为画布空间测量,只需将其除以比例因子即可。 (一旦转换为画布空间,您可以看到红色,橙色和黄色的这些线条。)
由于点B
是常量,它与边之间的距离是恒定的(这些由蓝线表示)。该值在用户空间和画布空间中相等。
你知道画布空间中画布的宽度,所以从总数中减去这两个画布空间测量值(Ax
到Bx
和Bx
到Edge
)您在画布空间中留下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/可以做你想做的事(我想 - 我不得不浏览你的帖子)。希望这可以帮助。如果这没有帮助,我今晚会看一看,看看能不能帮助我。