Update: The multiple device screen dimensions is a red herring - the problem is just that the image does not scale up properly to fill the screen - see comments on Ivan's answer.
I have a layout file with one image:
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/image"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="centerCrop" />
Then I assign a drawable, which is small and must be scaled up:
setContentView(R.layout.image_story);
ImageView image = (ImageView)findViewById(R.id.image);
image.setImageDrawable(s.image);
Here is how it is rendered on two AVDs with different screen dimensions. These should be the same (detailed question at the bottom). Sorry for the large images.
with scaleType=centerCrop:
with centerInside
AVDs:
Edit:
With layout_height="fill_parent" and scaleType="centerInside"
I have a 2.1 AVD with the default values, so the screen is smaller, and this works exactly as expected- the image is scaled up to fill the width, and the view height is wrapped to the scaled height.
On my Droid Bionic with a longer screen, and on any AVD with the same screen dimensions, this doesn't work- the image is scaled to fill the width, but the view is wrapped to the original pre-scale image height, so the top and bottom are cropped off.
I have no idea why the device screen aspect ratio would have an effect on this. I've tried countless combination of layout parameters and scale types trying to get this to work on the Bionic. Everything works exactly as expected on the smaller screen, and not on the larger. If I set the image height explicitly in dp
, it works as expected, but I never know what the dimensions of the image (or screen) will be. Any suggestions?
That is a very good question indeed.
Here's the reason it's behaving like that (from ImageView.onMeasure(int, int) [line 661]):
// Try adjusting height to be proportional to width
if (!done && resizeHeight) {
int newHeight = (int)((widthSize - pleft - pright)
/ desiredAspect) + ptop + pbottom;
if (newHeight <= heightSize) { // line 661
heightSize = newHeight;
} // line 663
}
What it does is it adjusts the height of the view only if the new height that is based on aspect ratio of the drawable and the adjusted width (in our case, that's exact width of the parent view) is smaller than the adjusted height (which at this poin is just drawable's native height plus padding in our case. Let me know if you want me to brake down this point further.)
What I don't get is why is there the restriction that new height must be smaller. It makes sense only if our heightSize is either EXACTLY or AT_MOST and has been set to the upper bound. In other cases it's not necessary for it to be so.
So actually instead of the whole piece 661 through 663 there should have been another call to
heightSize = resolveAdjustedSize(newHeight, mMaxHeight, heightSpec);
to make sure we only use the height restriction when it should be restricted (ie. we got AT_MOST restriction in heightSpec and the height value in heightSpec is smaller than the new height. EXACTLY can't happen here unless we use variable width.)
Maybe, I missed something there. Guys, whoever is reading this, please comment if you see any flaws in that, especially if you're part of Android team at Google :)
PS As a workaround, I can suggest you implement a custom ImageView and override onMeasure(int, int) to set the bounds to your exact aspect ratio. Let me know if you need help on the actual code.
UPD I'm going to write out names to catch attention of smart android guys at Google (I'm hoping the guys have Google Alerts set up): Romain Guy, Roman Nurik, Reto Meier, please, take a look at this discussion.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With