Proportional image resizing is a fairly common scenario while developing an Android app: there are a number of situations where you might want an image to stretch itself to horizontally fit the whole screen while keeping its original aspect ratio. You might think it should be easy: sadly, it's not.
The problem
Achieving this result in HTML would be extremely easy: we just need to set our image width to 100% and avoid to set any height, letting the browser doing the whole proportional resize job. Unfortunately, Android wears a different pair of shoes: the ImageView element containing your drawable resource (or downloaded file) requires a width and an height: we can give a fixed number in px, dp or other supported units, use the parent width with the match_parent command or use the actual image width using the wrap_content command: as we can see, proportional resize is not an option. We can achieve some decent results by setting android:layout_width, android:layout_height and android:scaleType to make them adapt to the container layout in the following way:
1 2 3 4 5 6 |
<ImageView android:id="@+id/myImage" android:layout_width="match_parent" android:layout_height="wrap_content" android:scaleType="fitCenter" /> |
These values could work in your specific scenario, but they tend to give inconsistant results among different devices and, most importantly, among different image sizes/ratios.
The solution
Why should we use a plain ImageView? We can start by taking a look to this interesting post on StackOverflow showing how to extend a View in order to achieve a very similar result: then we can use that knowledge to build our own ImageView extension class. Here's an example of a ProportionalImageView which automatically scales its height according to its computed width keeping its original aspect ratio intact:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
package com.my.namespace; import android.content.Context; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.widget.ImageView; public class ProportionalImageView extends ImageView { public ProportionalImageView(Context context) { super(context); } public ProportionalImageView(Context context, AttributeSet attrs) { super(context, attrs); } public ProportionalImageView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { Drawable d = getDrawable(); if (d != null) { int w = MeasureSpec.getSize(widthMeasureSpec); int h = w * d.getIntrinsicHeight() / d.getIntrinsicWidth(); setMeasuredDimension(w, h); } else super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } |
And this is how we can use it inside an XML layout:
1 2 3 4 5 6 7 8 9 |
<com.my.namespace.ProportionalImageView android:id="@+id/img" android:src="@drawable/myCustomImage" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:adjustViewBounds="true" /> |
Just remember to replace com.my.namespace with your app namespace and you should be fine.
Happy coding!
Thank you boss.
thnkx boss, it helped a lot
Hello; Thanx for this. it was helpful
but i have issue here, am having images in listview how can i reduce the size of the image(height) with out losing its ratio????
Thank you very much sir. I’m facing issues related to stretch background every time I insert a drawable inside a Linear Layout. With this solution I found out ImageView can be used as a background and your code on onMeasure did the trick maintaining proportion.
You are very welcome!
Thank you BOSS, wonderful
Hi, many thanks for the code. I have read thru also the stackoverflow thread. I have adjust the code so that the class know either to scale to height or width but it somehow get wierd result.
the 0.752 is my imageview dimension ratio.
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Drawable d = getDrawable();
if (d != null) {
if (((float)(d.getIntrinsicWidth() / d.getIntrinsicHeight()))<0.752){
int w = MeasureSpec.getSize(widthMeasureSpec);
int h = w * d.getIntrinsicHeight() / d.getIntrinsicWidth();
setMeasuredDimension(w, h);
} else {
int h = MeasureSpec.getSize(heightMeasureSpec);
int w = h * d.getIntrinsicWidth() / d.getIntrinsicHeight();
setMeasuredDimension(w, h);
}
}
else super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}