画布捏缩放到界限内

Eri*_*ric 10 android android-canvas android-gesture

我已经坚持这个问题八个小时了,所以我觉得是时候得到一些帮助了.

在我开始我的问题之前,我只会让我知道我已经通过这个网站和谷歌,我找到的答案都没有帮助.(是一个,另一个,另一个.)

这是交易:我有一个扩展的类SurfaceView(让我们称之为MySurface)并覆盖其中的许多方法.通常情况下,它会绘制几个正方形和文本框,这一切都很好.一旦用户开始触摸,它就会转换为a 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;
}
Run Code Online (Sandbox Code Playgroud)

虽然两个元素都可以正常工作 - 来回滚动和缩放 - 但是存在一个大问题.使用时,缩放到0,0缩放会缩放到点,而不是缩放到手指点.

我已经尝试了很多方法来解决这个问题:

  • 使用canvas.scale(mScaleVector.z, mScaleVector.z, mScaleVector.x, mScaleVector.y);; 很明显,这会产生不需要的结果,因为mScaleVectorx和y值是0偏移量.
  • 管理使用与translate()方法相同的偏移量的"pivot"坐标,但这会产生相同的0,0问题,或者在触摸视图时跳转.
  • 许多其他的事情......我已经用前面提到的枢轴坐标做了很多工作,尝试将其位置基于用户的第一次触摸,并相对于每次连续手势的触摸移动它.

此外,此画布必须有界,因此用户无法永久滚动.但是,当我使用该.scale(sx, sy, px, py)方法时,它会超出我设置的范围.translate().

我......在这一点上对任何事情都很开放.我知道可以添加此功能,因为它可以在Android 4.0库中看到(查看单个图像时).我试图找到处理此问题的源代码,但无济于事.

Ada*_*nos 16

下面是我用它来实现在双指缩放的代码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;
}
Run Code Online (Sandbox Code Playgroud)

在我的情况下,我计算了onMeasure()View方法中的必要值,你可能想在你的其他地方做这个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);
Run Code Online (Sandbox Code Playgroud)

一点解释:

saveScale 是目前的规模比例 Bitmap

mScaleFactor 是您必须将您的比例乘以的因素.

maxScale并且minScale可以是常数值.

width并且height是屏幕的尺寸.

redundantXSpace并且redundantYSpace在图像边框和屏幕边框之间是空的,因为当图像小于屏幕时图像居中

origHeight并且origWidth是位图的大小

matrix 是用于绘制位图的当前变换矩阵

诀窍是,当我第一次缩放并在初始化时将图像居中时,我选择该比例为1并且scaleMappingRatio我映射了相对于图像的图像的实际比例值.

  • 欢迎你,我花了很多时间找出正确的方法,当我开始编写上面的代码时,我认为值得分享. (3认同)
  • 噢,在审查并彻底测试代码的各个方面之后,我发现那些矩阵变换是关键!如果可以的话,我会给你买啤酒!A +,先生. (2认同)