[XDA-University] Ticklefish:
*** android:layout_gravity, android:gravity AND android:layout_weight ***​

Centering the clock in the previous two posts wouldn't have been possible without using two particular attributes: 'android:gravity' and 'android:layout_weight'.

In physics, weight is the result of gravity acting on mass. But this isn't physics, it's Android, so what do these attributes actually mean?

*** GRAVITY ***​

In Android, the gravity of an object changes how it's laid out in the space it occupies.

Let's have a few practical examples to better explain..

Here's a blank status bar.

​

Let's add a simple clock to it.

<com.android.systemui.statusbar.policy.Clock
android:id="@id/clock"
android:background="#ff33b5e5"
android:textColor="#ffffffff"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
/>

I've missed a few attributes that you'd normally find in a real XML file, just to keep it simple. But I have added an attribute that sets the background colour to Holo Blue Light. You'll see how useful that is as I continue..

Here's my status bar now.

​

(DISCLAIMER: This isn't a real status bar. It's just something I put together in a picture-editing program on my PC. Doing these images with a real status bar would take forever...)

Let's add a gravity attribute to get this clock centered..

<com.android.systemui.statusbar.policy.Clock
android:id="@id/clock"
[COLOR="Red"] android:gravity="center"[/COLOR]
android:background="#ff33b5e5"
android:textColor="#ffffffff"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
/>

This gives us this..

​

That didn't work. The android:gravity attribute changes how an object is laid out, but only in its own space. The clock is now centered in that space, but not the statusbar.

So let's expand that space.

<com.android.systemui.statusbar.policy.Clock
android:id="@id/clock"
android:background="#ff33b5e5"
android:textColor="#ffffffff"
[COLOR="Red"] android:layout_width="fill_parent"[/COLOR]
android:layout_height="fill_parent"
/>

Which looks like:

​

Oops, forgot that gravity attribute!

<com.android.systemui.statusbar.policy.Clock
android:id="@id/clock"
[COLOR="Red"] android:gravity="center"[/COLOR]
android:background="#ff33b5e5"
android:textColor="#ffffffff"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>

Here's the result.

​

Better, no?

So what's the difference between android:gravity and android:layout_gravity?

Well, android:layout_gravity affects how an object is laid out, not in its own space, but in its parent Layout.

Let's try using that to center the clock:

<com.android.systemui.statusbar.policy.Clock
android:id="@id/clock"
[COLOR="Red"] android:layout_gravity="center"[/COLOR]
android:background="#ff33b5e5"
android:textColor="#ffffffff"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
/>

Here's the result:

​

So why didn't that work? Well, it really depends on that parent Layout.

In a LinearLayout, some of the layout_gravity attributes are ignored. This would have worked in a RelativeLayout but, as we saw in the Simple Method two posts ago, other objects can overlap the clock.

Generally when you're modding, you normally only worry about layout_gravity when using a RelativeLayout or FrameLayout.

For your reference, here's are the values you can set your gravity to. This is taken direct from Google themselves.

Most of those you'll rarely, if ever, use while modding. But it's handy to know about them!

*** WEIGHT ***​

Weight is a trickier concept to understand.

Google, on their developer website, have this to say about the layout_weight attribute:

In my opinion, this isn't the best explanation ever. It makes things too confusing. (Although it does say a lot about work practices inside Google HQ...clearly I have the wrong job!)

Let me see if I can't explain it a little bit better..

Here's our blank status bar again.

​

Let's say we want a notification icon and a signal icon here.

<LinearLayout
android:orientation="horizontal"
android:id="status_bar_contents"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<com.android.systemui.statusbar.phone.IconMerger
android:id="@id/notificationIcons"
android:background="#ff0099cc"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
/>
<ImageView
android:id="@id/mobile_signal"
android:background="#ff33b5e5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>

As before, I've missed out a few attributes that you'd have in the real code, just to make things a bit simpler to read.

And, as before, I've added a background to the signal icon. This time I'm using Holo Blue Dark (I like Holo Blue...).

This makes our status bar look like this:

​

I'm sure you've noticed on your status bar that the notification icons are on the left and the signal icon on the right. That's easy enough to do with a RelativeLayout or a FrameLayout but, if we have too many notifications, we would be overlapping the signal icon.

So, to do this in a LinearLayout, we use layout_weight.

This attribute essentially sets the priority for the object it's attached to.

By default, an object has a layout_weight of 0 (even if there's no layout_weight attribute in the code). So, if we give an object a layout_weight of 1, Android will allocate it more space.

Let's see it in action by changing the layout_weight of our notification icons to 1:

<LinearLayout
android:orientation="horizontal"
android:id="status_bar_contents"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<com.android.systemui.statusbar.phone.IconMerger
android:id="@id/notificationIcons"
[COLOR="Red"] android:layout_weight="1"[/COLOR]
android:background="#ff0099cc"
android:layout_width="0.0dip"
android:layout_height="fill_parent"
/>
<ImageView
android:id="@id/mobile_signal"
android:background="#ff33b5e5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>

Android now gives that object as much space in the Layout as it can, effectively pushing the signal icon to the far right.

​

Because the width of the notification icon object is being changed by the layout_weight, the layout_width attribute is superseded. So you should always set it to "0.0dip", this stops Android from having to waste processing power that's not needed.

Since this is only the status bar, we're not talking about a lot of power and it's doubtful you'd notice any difference in performance. But it's always good practice.

This is essentially how the standard Android status bar is set up.

Yes, there are more icons and more Layout but, at its heart, there is still a notification area with a weight of 1 pushing a status icon area with a weight of 0 to the right of the screen.

Have a look at your status_bar.xml and you'll see what I mean!

For fun, let's see what happens if we change the weight of the signal icon as well...

<LinearLayout
android:orientation="horizontal"
android:id="status_bar_contents"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<com.android.systemui.statusbar.phone.IconMerger
android:id="@id/notificationIcons"
[COLOR="Red"] android:layout_weight="1"[/COLOR]
android:background="#ff0099cc"
[COLOR="Red"] android:layout_width="0.0dip"[/COLOR]
android:layout_height="fill_parent"
/>
<ImageView
android:id="@id/mobile_signal"
[COLOR="Red"] android:layout_weight="1"[/COLOR]
android:background="#ff33b5e5"
[COLOR="Red"] android:layout_width="0.0dip"[/COLOR]
android:layout_height="wrap_content"
/>
</LinearLayout>

This results in:

​

That looks a bit stupid. But not to worry, we can use a spacer to push that signal icon back to the right of the screen again.

<LinearLayout
android:orientation="horizontal"
android:id="status_bar_contents"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<com.android.systemui.statusbar.phone.IconMerger
android:id="@id/notificationIcons"
android:layout_weight="1"
android:background="#ff0099cc"
android:layout_width="0.0dip"
android:layout_height="fill_parent"
/>
<LinearLayout
android:orientation="horizontal"
android:layout_weight="1"
android:layout_width="0.0dip"
android:background="#ff33b5e5"
android:layout_height="fill_parent"
>
[color="red"]<View
android:visibility="invisible"
android:layout_width="0.0dip"
android:layout_height="fill_parent"
android:layout_weight="1"
/>[/color]
<ImageView
android:id="@id/mobile_signal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
</LinearLayout>

Here we go..

​

I've changed the signal icon's weight back to 0 and put it inside a LinearLayout with a View object that has a weight of 1.

The View line pushes the signal icon to the far right but has this attribute: "android:visibility="invisible"". Remember this attribute from a few posts ago? If this is set to !invisible, Android doesn't show it but still pays attention to how much space it uses. Making it the

perfect

spacer!

Why am I doing this? Well, look what happens if we add a clock to all this code (using a background of Holo Blue Bright this time)...

<LinearLayout
android:orientation="horizontal"
android:id="status_bar_contents"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<com.android.systemui.statusbar.phone.IconMerger
android:id="@id/notificationIcons"
android:layout_weight="1"
android:background="#ff0099cc"
android:layout_width="0.0dip"
android:layout_height="fill_parent"
/>
[COLOR="Red"] <com.android.systemui.statusbar.policy.Clock
android:id="@id/clock"
android:layout_gravity="center"
android:background="#00ddff"
android:textColor="#ffffffff"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
/>[/COLOR]
<LinearLayout
android:orientation="horizontal"
android:layout_weight="1"
android:layout_width="0.0dip"
android:background="#ff33b5e5"
android:layout_height="fill_parent"
>
<View
android:visibility="invisible"
android:layout_width="0.0dip"
android:layout_height="fill_parent"
android:layout_weight="1"
/>
<ImageView
android:id="@id/mobile_signal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
</LinearLayout>

Here it is:

​

Well, would you look at that? We've just centered the clock! Neat!

Let's remove those background attributes..

<LinearLayout
android:orientation="horizontal"
android:id="status_bar_contents"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<com.android.systemui.statusbar.phone.IconMerger
android:id="@id/notificationIcons"
android:layout_weight="1"
android:layout_width="0.0dip"
android:layout_height="fill_parent"
/>
<com.android.systemui.statusbar.policy.Clock
android:id="@id/clock"
android:layout_gravity="center"
android:textColor="#ffffffff"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
/>
<LinearLayout
android:orientation="horizontal"
android:layout_weight="1"
android:layout_width="0.0dip"
android:layout_height="fill_parent"
>
<View
android:visibility="invisible"
android:layout_width="0.0dip"
android:layout_height="fill_parent"
android:layout_weight="1"
/>
<ImageView
android:id="@id/mobile_signal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
</LinearLayout>

Et voilà...

​

Have another look at the previous two posts. Hopefully they should make a bit more sense now...