37

I am trying to manually get an image inside an imageview centered and fitting the screen. I need to do it with a matrix (I will later dynamically change the matrix transformation).

Problem is I can't get the image centered in the view (scale is appropriate). Here is the code:

// Compute the scale to choose (this works)
float scaleX = (float) displayWidth / (float) imageWidth;
float scaleY = (float) displayHeight / (float) imageHeight;
float minScale = Math.min(scaleX, scaleY);

// tx, ty should be the translation to take the image back to the screen center
float tx = Math.max(0, 
        0.5f * ((float) displayWidth - (minScale * imageWidth)));
float ty = Math.max(0, 
        0.5f * ((float) displayHeight - (minScale * imageHeight)));

// Compute the matrix
Matrix m = new Matrix();
m.reset();

// Middle of the image should be the scale pivot
m.postScale(minScale, imageWidth/2, imageHeight/2);

// Translate
m.postTranslate(tx, ty);

imageView.setImageMatrix(m);

The above code works if I don't center the scale on the image center (but I will need to do it later so I need to figure out the formula now).

I thought doing the following would correct the issue, but the image is still offset (towards bottom and right).

tx += 0.5*imageWidth*minScale;
ty += 0.5*imageHeight*minScale;

Some values I have: - image: 200x133 - display: 800x480 - minScale: 2.4 - final top-left corner of the image: 100, 67 (should be 17, 0)

Vincent Mimoun-Prat
  • 28,208
  • 16
  • 81
  • 124
  • Hello, I am having the same kind of requirement. can you please share me the sample here. That would save my time. – Ganesh Oct 17 '14 at 10:01

4 Answers4

75

There's a convenient method called Matrix.setRectToRect(RectF, RectF, ScaleToFit) to help you here.

Matrix m = imageView.getImageMatrix();
RectF drawableRect = new RectF(0, 0, imageWidth, imageHeight);
RectF viewRect = new RectF(0, 0, imageView.getWidth(), imageView.getHeight());
m.setRectToRect(drawableRect, viewRect, Matrix.ScaleToFit.CENTER);
imageView.setImageMatrix(m);

That should set the matrix m to have combo of scaling and translate values that is needed to show the drawable centered and fit within the ImageView widget.

Joakim Erdfelt
  • 46,896
  • 7
  • 86
  • 136
  • Thanks, I had solved the problem in the meantime by a combination of matrices. +1 for making me notice that useful function. – Vincent Mimoun-Prat May 30 '11 at 07:57
  • 1
    Care to share your solution? I have a corner case where a combination of matrices could prove useful. – Joakim Erdfelt May 30 '11 at 14:13
  • 2
    @joakime image doesn't show. Then, when I tap the image once, it will show but not centered and fitted against its view. I am using TouchImageView (source code here https://github.com/gabu/AndroidSDK-RecipeBook/blob/master/Recipe060/src/com/example/android/recipe060/TouchImageView.java) – eros Sep 28 '11 at 10:41
  • @joakime, what do you replace "imageWidth" and "imageHeight" with (line 2 in your comment above)? I'm having the same exact problem right now but I'm stuck on this part. Thanks for your help! – NewGradDev Dec 25 '12 at 19:12
  • 7
    Careful with the above, getImageMatrix() docs state "Do not change this matrix in place. If you want a different matrix applied to the drawable, be sure to call setImageMatrix()". However setImageMatrix() does a check and will not invalidate the view if the local matrix, mMatrix.equals(the matrix passed in). Hence I would recommend: Matrix m = new Matrix(); m.reset(); over getImageMatrix() in this instance. – Twice Circled Jun 29 '13 at 14:10
  • hey how can i use this in scalling bitmap draw in center of canvas on surfaceview – Sunishtha Singh May 01 '15 at 08:42
  • how can i use scaletofit after image rotation? i have tried your code but it removes my rotation effect – H Raval Aug 13 '16 at 07:22
  • Very useful for me. Thanks – hungtdo Aug 14 '17 at 08:18
5

Here is how I solved my problem using matrices (requested by joakime in the other answer):

private void setImageTransformation(float tx, float ty, float scale) {
    savedMatrix.reset();
    savedMatrix.postTranslate(-imageWidth / 2f, -imageHeight / 2f);
    savedMatrix.postScale(scale, scale);
    savedMatrix.postTranslate(tx, ty);
    imageView.setImageMatrix(savedMatrix);
}

public void resetImageMatrix() {
    if (!isImageLoaded()) return;

    imageWidth = imageView.getDrawable().getIntrinsicWidth();
    imageHeight = imageView.getDrawable().getIntrinsicHeight();

    float scaleX = (float) displayWidth / (float) imageWidth;
    float scaleY = (float) displayHeight / (float) imageHeight;
    minScale = Math.min(scaleX, scaleY);
    maxScale = 2.5f * minScale;

    initialTranslation.set(
              Math.max(0, 
                minScale * imageWidth / 2f 
                + 0.5f * (displayWidth - (minScale * imageWidth))), 
              Math.max(0, 
                minScale * imageHeight / 2f
                + 0.5f * (displayHeight - (minScale * imageHeight))));

    currentScale = minScale;
    currentTranslation.set(initialTranslation);
    initialImageRect.set(0, 0, imageWidth, imageHeight);

    setImageTransformation(initialTranslation.x, initialTranslation.y, 
                minScale);
}

I am cheating here a bit because the pinch is not really centered between the user fingers, which is acceptable in my case.

Vincent Mimoun-Prat
  • 28,208
  • 16
  • 81
  • 124
  • 4
    what is initialTranslation, savedMatrix, displayWidth, displayHeight? – eros Sep 28 '11 at 10:48
  • `initialTranslation` and `savedMatrix` are just reused `Matrix` instances. The code doesn't depend on their state because it entirely resets it before reading. `displayWidth/Height` are the dimensions of the target box into which the image must be placed. – Marko Topolnik Mar 17 '18 at 09:30
3

Or using

    m.postScale(minScale, minScale); //keep aspect ratio

    float tx = (getWidth() - bitmap1.getWidth()* scale) * 0.5f ;
    float ty = (getHeight() - bitmap1.getHeight()* scale) * 0.5f ;

    matrix.postTranslate(tx, ty );

where getWidth is the width of your imageView. Works perfectly for me

voytez
  • 1,814
  • 16
  • 16
1

Hey I was having the same issue, and you were oh so close! It was an order of operations issue, at least for me. The following worked for me:

tx = (width - imgWidth) * 0.5f * scale;
scone
  • 566
  • 1
  • 4
  • 12