如何使用使用Matrix缩放的ImageView进行缩放

时间:2011-09-14 15:27:53

标签: android

所以我有一个使用Matrix的ImageView来缩放我正在显示的位图。我可以双击缩放到全尺寸,我的ScaleAnimation处理动画放大,这一切都很好。

现在我想再次双击缩小,但是当我使用ScaleAnimation为其设置动画时,ImageView不会绘制图像的新曝光区域(因为当前视口缩小),而是看到可见部分我已经尝试使用ViewGroup.setClipChildren(false),但是这只留下了前一帧中最后绘制的工件 - 导致了一种幻觉伸缩效果,但不是我所追求的。

我知道有许多与缩放相关的问题,但没有一个涵盖我的情况 - 特别是动画缩小操作。我确实有机制工作 - 即除了缩小动画,双击放大和缩小工作正常。

有什么建议吗?

1 个答案:

答案 0 :(得分:5)

最后我决定停止使用Android提供的Animation类,因为ScaleAnimation将一个比例应用到整个ImageView,然后与ImageView的图像Matrix的比例相结合,使其工作变得复杂(一边)从剪辑问题我有)。 因为我真正需要的是为ImageView的Matrix所做的更改设置动画,所以我实现了OnDoubleTapListener(在这篇文章的最后 - 我将它作为“练习给读者”留下来添加缺少的字段和方法 - 我使用了几个PointF和Matrix字段,以避免过多的垃圾创建)。基本上,动画本身是通过使用View.post来发布一个Runnable来实现的,该Runnable会逐步改变ImageView的图像矩阵:

public boolean onDoubleTap(MotionEvent e) {
        final float x = e.getX();
        final float y = e.getY();
        matrix.reset();
        matrix.set(imageView.getImageMatrix());
        matrix.getValues(matrixValues);
        matrix.invert(inverseMatrix);
        doubleTapImagePoint[0] = x;
        doubleTapImagePoint[1] = y;
        inverseMatrix.mapPoints(doubleTapImagePoint);
        final float scale = matrixValues[Matrix.MSCALE_X];
        final float targetScale = scale < 1.0f ? 1.0f : calculateFitToScreenScale();
        final float finalX;
        final float finalY;
        // assumption: if targetScale is less than 1, we're zooming out to fit the screen
        if (targetScale < 1.0f) {
            // scaling the image to fit the screen, we want the resulting image to be centred. We need to take
            // into account the shift that is applied to zoom on the tapped point, easiest way is to reuse
            // the transformation matrix.
            RectF imageBounds = new RectF(imageView.getDrawable().getBounds());
            // set up matrix for target 
            matrix.reset();
            matrix.postTranslate(-doubleTapImagePoint[0], -doubleTapImagePoint[1]);
            matrix.postScale(targetScale, targetScale);
            matrix.mapRect(imageBounds);

            finalX = ((imageView.getWidth() - imageBounds.width()) / 2.0f) - imageBounds.left;
            finalY = ((imageView.getHeight() - imageBounds.height()) / 2.0f) - imageBounds.top;
        } 
        // else zoom around the double-tap point
        else {
            finalX = x;
            finalY = y;
        }

        final Interpolator interpolator = new AccelerateDecelerateInterpolator();
        final long startTime = System.currentTimeMillis();
        final long duration = 800;
        imageView.post(new Runnable() {
            @Override
            public void run() {
                float t = (float) (System.currentTimeMillis() - startTime) / duration;
                t = t > 1.0f ? 1.0f : t;
                float interpolatedRatio = interpolator.getInterpolation(t);
                float tempScale = scale + interpolatedRatio * (targetScale - scale);
                float tempX = x + interpolatedRatio * (finalX - x);
                float tempY = y + interpolatedRatio * (finalY - y);
                matrix.reset();
                // translate initialPoint to 0,0 before applying zoom
                matrix.postTranslate(-doubleTapImagePoint[0], -doubleTapImagePoint[1]);
                // zoom
                matrix.postScale(tempScale, tempScale);
                // translate back to equivalent point
                matrix.postTranslate(tempX, tempY);
                imageView.setImageMatrix(matrix);
                if (t < 1f) {
                    imageView.post(this);
                }
            }
        });

        return false;
    }