The problem
I have a custom Android view in which I want get the user set gravity in order to layout the content in onDraw
. Here is a simplified version that I am using in onDraw
:
// check gravity
if ((mGravity & Gravity.CENTER_VERTICAL) == Gravity.CENTER_VERTICAL) {
// draw the content centered vertically
} else if ((mGravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
// draw the content at the bottom
}
where mGravity
is obtained from the xml attributes (like this).
If I set the gravity to Gravity.CENTER_VERTICAL
it works fine. But I was surprised to find that if I set it to Gravity.BOTTOM
, the Gravity.CENTER_VERTICAL
check is still true!
Why is this happening?
I had to look at the binary values to see why:
0001 0000
, Gravity.CENTER_VERTICAL
: Constant Value: 16 (0x00000010)0101 0000
, Gravity.BOTTOM
: Constant Value: 80 (0x00000050)Thus, when I do
mGravity = Gravity.BOTTOM;
(mGravity & Gravity.CENTER_VERTICAL) == Gravity.CENTER_VERTICAL
// (0101 & 0001) == 0001
I get a false positive.
What do I do?
So how am I supposed to check the gravity flags?
I could do something like if (mGravity == Gravity.CENTER_VERTICAL)
, but then I would only get an exact match. If the user set gravity to something like center_vertical|right
then it would fail.
You can examine how FrameLayout
lays its children. Particularly, this code:
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
...
case Gravity.RIGHT:
...
case Gravity.LEFT:
...
}
switch (verticalGravity) {
case Gravity.TOP:
...
case Gravity.CENTER_VERTICAL:
...
case Gravity.BOTTOM:
...
}
There're masks in Gravity
class: VERTICAL_GRAVITY_MASK
, HORIZONTAL_GRAVITY_MASK
which will help you to find out what gravities have been applied.
This is a supplemental answer to @azizbekian's very helpful solution. I'm adding this to help myself more fully understand how gravity works behind the scenes.
LEFT
and RIGHT
are known as absolute gravity. That is, if a user specifies a relative gravity of START
or END
, then it is converted internally to an absolute gravity of RIGHT
or LEFT
depending on the situation.
0000 0001 CENTER_HORIZONTAL
0000 0011 LEFT
0000 0101 RIGHT
---------
0000 0111 HORIZONTAL_GRAVITY_MASK
A note about START
and END
1000 0000 0000 0000 0000 0011 START
0000 0000 0000 0000 0000 0011 LEFT
1000 0000 0000 0000 0000 0101 END
0000 0000 0000 0000 0000 0101 RIGHT
-----------------------------
0000 0000 0000 0000 0000 0111 HORIZONTAL_GRAVITY_MASK
As you can see here, START
and LEFT
only differ by a single bit. It is the same for END
and RIGHT
. Thus, if you use the HORIZONTAL_GRAVITY_MASK
directly on START
and END
, they will default to LEFT
and RIGHT
respectively. However, this should be used with caution. Right-to-left language locales should be taken into consideration.
The y axis gravity is shifted over 4 bits from the x axis (horizontal) gravity.
0001 0000 CENTER_VERTICAL
0011 0000 TOP
0101 0000 BOTTOM
---------
0111 0000 VERTICAL_GRAVITY_MASK
Note that CENTER
is a combination of CENTER_VERTICAL
and CENTER_HORIZONTAL
. Thus, you can also use one of the gravity masks to convert it.
0000 0001 CENTER_HORIZONTAL
0001 0000 CENTER_VERTICAL
0001 0001 CENTER
---------
0000 0111 HORIZONTAL_GRAVITY_MASK
0111 0000 VERTICAL_GRAVITY_MASK
Use the bit OR operator (|
) to combine horizontal and vertical gravity.
Example:
int myGravity = Gravity.RIGHT | Gravity.BOTTOM;
0000 0101 RIGHT
0101 0000 BOTTOM
---------
0101 0101 myGravity
Use the bit AND operator (&
) with one of the gravity masks to isolate the horizontal or vertical gravity.
Example
int verticalGravity = myGravity & Gravity.VERTICAL_GRAVITY_MASK;
if (verticalGravity == Gravity.BOTTOM) ...
0101 0101 myGravity
0111 0000 VERTICAL_GRAVITY_MASK
---------
0101 0000 verticalGravity
0101 0000 BOTTOM
User contributions licensed under CC BY-SA 3.0