How to align ImageView to the bottom, fill its width and height, and keep its aspect ratio?

You need a combination of three attributes:

android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:adjustViewBounds="true"

The adjustViewBounds attribute is needed because ImageView measures itself to match the original dimensions of the drawable by default, without regard to any scaling performed according to the scale type.


You can use is custom FitWidthAtBottomImageView to achieve this code:

public class FitWidthAtBottomImageView extends ImageView {
    public FitWidthAtBottomImageView(Context context) {
        super(context);
    }

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

    public FitWidthAtBottomImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public FitWidthAtBottomImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        int i = getWidth() - getPaddingLeft() - getPaddingRight();
        int j = getHeight() - getPaddingTop() - getPaddingBottom();
        if (getBackground() != null) {
            getBackground().draw(canvas);
        }
        if (getDrawable() != null && getDrawable() instanceof BitmapDrawable) {
            Bitmap bitmap = ((BitmapDrawable) getDrawable()).getBitmap();
            int h = bitmap.getHeight() * i / bitmap.getWidth();
            canvas.drawBitmap(bitmap, null, new RectF(getPaddingLeft(), getPaddingTop() + j - h, getPaddingLeft() + i, getHeight() - getPaddingBottom()), null);
        } else {
            super.onDraw(canvas);
        }

    }
}

by manually draw the bottom aligned image that you want.


this is a proof-om-concept custom ImageView, you will need to add missing constructors and perform the Matrix setting whenever ImageView's Drawable changes:

class V extends ImageView {
    public V(Context context) {
        super(context);
        setScaleType(ScaleType.MATRIX);
    }
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        Drawable d = getDrawable();
        if (d != null) {
            Matrix matrix = new Matrix();
            RectF src = new RectF(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
            RectF dst = new RectF(0, 0, w, h);
            matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
            float[] points = {
                    0, d.getIntrinsicHeight()
            };
            matrix.mapPoints(points);
            matrix.postTranslate(0, h - points[1]);
            setImageMatrix(matrix);
        }
    }
}

how it works in short:

  • first the Matrix is set by calling Matrix.setRectToRect() with stf == Matrix.ScaleToFit.CENTER, the result is the same as ImageView.ScaleType.FIT_CENTER

  • then in order to align the Drawable to the bottom Matrix.mapPoints() is called, it maps the Drawable's left/bottom corner and the result is transformed point as would be seen in the ImageView

  • and finally Matrix.postTranslate() translates the MAtrix in Y axis to the ImageView's bottom