我已经坚持这个问题八个小时,所以我觉得是时候得到一些帮助了。
在我开始我的问题之前,我会让我知道我已经通过这个网站和谷歌,我找到的答案都没有帮助。 (This是一个,another和another。)
这是交易:我有一个扩展SurfaceView
的类(让我们称之为MySurface
)并覆盖其中的许多方法。通常情况下,它会绘制几个正方形和文本框,这一切都很好。一旦用户开始触摸,它就会转换为Bitmap
,然后绘制每个帧,直到用户释放。
这就是问题:我想实现这样一种功能:用户可以将两根手指放在屏幕上,捏缩放,也可以平移(但只能用两根手指向下)。
我找到了一些缩放缩放的实现,并通过以下内容将它们改编为Canvas
中的MySurface
对象:
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.scale(mScaleVector.z, mScaleVector.z); // This is the scale factor as seen below
canvas.translate(mScaleVector.x, mScaleVector.y); // These are offset values from 0,0, both working fine
// Start draw code
// ...
// End draw code
canvas.restore();
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
float factor = detector.getScaleFactor();
if (Math.abs(factor - 1.0f) >= 0.0075f) {
mScaleVector.z *= factor;
mScaleVector.z = Math.max(MIN_ZOOM, Math.min(mScaleVector.z, MAX_ZOOM));
}
// ...
invalidate();
return true;
}
}
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction() & MotionEvent.ACTION_MASK;
int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
if (event.getPointerCount() == 2) {
if (action == MotionEvent.ACTION_POINTER_DOWN && pointerIndex == 1) {
// The various pivot coordinate codes would belong here
}
}
detector.onTouchEvent(event); // Calls the Scale Gesture Detector
return true;
}
虽然两个元素都能很好地工作 - 来回滚动和缩放 - 但是存在一个大问题。使用时,缩放到缩放会缩放到点0,0
,而不是缩放到手指点。
我已经尝试了很多方法来解决这个问题:
canvas.scale(mScaleVector.z, mScaleVector.z, mScaleVector.x, mScaleVector.y);
;很明显,这会产生不需要的结果,因为mScaleVector
x和y值是0偏移。translate()
方法相同的偏移量的“pivot”坐标,但这会产生相同的0,0
问题,或者在触摸视图时跳转。此外,此画布必须有界,因此用户无法永久滚动。但是,当我使用.scale(sx, sy, px, py)
方法时,它会超出我在.translate()
中设置的任何边界。
我......在这一点上对任何事情都很开放。我知道可以添加此功能,因为它可以在Android 4.0库中看到(查看单个图像时)。我试图找到处理此问题的源代码,但无济于事。
答案 0 :(得分:15)
以下是我使用ImageView
在ScaleGestureDetector
中实现缩放缩放的代码。很少或根本没有修改你也应该能够使用它,因为你也可以使用转换游戏,以便在Canvas
上绘制。
@Override
public boolean onScale(ScaleGestureDetector detector) {
float mScaleFactor = (float) Math.min(
Math.max(.8f, detector.getScaleFactor()), 1.2);
float origScale = saveScale;
saveScale *= mScaleFactor;
if (saveScale > maxScale) {
saveScale = maxScale;
mScaleFactor = maxScale / origScale;
} else if (saveScale < minScale) {
saveScale = minScale;
mScaleFactor = minScale / origScale;
}
right = width * saveScale - width
- (2 * redundantXSpace * saveScale);
bottom = height * saveScale - height
- (2 * redundantYSpace * saveScale);
if (origWidth * saveScale <= width
|| origHeight * saveScale <= height) {
matrix.postScale(mScaleFactor, mScaleFactor, width / 2, height / 2);
if (mScaleFactor < 1) {
matrix.getValues(m);
float x = m[Matrix.MTRANS_X];
float y = m[Matrix.MTRANS_Y];
if (mScaleFactor < 1) {
if (Math.round(origWidth * saveScale) < width) {
if (y < -bottom)
matrix.postTranslate(0, -(y + bottom));
else if (y > 0)
matrix.postTranslate(0, -y);
} else {
if (x < -right)
matrix.postTranslate(-(x + right), 0);
else if (x > 0)
matrix.postTranslate(-x, 0);
}
}
}
} else {
matrix.postScale(mScaleFactor, mScaleFactor, detector.getFocusX(), detector.getFocusY());
matrix.getValues(m);
float x = m[Matrix.MTRANS_X];
float y = m[Matrix.MTRANS_Y];
if (mScaleFactor < 1) {
if (x < -right)
matrix.postTranslate(-(x + right), 0);
else if (x > 0)
matrix.postTranslate(-x, 0);
if (y < -bottom)
matrix.postTranslate(0, -(y + bottom));
else if (y > 0)
matrix.postTranslate(0, -y);
}
}
return true;
}
在我的情况下,我在View的onMeasure()
方法中计算了必要值,您可能希望在SurfaceView
width = MeasureSpec.getSize(widthMeasureSpec); // Change this according to your screen size
height = MeasureSpec.getSize(heightMeasureSpec); // Change this according to your screen size
// Fit to screen.
float scale;
float scaleX = (float) width / (float) bmWidth;
float scaleY = (float) height / (float) bmHeight;
scale = Math.min(scaleX, scaleY);
matrix.setScale(scale, scale);
setImageMatrix(matrix);
saveScale = 1f;
scaleMappingRatio = saveScale / scale;
// Center the image
redundantYSpace = (float) height - (scale * (float) bmHeight);
redundantXSpace = (float) width - (scale * (float) bmWidth);
redundantYSpace /= (float) 2;
redundantXSpace /= (float) 2;
matrix.postTranslate(redundantXSpace, redundantYSpace);
origWidth = width - 2 * redundantXSpace;
origHeight = height - 2 * redundantYSpace;
right = width * saveScale - width - (2 * redundantXSpace * saveScale);
bottom = height * saveScale - height - (2 * redundantYSpace * saveScale);
setImageMatrix(matrix);
一点解释:
saveScale
是Bitmap
mScaleFactor
是您必须将您的比例乘以。
maxScale
和minScale
可以是常量值。
width
和height
是屏幕的尺寸。
redundantXSpace
和redundantYSpace
在图像边框和屏幕边框之间是空的,因为当图像小于屏幕时图像居中
origHeight
和origWidth
是位图的大小
matrix
是用于绘制位图的当前变换矩阵
诀窍是,当我第一次缩放并将图像置于初始化中心时,我选择该比例为1并且使用scaleMappingRatio
我映射了相对于图像的实际比例值。
答案 1 :(得分:-1)
protected override void OnDraw(Canvas canvas)
{
canvas.Save();
int x = (int)(canvas.Width * mScaleFactor - canvas.Width) / 2;
int y = (int)(canvas.Height * mScaleFactor - canvas.Height) / 2;
if (mPosX > (canvas.Width + x - 50))
{
mPosX = canvas.Width + x - 50;
}
if (mPosX < (50 - canvas.Width - x))
{
mPosX = 50 - canvas.Width - x;
}
if (mPosY > (canvas.Height + y - 50))
{
mPosY = canvas.Height + y - 50;
}
if (mPosY < (50 - canvas.Height - y))
{
mPosY = 50 - canvas.Height - y;
}
canvas.Translate(mPosX, mPosY);
canvas.Scale(mScaleFactor, mScaleFactor, canvas.Width/2, canvas.Height/2);
base.OnDraw(canvas);
canvas.Restore();
}
这是monodroid代码,但可以很容易地转换为JAVA。 您可以为您的边界选择任何值(此处为50像素边距)