Muz
Muz

Reputation: 5980

Resizing ImageView to fit to aspect ratio

I need to resize an ImageView such that it fits to the screen, maintains the same aspect ratio. The following conditions hold:

For example, a tiny 50x50 image on a 400 px width screen would scale the image up to 400x400 px. A 800x200 image would scale to 400x100.

I've looked at most of the other threads, and many proposed solutions like simply changing adjustViewBounds and scaleType will only scale down an image, and not scale it up.

Upvotes: 15

Views: 40049

Answers (4)

samriddha
samriddha

Reputation: 35

Anybody reading this answer in 2023. Here is the kotlin version the "AspectRatioImageView" class -->

class AspectRatioImageViewKt : AppCompatImageView {

    constructor(context: Context) : super(context) {
        this.scaleType = ScaleType.FIT_XY
    }

    constructor(context: Context?, attrs: AttributeSet?) : super(
        context!!, attrs
    ) {
        this.scaleType = ScaleType.FIT_XY
    }

    constructor(context: Context?, attrs: AttributeSet?, defStyle: Int) : super(
        context!!, attrs, defStyle
    ) {
        this.scaleType = ScaleType.FIT_XY
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val d = drawable
        if (d != null && d.intrinsicHeight > 0) {
            var width = layoutParams.width
            var height = layoutParams.height

            if (height == ViewGroup.LayoutParams.WRAP_CONTENT || width in 1..9999) {
                if (width <= 0) width = MeasureSpec.getSize(widthMeasureSpec)
                if (width > 0) height = width * d.intrinsicHeight / d.intrinsicWidth

                if (height > maxHeight) {
                    height = maxHeight
                    width = height * d.intrinsicWidth / d.intrinsicHeight
                }
            } else if (width == ViewGroup.LayoutParams.WRAP_CONTENT || height in 1..9999) {
                if (height <= 0) height = MeasureSpec.getSize(heightMeasureSpec)
                if (height > 0) width = height * d.intrinsicWidth / d.intrinsicHeight
                if (width > maxWidth) {
                    width = maxWidth
                    height = width * d.intrinsicHeight / d.intrinsicWidth
                }
            }
            if (width > 0 && height > 0) setMeasuredDimension(width, height) else super.onMeasure(
                widthMeasureSpec,
                heightMeasureSpec
            )
        } else super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    }
}

Upvotes: 0

hamid toosi
hamid toosi

Reputation: 95

I prefer Agarwal's answer with some modifications, because it is resuable:

package com.yourpackage.widgets;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import android.view.ViewGroup;

public class AspectRatioImageView extends AppCompatImageView {

    public AspectRatioImageView(Context context) 
    {       
        super(context);
        this.setScaleType(ScaleType.FIT_XY);
    }

    public AspectRatioImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.setScaleType(ScaleType.FIT_XY);
    }

    public AspectRatioImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.setScaleType(ScaleType.FIT_XY);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
    {
        Drawable d = getDrawable();

        if (d != null && d.getIntrinsicHeight() > 0)
        {
            int width = getLayoutParams().width;
            int height = getLayoutParams().height;

            // تو این حالت مبنای محاسبات رو بر اساس طول در نظر می گیرم
            if (height == ViewGroup.LayoutParams.WRAP_CONTENT || (width > 0 &&  width < 10000))
            {
                if (width <= 0);
                width = MeasureSpec.getSize(widthMeasureSpec);

                if (width > 0)
                    height = width * d.getIntrinsicHeight() / d.getIntrinsicWidth();

                // ارتفاع نباید از بیشینه ارتفاع بیشتر باشه
                if (height > getMaxHeight()) {
                    height = getMaxHeight();
                    width = height * d.getIntrinsicWidth() / d.getIntrinsicHeight();
                }

            }
            else if (width == ViewGroup.LayoutParams.WRAP_CONTENT  || (height > 0 &&  height < 10000))
            {
                // اگر مقداری برای ارتفاع مشخص نشده بود ارتفاع پدر رو در نظر می گیریم
                if (height <= 0)
                    height = MeasureSpec.getSize(heightMeasureSpec);

                // اگه ارتفاع مشخص بود که می تونیم طول رو بر اساسش حساب کنیم
                if (height > 0)
                    width = height * d.getIntrinsicWidth() / d.getIntrinsicHeight();

                // طول نباید از بیشینه طول بیشتر باشه
                if (width > getMaxWidth()) {
                    width = getMaxWidth();
                    height = width * d.getIntrinsicHeight() / d.getIntrinsicWidth();
                }
            }

            // اگه محاسبات موفقیت آمیز بود
            if (width > 0 && height > 0)
                setMeasuredDimension(width, height);
                // در غیر اینصورت همه چی رو می سپریم به خود ایمیج ویو
            else
                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
        else
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

Now to use it in the XML:

<com.yourpackage.widgets.AspectRatioImageView 
android:src="@drawable/yourdrawable"
android:layout_height="wrap_content"
android:layout_width="match_parent"/>

Upvotes: 2

Akshat Agarwal
Akshat Agarwal

Reputation: 2847

I created it with the help of this Bob Lee's answer in this post: Android: How to stretch an image to the screen width while maintaining aspect ratio?

package com.yourpackage.widgets;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ImageView;

public class AspectRatioImageView extends ImageView {

    public AspectRatioImageView(Context context) {
        super(context);
    }

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = width * getDrawable().getIntrinsicHeight() / getDrawable().getIntrinsicWidth();
        setMeasuredDimension(width, height);
    }
}

Now to use it in the XML:

<com.yourpackage.widgets.AspectRatioImageView android:layout_centerHorizontal="true"
    android:src="@drawable/yourdrawable" android:id="@+id/image"
    android:layout_alignParentTop="true" android:layout_height="wrap_content"
    android:layout_width="match_parent" android:adjustViewBounds="true" />

Have fun!

Upvotes: 27

Muz
Muz

Reputation: 5980

ImageView mImageView; // This is the ImageView to change

// Use this in onWindowFocusChanged so that the ImageView is fully loaded, or the dimensions will end up 0.
public void onWindowFocusChanged(boolean hasFocus) 
{
    super.onWindowFocusChanged(hasFocus);

    // Abstracting out the process where you get the image from the internet
    Bitmap loadedImage = getImageFromInternet (url);

    // Gets the width you want it to be
    intendedWidth = mImageView.getWidth();

    // Gets the downloaded image dimensions
    int originalWidth = loadedImage.getWidth();
    int originalHeight = loadedImage.getHeight();

    // Calculates the new dimensions
    float scale = (float) intendedWidth / originalWidth;
    int newHeight = (int) Math.round(originalHeight * scale);

    // Resizes mImageView. Change "FrameLayout" to whatever layout mImageView is located in.
    mImageView.setLayoutParams(new FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.WRAP_CONTENT,
            FrameLayout.LayoutParams.WRAP_CONTENT));
    mImageView.getLayoutParams().width = intendedWidth;
    mImageView.getLayoutParams().height = newHeight;
}

Upvotes: 11

Related Questions