The problem of proportional scaling
Milestone 1 of my journey into Android software development is creating a rudimentary GUI for a MP3 player app. On the way towards it I soon ran into a first problem, naturally. The cover image (in the mockup represented by compact disc) would not display as intended. It was supposed to fill the screen width, scaling proportionally in height (assuming square dimensions of the cover and portrait orientation of the phone). And though theBitmapDrawable
scaled (down) correctly, the enclosing ImageView
would never wrap it up in height. In fact, it would be so large that it even pushed the SeekBar
out of screen:Dream (left) and reality (right): the ImageView does not adjust its height to the picture of the compact disc. Its actual dimensions are marked by the blue border! |
RelativeLayout
(not shown) and is aligned below the song title (line 5).<ImageView android:id="@+id/cover" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/songtitle" android:src="@drawable/cd" />
I assumed that setting
layout_width
to to «match_parent
» would scale the image to fit in width, which it seemed to do. Furthermore I assumed that setting layout_height
to «wrap_content
» would shrink the view closely wrap the image, i.e. would result in a ImageView
of square proportion. It did not, obviously.I am not going to retrace in detail how I went trial-and-error through the numerous properties of the
Drawable
and ImageView
classes. Lets approach it more systematically, because there is more than a single factor in effect here. The source code for this project is available at GitHub. I will created tags for each point discussed in this blog.For this experiment, I created three square images of varying dimensions: one too small for a WVGA800 screen (which is 480×800px in the case of the Galaxy S2), it is 100 pixel wide and tall; another of exact fit (480px wide); and a third images too large for the screen (800px wide). The vanilla layout looks like this for these three image sizes:
Android prevents clipping of over-sized images and rather scales them down. The blue border marks the bounds of the ImageView. |
ImageView
behaves as expected (by me, at least). For both the exact fit and the over-sized picture the ImageView
does not wrap its content. Android also scales down the over-sized picture to fit the screen, which is a sensible precaution. The small image however is not enlarged automatically, probably because it a) might look terrible, b) does no harm to leave it as is.Now lets play with the properties of the user interface components.
BitmapDrawable
gravity (and tiling)
Poking around the API documentation, I first stumbled upon the gravity attribute of the BitmapDrawable
class. It looked promising, offering options for pushing a picture toward any edge of the container, or scaling it, or clipping it. Surprisingly, none of the options had any effect on the scaling and placement of the sample images from above. For both of the larger images the API documentation might explain:The gravity indicates where to position the drawable in its container if the bitmap is smaller than the container.But why then does it not work for the small image either? It turned out, that the phrase «in its container» misled me. The source code of
BitmapDrawable.draw(Canvas)
reveals that gravity only applies within the bounds of the BitmapDrawable
itself. The ImageView
sets these bounds via Drawable.setBounds(int,int,int,int)
to the exact dimension of the underlying bitmap image. There is only one case in which the bounds actually matches the proportion of the ImageView and this is when ImageView.scaleType==FIT_XY
(«fitXY» in a XML-layout) in the private method ImageView.configureBounds()
.The
scaleType
of class ImageView
defaults to «FIT_CENTER
». To make use of gravity, it must be set to «fitXY
». Furthermore the src
-property of the ImageView
must refer to a bitmap XML-definition and not to an image file (res/layout/toosmall.xml):<ImageView android:id="@+id/cover" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/songtitle" android:scaleType="fitXY" android:src="@drawable/toosmall" />
BitmapDrawable
can be configured with something like this (res/drawable/toosmall.xml):<?xml version="1.0" encoding="utf-8"?> <bitmap xmlns:android="http://schemas.android.com/apk/res/android" android:src="@drawable/square100" android:gravity="fill_vertical" />
Drawable
are actually larger than the physical dimension of the picture displayed. Apart from ImageView
s with ScaleType.FIT_XY
this is the case for View
backgrounds. Best use an empty, fully expanded LinearLayout
for experiment, as I did for the following screenshot.As background image, a DrawableBitmap receives enough room for gravity and tiling to have an effect. From left to right: gravity="right|center_vertical", gravity="fill_vertical", tileMode="mirror". |
ImageView scaleType
and adjustViewBounds
The scaleType
has a major say on the placement of a Drawable
inside an ImageView
(read the Andre Steingress' blog posting on its various effects). But disabling scaling via «fitXY
» in order to make gravity take effect will not solve the problem of the view area being larger than the contained bitmap! No combination of gravity flags will scale the bitmap proportionally! So gravity was a red herring after all. It is the property «adjustViewBounds»
in combination with the default scaleType
that will do the job.[Update 2012-05-25]: When
adjustViewBounds
is true, the scaleType
no longer matters: ImageView
will reset it to FIT_CENTER
(the default) anyway.The adjustViewBounds property will shrink the ImageView to fit its content. it has unwanted side-effects, though. |
If someone knows a way to archive it by pure XML, I would be grateful to hear about it!
Conclusion
There seems to be no way to proportionally scale anImageView
to match its parents width by means of a XML-layout definition alone. Bitmaps which are larger than the available display space will be reduced in size correctly but smaller images never enlarge proportionally. The most obvious solution would be to provide fitting or over-sized bitmap images for each targeted device. But this is neither future-proof nor always possible—think of the cover images which usually come in just one size, attached to a MP3-file. But then again, dynamically loading images always requires some programming anyway. So a few extra lines of code might not matter too much.
"But then again, dynamically loading images always requires some programming anyway. So a few extra lines of code might not matter too much."
ReplyDeleteNot valid in case you are creating a home screen widget, which uses RemoteView. You don't have any option other than using only XML layout.
It's interesting that many of the bloggers to helped clarify a few things for me as well as giving.Most of ideas can be nice content.The people to give them a good shake to get your point and across the command
ReplyDeleterpa training in bangalore
best rpa training in bangalore
RPA training in bangalore
rpa courses in bangalore
Good Post! Thank you so much for sharing this pretty post, it was so good to read and useful to improve my knowledge as updated one, keep blogging.
ReplyDeleteBest Devops Training in pune
Microsoft azure training in Bangalore
Power bi training in Chennai
Best Devops Training in pune
Hi, Great.. Tutorial is just awesome..It is really helpful for a newbie like me.. I am a regular follower of your blog. Really very informative post you shared here. Kindly keep blogging.
ReplyDeletepython training in chennai
python course in chennai
python training in bangalore
Awesome article. It is so detailed and well formatted that i enjoyed reading it as well as get some new information too.
ReplyDeleteAWS Training in pune
AWS Online Training
AWS Training in Bangalore
My spouse and I love your blog and find almost all of your post’s to be just what I’m looking for. Can you offer guest writers to write content for you? I wouldn’t mind producing a post or elaborating on some the subjects you write concerning here. Again, awesome weblog!
ReplyDeletepython training in rajajinagar
Python training in bangalore
Python Online training in usa
My partner and I stumbled over here different website and thought I might as well check things out. I like what I see so now I’m following you. Look forward to checking out your web page repeatedly.
ReplyDeletepython training in rajajinagar
Python training in bangalore
Python Online training in usa
Thanks For sharing Your information The information shared Is Very Valuable Please Keep Updating Us Time Just went On reading Thae article Python Online Training DataScience Online Training AWS Online Training Hadoop Online Training
ReplyDeleteSuch a great information for blogger iam a professional blogger thanks…
ReplyDeleteLooking for Software Training in Bangalore , learn from Softgen Infotech Software Courses on online training and classroom training. Join today!
perde modelleri
ReplyDeleteMobil Onay
mobil ödeme bozdurma
nft nasıl alınır
ankara evden eve nakliyat
Trafik sigortası
Dedektör
Web site kurmak
ask kitaplari
kadıköy toshiba klima servisi
ReplyDeleteçekmeköy lg klima servisi
kadıköy beko klima servisi
ümraniye alarko carrier klima servisi
kartal daikin klima servisi
ümraniye daikin klima servisi
üsküdar toshiba klima servisi
tuzla beko klima servisi
çekmeköy bosch klima servisi