如何在自定义视图中固定捏缩放焦点?

时间:2019-03-20 09:56:11

标签: android android-custom-view scaling pinchzoom image-scaling

对于我的问题,我已经在Github上准备了a very simple test app

为简单起见,我删除了滑动,滚动约束和边缘效果(在我的实际应用中效果很好):

test app screenshot

所以我的测试应用中的the custom view仅支持滚动:

mGestureDetector = new GestureDetector(context,
        new GestureDetector.SimpleOnGestureListener() {
    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float dX, float dY) {
        mBoardScrollX -= dX;
        mBoardScrollY -= dY;

        ViewCompat.postInvalidateOnAnimation(MyView.this);
        return true;
    }
});

并用2根手指捏住变焦(但焦点已断开!):

mScaleDetector = new ScaleGestureDetector(context,
        new ScaleGestureDetector.SimpleOnScaleGestureListener() {
    @Override
    public boolean onScale(ScaleGestureDetector scaleDetector) {
        float focusX = scaleDetector.getFocusX();
        float focusY = scaleDetector.getFocusY();
        float factor = scaleDetector.getScaleFactor();

        mBoardScrollX = mBoardScrollX + focusX * (1 - factor) * mBoardScale;
        mBoardScrollY = mBoardScrollY + focusY * (1 - factor) * mBoardScale;
        mBoardScale *= factor;

        ViewCompat.postInvalidateOnAnimation(MyView.this);
        return true;
    }
});

最后,这里的代码绘制了被称为偏移的游戏板Drawable

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

    canvas.save();
    canvas.scale(mBoardScale, mBoardScale);
    canvas.translate(mBoardScrollX / mBoardScale, mBoardScrollY / mBoardScale);
    mBoard.draw(canvas);
    canvas.restore();
}

如果您尝试运行我的应用程序,您会注意到在用两指捏缩放手势缩放游戏板的同时,缩放焦点会跳动。

我的理解是,在缩放Drawable时,我需要通过调整mBoardScrollXmBoardScrollY的值来平移-使焦点保持在游戏板上的同一点坐标。所以我的计算是-

该点的位置是:

-mBoardScrollX + focusX * mBoardScale
-mBoardScrollY + focusY * mBoardScale

new 位置将位于:

-mBoardScrollX + focusX * mBoardScale * factor
-mBoardScrollY + focusY * mBoardScale * factor

通过求解这两个线性方程,我得到:

mBoardScrollX = mBoardScrollX + focusX * (1 - factor) * mBoardScale;
mBoardScrollY = mBoardScrollY + focusY * (1 - factor) * mBoardScale;

但是那行不通!

为消除任何错误,我什至尝试将焦点硬编码到我的自定义视图的中间,并且在缩放时,游戏板的中心仍在晃动:

float focusX = getWidth() / 2f;
float focusY = getHeight() / 2f;

我想我缺少一些小东西,请帮助我。

我宁愿不使用Matrix来找到解决方案,因为我认为上述计算中确实缺少一些细微的东西。是的,我已经研究了很多可比较的代码,包括Chris Banes的PhotoView和Google的InteractiveChart示例。

两次TAP更新:

pskink的基于Matrix的解决方案效果很好,但是我仍然遇到一个焦点错误的问题-

我尝试将代码添加到the custom view中,以在双击手势时将缩放比例提高100%:

public boolean onDoubleTap(final MotionEvent e) {
    float[] values = new float[9];
    mMatrix.getValues(values);
    float scale = values[Matrix.MSCALE_X];
    ValueAnimator animator = ValueAnimator.ofFloat(scale, 2f * scale);
    animator.setDuration(3000);
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animator){
            float scale = (float) animator.getAnimatedValue();
            mMatrix.setScale(scale, scale, e.getX(), e.getY());
            ViewCompat.postInvalidateOnAnimation(MyView.this);
        }
    });
    animator.start();
    return true;
}

虽然缩放正确更改,但是焦点仍然是错误的-即使将焦点坐标传递给每个setScale调用。

例如,当我在屏幕中间双击时,结果将左右平移太远:

emulator screenshot

1 个答案:

答案 0 :(得分:3)

使用Matrix是一个真正的更好的主意-代码更加简单,您不必证明自己的数学技能;-),看看Matrix#postTranslate和{使用了{1}}个方法:

Matrix#postScale