views:

112

answers:

1

Hey, I'm writing an app for android (although I think this is a generic question) and I need to display a large image (in an ImageView) that can be scrolled and zoomed. I've managed to get scrolling to work by capturing touch events and performing matrix translations, and i'm now working on zooming.

If I simply apply a scale transformation to the image, it zooms in at the origin, which is the top left-hand corner of the screen. I would like to zoom in at the center of the screen. From what i've read, this means I need a transformation to make the origin the center of the screen. I think what is required is something like the following- assume the center of the screen is (5, 5) for simplicity...

-Translate by (-5, -5)
-Scale by the zoom factor
-Translate by (+5, +5)*zoomfactor

Unfortunatly, this doesnt seem to work- the zoom seems to go anywhere BUT the center...can someone help me out here?

EDIT: This is the code that now works

    Matrix zoommatrix = new Matrix();
    float[] centerpoint = {targetimageview.getWidth()/2.0f, targetimageview.getHeight()/2.0f};

    zoommatrix.postScale(zoomfactor, zoomfactor, centerpoint[0], centerpoint[1]);
    zoommatrix.preConcat(targetimageview.getImageMatrix());

    targetimageview.setImageMatrix(zoommatrix);
    targetimageview.invalidate();
+3  A: 

Check ImageViewTouchBase in the Android source code's Camera app; its "zoomTo" method does this:

protected void zoomTo(float scale, float centerX, float centerY) {
    if (scale > mMaxZoom) {
        scale = mMaxZoom;
    }

    float oldScale = getScale();
    float deltaScale = scale / oldScale;

    mSuppMatrix.postScale(deltaScale, deltaScale, centerX, centerY);
    setImageMatrix(getImageViewMatrix());
    center(true, true);
}

That center method is probably the bit you'll really care about:

    protected void center(boolean horizontal, boolean vertical) {
    if (mBitmapDisplayed.getBitmap() == null) {
        return;
    }

    Matrix m = getImageViewMatrix();

    RectF rect = new RectF(0, 0,
            mBitmapDisplayed.getBitmap().getWidth(),
            mBitmapDisplayed.getBitmap().getHeight());

    m.mapRect(rect);

    float height = rect.height();
    float width  = rect.width();

    float deltaX = 0, deltaY = 0;

    if (vertical) {
        int viewHeight = getHeight();
        if (height < viewHeight) {
            deltaY = (viewHeight - height) / 2 - rect.top;
        } else if (rect.top > 0) {
            deltaY = -rect.top;
        } else if (rect.bottom < viewHeight) {
            deltaY = getHeight() - rect.bottom;
        }
    }

    if (horizontal) {
        int viewWidth = getWidth();
        if (width < viewWidth) {
            deltaX = (viewWidth - width) / 2 - rect.left;
        } else if (rect.left > 0) {
            deltaX = -rect.left;
        } else if (rect.right < viewWidth) {
            deltaX = viewWidth - rect.right;
        }
    }

    postTranslate(deltaX, deltaY);
    setImageMatrix(getImageViewMatrix());
}
Yoni Samlan
Excelent! What i needed was the overload of postScale() that took a point as well as a scale lol. The zoom code looks like this now...Alright, it doesn't fit, i'll edit my question to include it. All that was needed was to multiply the current transform matrix by one generated by the four-argument form of scale()
KJ Tsanaktsidis