Rectangle 27 121

After clarifications: This cannot be done in xml only. It is not possible to scale both the image and the ImageView so that image's one dimension would always be 250dp and the ImageView would have the same dimensions as the image.

This code scales Drawable of an ImageView to stay in a square like 250dp x 250dp with one dimension exactly 250dp and keeping the aspect ratio. Then the ImageView is resized to match the dimensions of the scaled image. The code is used in an activity. I tested it via button click handler.

private void scaleImage(ImageView view) throws NoSuchElementException  {
    // Get bitmap from the the ImageView.
    Bitmap bitmap = null;

    try {
        Drawable drawing = view.getDrawable();
        bitmap = ((BitmapDrawable) drawing).getBitmap();
    } catch (NullPointerException e) {
        throw new NoSuchElementException("No drawable on given view");
    } catch (ClassCastException e) {
        // Check bitmap is Ion drawable
        bitmap = Ion.with(view).getBitmap();
    }

    // Get current dimensions AND the desired bounding box
    int width = 0;

    try {
        width = bitmap.getWidth();
    } catch (NullPointerException e) {
        throw new NoSuchElementException("Can't find bitmap on given view/drawable");
    }

    int height = bitmap.getHeight();
    int bounding = dpToPx(250);
    Log.i("Test", "original width = " + Integer.toString(width));
    Log.i("Test", "original height = " + Integer.toString(height));
    Log.i("Test", "bounding = " + Integer.toString(bounding));

    // Determine how much to scale: the dimension requiring less scaling is
    // closer to the its side. This way the image always stays inside your
    // bounding box AND either x/y axis touches it.  
    float xScale = ((float) bounding) / width;
    float yScale = ((float) bounding) / height;
    float scale = (xScale <= yScale) ? xScale : yScale;
    Log.i("Test", "xScale = " + Float.toString(xScale));
    Log.i("Test", "yScale = " + Float.toString(yScale));
    Log.i("Test", "scale = " + Float.toString(scale));

    // Create a matrix for the scaling and add the scaling data
    Matrix matrix = new Matrix();
    matrix.postScale(scale, scale);

    // Create a new bitmap and convert it to a format understood by the ImageView 
    Bitmap scaledBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
    width = scaledBitmap.getWidth(); // re-use
    height = scaledBitmap.getHeight(); // re-use
    BitmapDrawable result = new BitmapDrawable(scaledBitmap);
    Log.i("Test", "scaled width = " + Integer.toString(width));
    Log.i("Test", "scaled height = " + Integer.toString(height));

    // Apply the scaled bitmap
    view.setImageDrawable(result);

    // Now change ImageView's dimensions to match the scaled image
    LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams(); 
    params.width = width;
    params.height = height;
    view.setLayoutParams(params);

    Log.i("Test", "done");
}

private int dpToPx(int dp) {
    float density = getApplicationContext().getResources().getDisplayMetrics().density;
    return Math.round((float)dp * density);
}

The xml code for the ImageView:

<ImageView a:id="@+id/image_box"
    a:background="#ff0000"
    a:src="@drawable/star"
    a:layout_width="wrap_content"
    a:layout_height="wrap_content"
    a:layout_marginTop="20dp"
    a:layout_gravity="center_horizontal"/>

UPDATE 7th, November 2012: Added null pointer check as suggested in comments

Ok. That cannot be done in xml only. Java code is required. With xml you can either scale the image or the ImageView, not both.

hi can some one say me what is Ion in the line bitmap = Ion.with(view).getBitmap();

Ion is a framework for asynchronous networking and image loading: github.com/koush/ion

android - Fit image into ImageView, keep aspect ratio and then resize ...

android imageview scale
Rectangle 27 120

After clarifications: This cannot be done in xml only. It is not possible to scale both the image and the ImageView so that image's one dimension would always be 250dp and the ImageView would have the same dimensions as the image.

This code scales Drawable of an ImageView to stay in a square like 250dp x 250dp with one dimension exactly 250dp and keeping the aspect ratio. Then the ImageView is resized to match the dimensions of the scaled image. The code is used in an activity. I tested it via button click handler.

private void scaleImage(ImageView view) throws NoSuchElementException  {
    // Get bitmap from the the ImageView.
    Bitmap bitmap = null;

    try {
        Drawable drawing = view.getDrawable();
        bitmap = ((BitmapDrawable) drawing).getBitmap();
    } catch (NullPointerException e) {
        throw new NoSuchElementException("No drawable on given view");
    } catch (ClassCastException e) {
        // Check bitmap is Ion drawable
        bitmap = Ion.with(view).getBitmap();
    }

    // Get current dimensions AND the desired bounding box
    int width = 0;

    try {
        width = bitmap.getWidth();
    } catch (NullPointerException e) {
        throw new NoSuchElementException("Can't find bitmap on given view/drawable");
    }

    int height = bitmap.getHeight();
    int bounding = dpToPx(250);
    Log.i("Test", "original width = " + Integer.toString(width));
    Log.i("Test", "original height = " + Integer.toString(height));
    Log.i("Test", "bounding = " + Integer.toString(bounding));

    // Determine how much to scale: the dimension requiring less scaling is
    // closer to the its side. This way the image always stays inside your
    // bounding box AND either x/y axis touches it.  
    float xScale = ((float) bounding) / width;
    float yScale = ((float) bounding) / height;
    float scale = (xScale <= yScale) ? xScale : yScale;
    Log.i("Test", "xScale = " + Float.toString(xScale));
    Log.i("Test", "yScale = " + Float.toString(yScale));
    Log.i("Test", "scale = " + Float.toString(scale));

    // Create a matrix for the scaling and add the scaling data
    Matrix matrix = new Matrix();
    matrix.postScale(scale, scale);

    // Create a new bitmap and convert it to a format understood by the ImageView 
    Bitmap scaledBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
    width = scaledBitmap.getWidth(); // re-use
    height = scaledBitmap.getHeight(); // re-use
    BitmapDrawable result = new BitmapDrawable(scaledBitmap);
    Log.i("Test", "scaled width = " + Integer.toString(width));
    Log.i("Test", "scaled height = " + Integer.toString(height));

    // Apply the scaled bitmap
    view.setImageDrawable(result);

    // Now change ImageView's dimensions to match the scaled image
    LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams(); 
    params.width = width;
    params.height = height;
    view.setLayoutParams(params);

    Log.i("Test", "done");
}

private int dpToPx(int dp) {
    float density = getApplicationContext().getResources().getDisplayMetrics().density;
    return Math.round((float)dp * density);
}

The xml code for the ImageView:

<ImageView a:id="@+id/image_box"
    a:background="#ff0000"
    a:src="@drawable/star"
    a:layout_width="wrap_content"
    a:layout_height="wrap_content"
    a:layout_marginTop="20dp"
    a:layout_gravity="center_horizontal"/>

UPDATE 7th, November 2012: Added null pointer check as suggested in comments

Ok. That cannot be done in xml only. Java code is required. With xml you can either scale the image or the ImageView, not both.

hi can some one say me what is Ion in the line bitmap = Ion.with(view).getBitmap();

Ion is a framework for asynchronous networking and image loading: github.com/koush/ion

android - Fit image into ImageView, keep aspect ratio and then resize ...

android imageview scale
Rectangle 27 2

We can use scaleType property of the ImageView to scale Image in ImageView. Android provides 8 scaleType properties we can use according user requirement.

1) CENTER : Center the image in the view, but perform no scaling.

2) CENTER_CROP : Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions (width and height) of the image will be equal to or larger than the corresponding dimension of the view (minus padding).

3) CENTER_INSIDE : Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions (width and height) of the image will be equal to or less than the corresponding dimension of the view (minus padding).

4) FIT_CENTER : Scale the image using CENTER.

5) FIT_END : Scale the image using END.

6) FIT_START : Scale the image using START.

7) FIT_XY : Scale the image using FILL.

8) MATRIX : Scale using the image matrix when drawing.

android - how to scale an image in an ImageView so that it "fits" - St...

android imageview scaling
Rectangle 27 2

We can use scaleType property of the ImageView to scale Image in ImageView. Android provides 8 scaleType properties we can use according user requirement.

1) CENTER : Center the image in the view, but perform no scaling.

2) CENTER_CROP : Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions (width and height) of the image will be equal to or larger than the corresponding dimension of the view (minus padding).

3) CENTER_INSIDE : Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions (width and height) of the image will be equal to or less than the corresponding dimension of the view (minus padding).

4) FIT_CENTER : Scale the image using CENTER.

5) FIT_END : Scale the image using END.

6) FIT_START : Scale the image using START.

7) FIT_XY : Scale the image using FILL.

8) MATRIX : Scale using the image matrix when drawing.

android - how to scale an image in an ImageView so that it "fits" - St...

android imageview scaling
Rectangle 27 1

public class TouchImageView extends ImageView {
    private Matrix matrix;
    // We can be in one of these 3 states
    private static final int NONE = 0;
    private static final int DRAG = 1;
    private static final int ZOOM = 2;
    private int mode = NONE;
    // Remember some things for zooming
    private final PointF last = new PointF();
    private final PointF start = new PointF();
    private float maxScale = 3f;
    private float[] m;
    private int viewWidth;
    private int viewHeight;
    private static final int CLICK = 3;
    private float saveScale = 1f;
    private float origWidth;
    private float origHeight;
    private int oldMeasuredHeight;
    private ScaleGestureDetector mScaleDetector;

    public TouchImageView(Context context) {
        super(context);
        sharedConstructing(context);
    }

    public TouchImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        sharedConstructing(context);
    }

    private void sharedConstructing(Context context) {

        super.setClickable(true);

        mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
        matrix = new Matrix();
        m = new float[9];
        setImageMatrix(matrix);
        setScaleType(ScaleType.MATRIX);

        setOnTouchListener(new OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {

                mScaleDetector.onTouchEvent(event);
                PointF curr = new PointF(event.getX(), event.getY());
                switch (event.getAction()) {

                    case MotionEvent.ACTION_DOWN:
                        last.set(curr);
                        start.set(last);
                        mode = DRAG;
                        break;

                    case MotionEvent.ACTION_MOVE:
                        if (mode == DRAG) {
                            float deltaX = curr.x - last.x;
                            float deltaY = curr.y - last.y;
                            float fixTransX = getFixDragTrans(deltaX, viewWidth, origWidth * saveScale);
                            float fixTransY = getFixDragTrans(deltaY, viewHeight, origHeight * saveScale);
                            matrix.postTranslate(fixTransX, fixTransY);
                            fixTrans();
                            last.set(curr.x, curr.y);
                        }

                        break;

                    case MotionEvent.ACTION_UP:
                        mode = NONE;
                        int xDiff = (int) Math.abs(curr.x - start.x);
                        int yDiff = (int) Math.abs(curr.y - start.y);
                        if (xDiff < CLICK && yDiff < CLICK)
                            performClick();
                        break;

                    case MotionEvent.ACTION_POINTER_UP:
                        mode = NONE;
                        break;
                }

                setImageMatrix(matrix);
                invalidate();
                return true; // indicate event was handled
            }

        });
    }

    public void setMaxZoom(float x) {

        maxScale = x;

    }

    private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {

        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            mode = ZOOM;
            return true;
        }

        @Override
        public boolean onScale(ScaleGestureDetector detector) {

            float mScaleFactor = detector.getScaleFactor();
            float origScale = saveScale;
            saveScale *= mScaleFactor;
            float minScale = 1f;
            if (saveScale > maxScale) {
                saveScale = maxScale;
                mScaleFactor = maxScale / origScale;
            } else if (saveScale < minScale) {
                saveScale = minScale;
                mScaleFactor = minScale / origScale;
            }

            if (origWidth * saveScale <= viewWidth || origHeight * saveScale <= viewHeight)
                matrix.postScale(mScaleFactor, mScaleFactor, viewWidth / 2, viewHeight / 2);
            else
                matrix.postScale(mScaleFactor, mScaleFactor, detector.getFocusX(), detector.getFocusY());

            fixTrans();
            return true;
        }
    }

    private void fixTrans() {

        matrix.getValues(m);
        float transX = m[Matrix.MTRANS_X];
        float transY = m[Matrix.MTRANS_Y];
        float fixTransX = getFixTrans(transX, viewWidth, origWidth * saveScale);
        float fixTransY = getFixTrans(transY, viewHeight, origHeight * saveScale);
        if (fixTransX != 0 || fixTransY != 0)
            matrix.postTranslate(fixTransX, fixTransY);
    }


    private float getFixTrans(float trans, float viewSize, float contentSize) {

        float minTrans, maxTrans;
        if (contentSize <= viewSize) {
            minTrans = 0;
            maxTrans = viewSize - contentSize;
        } else {
            minTrans = viewSize - contentSize;
            maxTrans = 0;
        }

        if (trans < minTrans)
            return -trans + minTrans;
        if (trans > maxTrans)
            return -trans + maxTrans;
        return 0;

    }

    private float getFixDragTrans(float delta, float viewSize, float contentSize) {

        if (contentSize <= viewSize) {
            return 0;
        }
        return delta;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        viewWidth = MeasureSpec.getSize(widthMeasureSpec);
        viewHeight = MeasureSpec.getSize(heightMeasureSpec);
        // Rescales image on rotation
        if (oldMeasuredHeight == viewWidth && oldMeasuredHeight == viewHeight
                || viewWidth == 0 || viewHeight == 0)
            return;

        oldMeasuredHeight = viewHeight;
        int oldMeasuredWidth = viewWidth;

        if (saveScale == 1) {
            //Fit to screen.
            float scale;
            Drawable drawable = getDrawable();
            if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0)
                return;

            int bmWidth = drawable.getIntrinsicWidth();
            int bmHeight = drawable.getIntrinsicHeight();
            Log.d("bmSize", "bmWidth: " + bmWidth + " bmHeight : " + bmHeight);
            float scaleX = (float) viewWidth / (float) bmWidth;
            float scaleY = (float) viewHeight / (float) bmHeight;
            scale = Math.min(scaleX, scaleY);
            matrix.setScale(scale, scale);
            // Center the image
            float redundantYSpace = (float) viewHeight - (scale * (float) bmHeight);
            float redundantXSpace = (float) viewWidth - (scale * (float) bmWidth);
            redundantYSpace /= (float) 2;
            redundantXSpace /= (float) 2;
            matrix.postTranslate(redundantXSpace, redundantYSpace);
            origWidth = viewWidth - 2 * redundantXSpace;
            origHeight = viewHeight - 2 * redundantYSpace;
            setImageMatrix(matrix);
        }
        fixTrans();
    }
}
<test.com.TouchImageView
    android:id="@+id/iv_zoom"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

How to zoom in an ImageView in Android - Stack Overflow

android imageview zooming
Rectangle 27 4

float[] lastEvent = null;
float d = 0f;
float newRot = 0f;

 public boolean onTouch(View v, MotionEvent event) {
            ImageView view = (ImageView) v;

            // Handle touch events here...
            switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_DOWN:
                savedMatrix.set(matrix);
                start.set(event.getX(), event.getY());
                mode = DRAG;
                lastEvent = null;
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                oldDist = spacing(event);
                if (oldDist > 10f) {
                    savedMatrix.set(matrix);
                    midPoint(mid, event);
                    mode = ZOOM;
                }
                lastEvent = new float[4];
                lastEvent[0] = event.getX(0);
                lastEvent[1] = event.getX(1);
                lastEvent[2] = event.getY(0);
                lastEvent[3] = event.getY(1);
                d = rotation(event);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_POINTER_UP:
                mode = NONE;
                lastEvent = null;
                break;
            case MotionEvent.ACTION_MOVE:
                if (mode == DRAG) {
                    // ...
                    matrix.set(savedMatrix);
                    matrix.postTranslate(event.getX() - start.x, event.getY()
                            - start.y);
                } else if (mode == ZOOM && event.getPointerCount() == 2) {
                    float newDist = spacing(event);
                    matrix.set(savedMatrix);
                    if (newDist > 10f) {
                        float scale = newDist / oldDist;
                        matrix.postScale(scale, scale, mid.x, mid.y);
                    }
                    if (lastEvent != null) {
                        newRot = rotation(event);
                        float r = newRot - d;
                        matrix.postRotate(r, view.getMeasuredWidth() / 2,
                                view.getMeasuredHeight() / 2);
                    }
                }
                break;
            }

            view.setImageMatrix(matrix);

            return true;
        }
private float rotation(MotionEvent event) {
        double delta_x = (event.getX(0) - event.getX(1));
        double delta_y = (event.getY(0) - event.getY(1));
        double radians = Math.atan2(delta_y, delta_x);

        return (float) Math.toDegrees(radians);
    }

rotation - Android ImageView zoom, rotate, position change prob - Stac...

android rotation imageview zoom ontouchlistener
Rectangle 27 3

  • Scale the container from 0.5 to 1.0
  • Zoom the inner image from 2.0 to 1.0

I assume this is as simple as

float zoom = 1.0F + value;
float scale = 1.0F / zoom;
img.setScaleX(scale);
img.setScaleY(scale);
...
matrix.postScale(zoom, zoom);

To scale/zoom from the center of the image you might need to do something like this (perhaps swapping the "-" order)...

matrix.postTranslate(translateCropX, translateCropY);
matrix.postScale(zoom, zoom);
matrix.postTranslate(-translateCropX, -translateCropY);

The result is the inner image remains the same resolution, always.

However you also mention portrait/landscape differences, so this gets a bit more complicated. I find the most difficult thing in cases such as this is to solidly define what you want to have happen in all cases.

I'm guessing you're after a grid of small thumbnails, all of the same size (which mean they all have the same aspect ratio). You want to display images within the thumbnail containers of different aspect ratios and size, but zoomed in. When you select an image, the borders expand (overlapping other images?). There's the constraint that the shown image must keep the same resolution. You also mention that the complete image must be visible after selecting it, and twice the thumbnail size. This last point raises another question - twice the thumbnails height, width or both (in which case do you crop or scale if the image aspect ratio doesn't match). Another issue that might crop up is the case when an image has insufficient resolution.

I have edited my question. Your reasoning is good, but will result in a "squished" image during the animation. But thanks for pointing out the others StackOverflow answers. I will look into them. :)

it seems strange that a uniform scale would squash the image. this to me points to android not expecting a frequently updating matrix (or doesn't expect it in addition to updating the img scale). What happens if you just animate the matrix or the container individually? Perhaps a more direct approach is to manually draw to a canvas (this would certainly give you more explicit control over what's happening and possible better performance)?

The Android animation works perfectly. This is really a mathematic question. I'm scaling the imageview 1:1 (the ImageView is scaled 0.5 to 1 on the X and Y axis), but the image matrix has a different ratio (3:1 for a landscape image for example). And I can't find the matrix formula that would work for every case.

does something along the lines of this work... float aspectRatio = translateCropX/translateCropY; matrix.postScale(aspectRatio * zoom, zoom); Saying the image is squished during the animation, but not before and after suggests this won't work. Perhaps instead, something like this is needed... float zoomX = 1.0F + value * aspectRatio; (just a guess)

android - How to animate a matrix to "crop-out" an image? - Stack Over...

android graphics matrix transformation objectanimator
Rectangle 27 3

  • Scale the container from 0.5 to 1.0
  • Zoom the inner image from 2.0 to 1.0

I assume this is as simple as

float zoom = 1.0F + value;
float scale = 1.0F / zoom;
img.setScaleX(scale);
img.setScaleY(scale);
...
matrix.postScale(zoom, zoom);

To scale/zoom from the center of the image you might need to do something like this (perhaps swapping the "-" order)...

matrix.postTranslate(translateCropX, translateCropY);
matrix.postScale(zoom, zoom);
matrix.postTranslate(-translateCropX, -translateCropY);

The result is the inner image remains the same resolution, always.

However you also mention portrait/landscape differences, so this gets a bit more complicated. I find the most difficult thing in cases such as this is to solidly define what you want to have happen in all cases.

I'm guessing you're after a grid of small thumbnails, all of the same size (which mean they all have the same aspect ratio). You want to display images within the thumbnail containers of different aspect ratios and size, but zoomed in. When you select an image, the borders expand (overlapping other images?). There's the constraint that the shown image must keep the same resolution. You also mention that the complete image must be visible after selecting it, and twice the thumbnail size. This last point raises another question - twice the thumbnails height, width or both (in which case do you crop or scale if the image aspect ratio doesn't match). Another issue that might crop up is the case when an image has insufficient resolution.

I have edited my question. Your reasoning is good, but will result in a "squished" image during the animation. But thanks for pointing out the others StackOverflow answers. I will look into them. :)

it seems strange that a uniform scale would squash the image. this to me points to android not expecting a frequently updating matrix (or doesn't expect it in addition to updating the img scale). What happens if you just animate the matrix or the container individually? Perhaps a more direct approach is to manually draw to a canvas (this would certainly give you more explicit control over what's happening and possible better performance)?

The Android animation works perfectly. This is really a mathematic question. I'm scaling the imageview 1:1 (the ImageView is scaled 0.5 to 1 on the X and Y axis), but the image matrix has a different ratio (3:1 for a landscape image for example). And I can't find the matrix formula that would work for every case.

does something along the lines of this work... float aspectRatio = translateCropX/translateCropY; matrix.postScale(aspectRatio * zoom, zoom); Saying the image is squished during the animation, but not before and after suggests this won't work. Perhaps instead, something like this is needed... float zoomX = 1.0F + value * aspectRatio; (just a guess)

android - How to animate a matrix to "crop-out" an image? - Stack Over...

android graphics matrix transformation objectanimator
Rectangle 27 6

I realise you (and the other answers) have been trying to solve this problem using matrix manipulation, but I'd like to propose a different approach with the same visual effect as outlined in your question.

In stead of using a matrix to manipulate to visible area of the image(view), why not define this visible area in terms of clipping? This is a rather straightforward problem to solve: all we need to do is define the visible rectangle and simply disregard any content that falls outside of its bounds. If we then animate these bounds, the visual effect is as if the crop bounds scale up and down.

Luckily, a Canvas supports clipping through a variety of clip*() methods to help us out here. Animating the clipping bounds is easy and can be done in a similar fashion as in your own code snippet.

If you throw everything together in a simple extension of a regular ImageView (for the sake of encapsulation), you would get something that looks this:

public class ClippingImageView extends ImageView {

    private final Rect mClipRect = new Rect();

    public ClippingImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initClip();
    }

    public ClippingImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initClip();
    }

    public ClippingImageView(Context context) {
        super(context);
        initClip();
    }

    private void initClip() {
        // post to message queue, so it gets run after measuring & layout
        // sets initial crop area to half of the view's width & height
        post(new Runnable() {
            @Override public void run() {
                setImageCrop(0.5f);
            }
        });
    }

    @Override protected void onDraw(Canvas canvas) {
        // clip if needed and let super take care of the drawing
        if (clip()) canvas.clipRect(mClipRect);
        super.onDraw(canvas);
    }

    private boolean clip() {
        // true if clip bounds have been set aren't equal to the view's bounds
        return !mClipRect.isEmpty() && !clipEqualsBounds();
    }

    private boolean clipEqualsBounds() {
        final int width = getWidth();
        final int height = getHeight();
        // whether the clip bounds are identical to this view's bounds (which effectively means no clip)
        return mClipRect.width() == width && mClipRect.height() == height;
    }

    public void toggle() {
        // toggle between [0...0.5] and [0.5...0]
        final float[] values = clipEqualsBounds() ? new float[] { 0f, 0.5f } : new float[] { 0.5f, 0f };
        ObjectAnimator.ofFloat(this, "imageCrop", values).start();
    }

    public void setImageCrop(float value) {
        // nothing to do if there's no drawable set
        final Drawable drawable = getDrawable();
        if (drawable == null) return;

        // nothing to do if no dimensions are known yet
        final int width = getWidth();
        final int height = getHeight();
        if (width <= 0 || height <= 0) return;

        // construct the clip bounds based on the supplied 'value' (which is assumed to be within the range [0...1])
        final int clipWidth = (int) (value * width);
        final int clipHeight = (int) (value * height);
        final int left = clipWidth / 2;
        final int top = clipHeight / 2;
        final int right = width - left;
        final int bottom = height - top;

        // set clipping bounds
        mClipRect.set(left, top, right, bottom);
        // schedule a draw pass for the new clipping bounds to take effect visually
        invalidate();
    }

}

The real 'magic' is the added line to the overridden onDraw() method, where the given Canvas is clipped to a rectangular area defined by mClipRect. All the other code and methods are mainly there to help out with calculating the clip bounds, determining whether clipping is sensible, and the animation.

Using it from an Activity is now reduced to the following:

public class MainActivity extends Activity {

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.image_activity);

        final ClippingImageView img = (ClippingImageView) findViewById(R.id.img);
        img.setOnClickListener(new OnClickListener() {
            @Override public void onClick(View v) {
                img.toggle();
            }
        });
    }
}
ClippingImageView
<mh.so.img.ClippingImageView
        android:id="@+id/img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/dog" />

To give you an idea of the visual transition:

android - How to animate a matrix to "crop-out" an image? - Stack Over...

android graphics matrix transformation objectanimator
Rectangle 27 6

I realise you (and the other answers) have been trying to solve this problem using matrix manipulation, but I'd like to propose a different approach with the same visual effect as outlined in your question.

In stead of using a matrix to manipulate to visible area of the image(view), why not define this visible area in terms of clipping? This is a rather straightforward problem to solve: all we need to do is define the visible rectangle and simply disregard any content that falls outside of its bounds. If we then animate these bounds, the visual effect is as if the crop bounds scale up and down.

Luckily, a Canvas supports clipping through a variety of clip*() methods to help us out here. Animating the clipping bounds is easy and can be done in a similar fashion as in your own code snippet.

If you throw everything together in a simple extension of a regular ImageView (for the sake of encapsulation), you would get something that looks this:

public class ClippingImageView extends ImageView {

    private final Rect mClipRect = new Rect();

    public ClippingImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initClip();
    }

    public ClippingImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initClip();
    }

    public ClippingImageView(Context context) {
        super(context);
        initClip();
    }

    private void initClip() {
        // post to message queue, so it gets run after measuring & layout
        // sets initial crop area to half of the view's width & height
        post(new Runnable() {
            @Override public void run() {
                setImageCrop(0.5f);
            }
        });
    }

    @Override protected void onDraw(Canvas canvas) {
        // clip if needed and let super take care of the drawing
        if (clip()) canvas.clipRect(mClipRect);
        super.onDraw(canvas);
    }

    private boolean clip() {
        // true if clip bounds have been set aren't equal to the view's bounds
        return !mClipRect.isEmpty() && !clipEqualsBounds();
    }

    private boolean clipEqualsBounds() {
        final int width = getWidth();
        final int height = getHeight();
        // whether the clip bounds are identical to this view's bounds (which effectively means no clip)
        return mClipRect.width() == width && mClipRect.height() == height;
    }

    public void toggle() {
        // toggle between [0...0.5] and [0.5...0]
        final float[] values = clipEqualsBounds() ? new float[] { 0f, 0.5f } : new float[] { 0.5f, 0f };
        ObjectAnimator.ofFloat(this, "imageCrop", values).start();
    }

    public void setImageCrop(float value) {
        // nothing to do if there's no drawable set
        final Drawable drawable = getDrawable();
        if (drawable == null) return;

        // nothing to do if no dimensions are known yet
        final int width = getWidth();
        final int height = getHeight();
        if (width <= 0 || height <= 0) return;

        // construct the clip bounds based on the supplied 'value' (which is assumed to be within the range [0...1])
        final int clipWidth = (int) (value * width);
        final int clipHeight = (int) (value * height);
        final int left = clipWidth / 2;
        final int top = clipHeight / 2;
        final int right = width - left;
        final int bottom = height - top;

        // set clipping bounds
        mClipRect.set(left, top, right, bottom);
        // schedule a draw pass for the new clipping bounds to take effect visually
        invalidate();
    }

}

The real 'magic' is the added line to the overridden onDraw() method, where the given Canvas is clipped to a rectangular area defined by mClipRect. All the other code and methods are mainly there to help out with calculating the clip bounds, determining whether clipping is sensible, and the animation.

Using it from an Activity is now reduced to the following:

public class MainActivity extends Activity {

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.image_activity);

        final ClippingImageView img = (ClippingImageView) findViewById(R.id.img);
        img.setOnClickListener(new OnClickListener() {
            @Override public void onClick(View v) {
                img.toggle();
            }
        });
    }
}
ClippingImageView
<mh.so.img.ClippingImageView
        android:id="@+id/img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/dog" />

To give you an idea of the visual transition:

android - How to animate a matrix to "crop-out" an image? - Stack Over...

android graphics matrix transformation objectanimator
Rectangle 27 1

This looks like you want to plot the user's current geo-location on an image of, say a building or campus. Assuming this, my approach would be to 'map' the still image to the screen which is likely to require a translation transform, a rotation transform and a scaling transform. In addition, you will need to know the actual geo-location coordinates of at least two points on your image. Given the image in your previous post, I would assume you have the geo coordinates of the bottom left corner and the bottom right corner. You already have the information to convert a geo coordinate into a screen coordinate so the image can be drawn matching up the bottom left corner of your image with the pixel coordinate which you've calculated. I will call this point your anchor point.

At this stage you probably have an image with one corner at the correct location but now it needs to be scaled down or up and then rotated about your anchor point. You can get the current zoom level from your mapView or you can get the latitudeSpan and you can calculate the scale factor to be applied to your image.

Lastly, if you have the geo coordinates of the two corners of the image, you can calculate the angle the image should be rotated. This can be calculated using pythagoras or you can convert from Cartesian coordinates to polar coordinates see here. This calculation doesn't have to be done by your app - it can be calculated separately and put in as a constant. Now you can apply the rotation transform around your fixed anchor point.

You may also want to make use of handy built-in functions such as mapController.zoomInFixing() which takes pixel coordinates or one of the other zoomTo() or animateTo() functions.

Edit: If you're not using a mapview to manage your geo-coordinates then you can apply the image transformations using code like this:

// create a matrix for the manipulation
    Matrix matrix = new Matrix();
    // resize the bit map
    matrix.postScale(scaleWidth, scaleHeight);
    // rotate the Bitmap
    matrix.postRotate(angle);

// recreate the new Bitmap
    Bitmap resizedBitmap = Bitmap.createBitmap(bitmapOrg, 0, 0, 
                      width, height, matrix, true); 

    // make a Drawable from Bitmap to allow to set the BitMap 
    // to the ImageView, ImageButton or what ever
    BitmapDrawable bmd = new BitmapDrawable(resizedBitmap);

    ImageView imageView = new ImageView(this);

    // set the Drawable on the ImageView
    imageView.setImageDrawable(bmd);

Edit: With the upper left and lower right coordinates, you can calculate the angle as follows:

angle = sin-1((right.x - left.y)/sqrt((right.x - left.x)sq + (right.y - left.y)sq))

I am not using any MapController or MapView rather simple ImageSwitcher & ImageView. So I need to compute it manually.

any other idea? would you give samples?

If you have the geo location coordinates of two points of the image, you can calculate the angle.

(1) what is the two points? (2) how can I calculate the angle with two points of the image?

Any two points on your image - ideally, the geo-location coordinates of the bottom left corner and the bottom right corner. Then you can use Pythagoras to calculate the angle. If you have these two coordinates I'll calculate it for you.

android - How to: plot a given latitude and longitude into still image...

android map geolocation location coordinates
Rectangle 27 2

There are two ways of doing this: First find the display height and width and call this method

private void scaleImage(int displayWidth) {

    // Get the ImageView and its bitmap

             width=displayWidth;
    Drawable drawing = holder.imagepost.getDrawable();

    {
        Bitmap bitmap = ((BitmapDrawable) drawing).getBitmap();


        int bounding = dpToPx(width);

        // Determine how much to scale: the dimension requiring less
        // scaling is
        // closer to the its side. This way the image always stays
        // inside your
        // bounding box AND either x/y axis touches it.
        float xScale = ((float) bounding) / width;
        float yScale = ((float) bounding) / height;
        float scale = (xScale <= yScale) ? xScale : yScale;

        // Create a matrix for the scaling and add the scaling data
        Matrix matrix = new Matrix();
        matrix.postScale(scale, scale);

        // Create a new bitmap and convert it to a format understood by
        // the ImageView
        Bitmap scaledBitmap = Bitmap.createBitmap(bitmap, 0, 0, width,
                height, matrix, true);
        width = scaledBitmap.getWidth(); // re-use
        height = scaledBitmap.getHeight(); // re-use
        BitmapDrawable result = new BitmapDrawable(scaledBitmap);

        // Apply the scaled bitmap
        holder.imagepost.setImageDrawable(result);

        // Now change ImageView's dimensions to match the scaled image
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) holder.imagepost
                .getLayoutParams();
        params.width = width;
        params.height = height;
        holder.imagepost.setLayoutParams(params);

    }

}

private int dpToPx(int dp) {
    float density = context.getResources().getDisplayMetrics().density;
    return Math.round((float) dp * density);
}

Or the Best way i know is to use Android Query

Here is the link http://code.google.com/p/android-query/ and you can download from there itself.Below id the code to maintain the Aspect Ratio

aq.id(R.id.imageView)

                .image(imageString, true, true,
                        displaywidth, 0, null, AQuery.FADE_IN, AQuery.RATIO_PRESERVE);

android - How to maintain the aspect ratio of the image if imageview f...

android android-imageview aspect-ratio
Rectangle 27 2

There are two ways of doing this: First find the display height and width and call this method

private void scaleImage(int displayWidth) {

    // Get the ImageView and its bitmap

             width=displayWidth;
    Drawable drawing = holder.imagepost.getDrawable();

    {
        Bitmap bitmap = ((BitmapDrawable) drawing).getBitmap();


        int bounding = dpToPx(width);

        // Determine how much to scale: the dimension requiring less
        // scaling is
        // closer to the its side. This way the image always stays
        // inside your
        // bounding box AND either x/y axis touches it.
        float xScale = ((float) bounding) / width;
        float yScale = ((float) bounding) / height;
        float scale = (xScale <= yScale) ? xScale : yScale;

        // Create a matrix for the scaling and add the scaling data
        Matrix matrix = new Matrix();
        matrix.postScale(scale, scale);

        // Create a new bitmap and convert it to a format understood by
        // the ImageView
        Bitmap scaledBitmap = Bitmap.createBitmap(bitmap, 0, 0, width,
                height, matrix, true);
        width = scaledBitmap.getWidth(); // re-use
        height = scaledBitmap.getHeight(); // re-use
        BitmapDrawable result = new BitmapDrawable(scaledBitmap);

        // Apply the scaled bitmap
        holder.imagepost.setImageDrawable(result);

        // Now change ImageView's dimensions to match the scaled image
        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) holder.imagepost
                .getLayoutParams();
        params.width = width;
        params.height = height;
        holder.imagepost.setLayoutParams(params);

    }

}

private int dpToPx(int dp) {
    float density = context.getResources().getDisplayMetrics().density;
    return Math.round((float) dp * density);
}

Or the Best way i know is to use Android Query

Here is the link http://code.google.com/p/android-query/ and you can download from there itself.Below id the code to maintain the Aspect Ratio

aq.id(R.id.imageView)

                .image(imageString, true, true,
                        displaywidth, 0, null, AQuery.FADE_IN, AQuery.RATIO_PRESERVE);

android - How to maintain the aspect ratio of the image if imageview f...

android android-imageview aspect-ratio
Rectangle 27 1

private Bitmap decodeFile(String filePath) {

    // Decode image size
    try {

        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;

        BitmapFactory.decodeFile(filePath, o);
        final int REQUIRED_SIZE = 1024;
        int width_tmp = o.outWidth, height_tmp = o.outHeight;
        int scale = 1;
        while (true) {
            if (width_tmp < REQUIRED_SIZE && height_tmp < REQUIRED_SIZE)
                break;
            width_tmp /= 2;
            height_tmp /= 2;
            scale *= 2;
        }

        o.inJustDecodeBounds = false;


        // Decode with inSampleSize

        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        bitmap = BitmapFactory.decodeFile(filePath, o2);

    }
    catch (Exception e)
    {
        Log.e(e.getClass().getName(), e.getMessage(), e);
    }
   return bitmap;
}
bitmap=  decodeFile(picturePath);

             rotateCam();
public void rotateCam(){


Matrix matrix = new Matrix();
matrix.postRotate(getImageOrientation(picturePath));
Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
        bitmap.getHeight(), matrix, true);
imageView = (ImageView) findViewById(R.id.imgView);
imageView.setImageBitmap(rotatedBitmap);

 }

Null Pointer Exception on Bitmap.createBitmap in Android - Stack Overf...

android bitmap android-imageview android-orientation
Rectangle 27 0

Here's what the docs have to say:

Set this to true if you want the ImageView to adjust its bounds to preserve the aspect ratio of its drawable.

android:adjustViewBounds="true"
imageView.setAdjustViewBounds(true)

But it doesn't work using scaleType fitStart. At least in this case: After scaling down there's empty vertical space at the bottom. This space is not adjusted when I set adjustViewBounds="true"

matrix - Resizing IMAGEVIEW on image scale android - Stack Overflow

android matrix resize imageview resize-image
Rectangle 27 0

Set "Adjust View Bounds" true, and then set the scale type to "fitXY" finally adjut layout_width and height. You will see that the image resizes.

matrix - Resizing IMAGEVIEW on image scale android - Stack Overflow

android matrix resize imageview resize-image
Rectangle 27 0

package com.example.model;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.widget.ImageView;

public class ImageLoader {

    MemoryCache memoryCache=new MemoryCache();
    FileCache fileCache;
    private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>());
    ExecutorService executorService; 

    public ImageLoader(Context context){
        fileCache=new FileCache(context);
        executorService=Executors.newFixedThreadPool(5);
    }

    final int stub_id=R.drawable.load;
    public void DisplayImage(String url, ImageView imageView)
    {
        imageViews.put(imageView, url);
        Bitmap bitmap=memoryCache.get(url);
        if(bitmap!=null)
            imageView.setImageBitmap(bitmap);
        else
        {
            queuePhoto(url, imageView);
            imageView.setImageResource(stub_id);
        }
    }

    private void queuePhoto(String url, ImageView imageView)
    {
        PhotoToLoad p=new PhotoToLoad(url, imageView);
        executorService.submit(new PhotosLoader(p));
    }

    private Bitmap getBitmap(String url) 
    {
        File f=fileCache.getFile(url);

        //from SD cache
        Bitmap b = decodeFile(f);
        if(b!=null)
            return b;

        //from web
        try {
            Bitmap bitmap=null;
            URL imageUrl = new URL(url);
           // System.out.println("url :"+imageUrl);
            HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
            conn.setConnectTimeout(30000);
            conn.setReadTimeout(30000);
            conn.setInstanceFollowRedirects(true);

            InputStream is=conn.getInputStream();
            OutputStream os = new FileOutputStream(f);
            Utils.CopyStream(is, os);

            os.close();
            bitmap = decodeFile(f);


            return bitmap;
        } catch (Throwable ex){
           ex.printStackTrace();
           if(ex instanceof OutOfMemoryError)
               memoryCache.clear();
           return null;
        }
    }

    //decodes image and scales it to reduce memory consumption
    private Bitmap decodeFile(File f){
        try {
            //decode image size
            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;
            o.inPurgeable = true; // Tell to garbage collector that whether it needs free memory, the Bitmap can be cleared
            o.inTempStorage = new byte[32 * 1024];
            BitmapFactory.decodeStream(new FileInputStream(f),null,o);

            //Find the correct scale value. It should be the power of 2.
            final int REQUIRED_SIZE=70;
            int width_tmp=o.outWidth, height_tmp=o.outHeight;
            int scale=1;
            while(true){
                if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
                    break;
                width_tmp/=2;
                height_tmp/=2;
                long heapSize = Runtime.getRuntime().maxMemory();

               long heapsize1=(heapSize/(1024*1024));
               if(heapsize1>95)
               {
                  scale*=1;
                 // System.out.println("scale1 :");
               }else if(heapsize1>63 && heapsize1<=95){
                  scale*=2;
                // System.out.println("scale2 :");
               }else if(heapsize1>31 && heapsize1<=63){
                   scale*=2;
                 // System.out.println("scale22 :");
               }else if(heapsize1>0 && heapsize1<=31){
                      scale*=2;
                    // System.out.println("scale23 :");
                   }
               /*else if(heapsize1>31 && heapsize1<=63){
                  scale*=2;
                // System.out.println("scale2 :");
               }else if(heapsize1>0 && heapsize1<=31){
                  scale*=2;
                    // System.out.println("scale2 :");
                   }*/

            }

            //decode with inSampleSize
            BitmapFactory.Options o2 = new BitmapFactory.Options();
            o2.inPurgeable = true; // Tell to garbage collector that whether it needs free memory, the Bitmap can be cleared
            o2.inTempStorage = new byte[32 * 1024];
            o2.inSampleSize=scale;
            Bitmap bitmap1=BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
          //  System.out.println("width : "+bitmap1.getWidth()+ " height : "+bitmap1.getHeight());
       /*     if(bitmap1.getHeight()>=bitmap1.getWidth())
            {

                bitmap1 = Bitmap.createScaledBitmap(bitmap1, bitmap1.getHeight(),bitmap1.getWidth(), true);
            }else{
                //bmp = Bitmap.createScaledBitmap(bmp, (int) height2,width, true);
                Matrix matrix = new Matrix();

                matrix.postRotate(270);
                bitmap1 = Bitmap.createBitmap(bitmap1 , 0, 0, bitmap1 .getWidth(), bitmap1 .getHeight(), matrix, true);

            }*/
            return bitmap1;
        } catch (FileNotFoundException e) {}
        return null;
    }

    //Task for the queue
    private class PhotoToLoad
    {
        public String url;
        public ImageView imageView;
        public PhotoToLoad(String u, ImageView i){
            url=u; 
            imageView=i;
        }
    }

    class PhotosLoader implements Runnable {
        PhotoToLoad photoToLoad;
        PhotosLoader(PhotoToLoad photoToLoad){
            this.photoToLoad=photoToLoad;
        }

        @Override
        public void run() {
            if(imageViewReused(photoToLoad))
                return;
            Bitmap bmp=getBitmap(photoToLoad.url);
            memoryCache.put(photoToLoad.url, bmp);
            if(imageViewReused(photoToLoad))
                return;
            BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad);
            Activity a=(Activity)photoToLoad.imageView.getContext();
            a.runOnUiThread(bd);
        }
    }

    boolean imageViewReused(PhotoToLoad photoToLoad){
        String tag=imageViews.get(photoToLoad.imageView);
        if(tag==null || !tag.equals(photoToLoad.url))
            return true;
        return false;
    }

    //Used to display bitmap in the UI thread
    class BitmapDisplayer implements Runnable
    {
        Bitmap bitmap;
        PhotoToLoad photoToLoad;
        public BitmapDisplayer(Bitmap b, PhotoToLoad p){
            bitmap=b;photoToLoad=p;
            }
        public void run()
        {
            if(imageViewReused(photoToLoad))
                return;
            if(bitmap!=null)
                photoToLoad.imageView.setImageBitmap(bitmap);
            else
                photoToLoad.imageView.setImageResource(stub_id);
        }
    }

    public void clearCache() {
        memoryCache.clear();
        fileCache.clear();
    }

}

android - OutOfMemoryError although vm has enough free memory - Stack ...

android android-memory
Rectangle 27 0

getX and getY will return the touch location in the ImageView's coordinate system. If you're looking for the point within the image's coordinate system, you can use the inverse matrix of the matrix used by the ImageView. I've done something like the following:

// calculate inverse matrix
Matrix inverse = new Matrix();
imageView.getImageMatrix().invert(inverse);

// map touch point from ImageView to image
float[] touchPoint = new float[] {event.getX(), event.getY()};
inverse.mapPoints(touchPoint);
// touchPoint now contains x and y in image's coordinate system

Android: how to detect touch location on ImageView if the image view i...

android matrix touch imageview scale
Rectangle 27 0

Zorgbargle answer is right but there is another consideration when your loading images from resource folder and that's density of the device.

Android scale images base on the device density so you if you only have the image in mdpi folder, you must also divide the points by the density to find the real point on the image:

float[] point = new float[] {event.getX(), event.getY()};

Matrix inverse = new Matrix();
imageView.getImageMatrix().invert(inverse);
inverse.mapPoints(point);

float density = getResources().getDisplayMetrics().density;
point[0] /= density;
point[1] /= density;

Android: how to detect touch location on ImageView if the image view i...

android matrix touch imageview scale
Rectangle 27 0

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.ImageView;

/**
 * Extends Android ImageView to include pinch zooming and panning.
 */
public class TouchImageView extends ImageView
{
    Matrix matrix = new Matrix();

    // We can be in one of these 3 states
    static final int NONE = 0;
    static final int DRAG = 1;
    static final int ZOOM = 2;
    int mode = NONE;

    // Remember some things for zooming
    PointF last = new PointF();
    PointF start = new PointF();
    float minScale = 1f;
    float maxScale = 3f;
    float[] m;

    float redundantXSpace, redundantYSpace;

    float width, height;
    static final int CLICK = 3;
    float saveScale = 1f;
    float right, bottom, origWidth, origHeight, bmWidth, bmHeight;

    ScaleGestureDetector mScaleDetector;

    Context context;

    public TouchImageView(Context context)
    {
        super(context);
        sharedConstructing(context);
    }

    public TouchImageView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        sharedConstructing(context);
    }

    private void sharedConstructing(Context context)
    {
        super.setClickable(true);
        this.context = context;
        mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
        matrix.setTranslate(1f, 1f);
        m = new float[9];
        setImageMatrix(matrix);
        setScaleType(ScaleType.MATRIX);

        setOnTouchListener(new OnTouchListener()
        {
            @Override
            public boolean onTouch(View v, MotionEvent event)
            {
                mScaleDetector.onTouchEvent(event);

                matrix.getValues(m);
                float x = m[Matrix.MTRANS_X];
                float y = m[Matrix.MTRANS_Y];
                PointF curr = new PointF(event.getX(), event.getY());

                switch (event.getAction())
                {
                    case MotionEvent.ACTION_DOWN:
                        last.set(event.getX(), event.getY());
                        start.set(last);
                        mode = DRAG;
                        break;
                    case MotionEvent.ACTION_MOVE:
                        if (mode == DRAG)
                        {
                            float deltaX = curr.x - last.x;
                            float deltaY = curr.y - last.y;
                            float scaleWidth = Math.round(origWidth * saveScale);
                            float scaleHeight = Math.round(origHeight * saveScale);
                            if (scaleWidth < width)
                            {
                                deltaX = 0;
                                if (y + deltaY > 0)
                                    deltaY = -y;
                                else if (y + deltaY < -bottom)
                                    deltaY = -(y + bottom);
                            }
                            else if (scaleHeight < height)
                            {
                                deltaY = 0;
                                if (x + deltaX > 0)
                                    deltaX = -x;
                                else if (x + deltaX < -right)
                                    deltaX = -(x + right);
                            }
                            else
                            {
                                if (x + deltaX > 0)
                                    deltaX = -x;
                                else if (x + deltaX < -right)
                                    deltaX = -(x + right);

                                if (y + deltaY > 0)
                                    deltaY = -y;
                                else if (y + deltaY < -bottom)
                                    deltaY = -(y + bottom);
                            }
                            matrix.postTranslate(deltaX, deltaY);
                            last.set(curr.x, curr.y);
                        }
                        break;

                    case MotionEvent.ACTION_UP:
                        mode = NONE;
                        int xDiff = (int) Math.abs(curr.x - start.x);
                        int yDiff = (int) Math.abs(curr.y - start.y);
                        if (xDiff < CLICK && yDiff < CLICK)
                            performClick();
                        break;

                    case MotionEvent.ACTION_POINTER_UP:
                        mode = NONE;
                        break;
                }
                setImageMatrix(matrix);
                invalidate();
                return true; // indicate event was handled
            }
        });
    }

    @Override
    public void setImageBitmap(Bitmap bm)
    {
        super.setImageBitmap(bm);
        if (bm != null)
        {
            bmWidth = bm.getWidth();
            bmHeight = bm.getHeight();
        }
    }

    public void setMaxZoom(float x)
    {
        maxScale = x;
    }

    private class ScaleListener extends
            ScaleGestureDetector.SimpleOnScaleGestureListener
    {
        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector)
        {
            mode = ZOOM;
            return true;
        }

        @Override
        public boolean onScale(ScaleGestureDetector detector)
        {
            float mScaleFactor = (float) Math.min(
                    Math.max(.95f, detector.getScaleFactor()), 1.05);
            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;
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = MeasureSpec.getSize(widthMeasureSpec);
        height = MeasureSpec.getSize(heightMeasureSpec);
        // 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;

        // 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);
    }
}

I tried this code but no matter what image src i choose it always shows up blank...

@Peter: If I understood correct you have blank space above your ImageView so you can add android:adjustViewBounds="true" to your ImageView in your xml layout.

Actually the entire imageview is blank, nothing shows up

Try my project on GitHub. It uses this class to implement pinch to zoom functionality in gallery. Image source - assets. But you can also implement other sources: SD card, drawable folder, etc.

I'll give that a try. Right now im using a source thats in my drawable folder.

Android Pinch and Zoom Image in Activity - Stack Overflow

android android-listview android-imageview pinchzoom