Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to check gravity flags in a custom Android View?

Tags:

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:

  • Binary: 0001 0000, Gravity.CENTER_VERTICAL: Constant Value: 16 (0x00000010)
  • Binary: 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.

like image 647
Suragch Avatar asked Feb 11 '17 09:02

Suragch


2 Answers

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.

like image 179
azizbekian Avatar answered Sep 30 '22 17:09

azizbekian


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.

Horizontal axis gravity

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.

Vertical axis gravity

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

Both axes

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

Bit math

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
like image 45
Suragch Avatar answered Sep 30 '22 18:09

Suragch