Android

The truth about gravity

By June 24, 2011August 8th, 2011No Comments

I ran into a bit of confusion this week while setting up a layer list for the background of our dashboard activity.

(A layer list is just a list of drawable items that will get drawn on top of each other, one after the other, much like layers in Photoshop.)

One of the items in my layer list was a bitmap. Bitmaps have an android:gravity attribute that determines how they are positioned and scaled, and this is where the confusion set in. I was testing a bitmap with and without the ‘clip_horizontal’ and ‘clip_vertical’ flags…but the results were the opposite of what I expected.

This is how the android documentation describes the gravity attribute:

The gravity indicates where to position the drawable in its container if the bitmap is smaller than the container.

This is what the android documentation says about clip_vertical:

Additional option that can be set to have the top and/or bottom edges of the child clipped to its container’s bounds. The clip will be based on the vertical gravity: a top gravity will clip the bottom edge, a bottom gravity will clip the top edge, and neither will clip both edges.

I was testing with an image that was much larger than the screen, so when I turned on clip_horizontal, I expected the image to be clipped at the right edge…but instead, the entire image was squished to fit within the screen. Hmm.

If I turned clip_horizontal off, the image was essentially “cropped” by the screen…which is what I expected to happen when clip_horizontal was on.

This seemed really strange to me — clip_horizontal seemed backwards. It had exactly the same result as turning on fill_horizontal. What gives?

The same was true for clip_vertical. I thought that setting android:gravity=”top|left|clip_vertical” on a bitmap would cause the image’s top left corner to be positioned at the view’s top left corner, and that, if the bitmap was taller than the view, it would be “clipped” at the bottom edge of the view. In other words, show only as much of the bitmap that the view is tall enough to reveal; do not stretch the bitmap, but instead only show whatever will fit, letting the rest extend below the bottom edge.

However, the opposite happened: when I set clip_vertical, my large bitmap was squished vertically to fit within the height of the view.

My first response was to check the android bug tracker (maybe it’s a known issue!) but there was nothing there; if this was a bug, surely someone else would have encountered it. Still, it wouldn’t be the first time. I decided to check the android source code to see if I could find any clues in there.

After examining the applyDisplay() method in platform/frameworks/core/java/android/view/Gravity.java, I realized the source of my confusion:

It wasn’t the background image in my bitmap view that was going to be clipped, but rather the view itself — the container the image is ultimately rendered into.

Setting clip_horizontal in my case didn’t mean “cut off the image at the right edge,” it meant “constrain the size of the BitmapDrawable view so its width matches the width of its parent container”…which then caused the image to be “squished” to fit that narrower space.

So, the important thing to remember with android:gravity is that clip_horizontal and clip_vertical apply to the measurements of the container itself, before the contents (such as my background image) are drawn. In other words, where the documention mentions “the child” and “the drawable” they don’t mean “the image” but “the view the background image is rendered into.”