• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.view;
18 
19 import android.annotation.IntDef;
20 import android.graphics.Rect;
21 
22 import java.lang.annotation.Retention;
23 import java.lang.annotation.RetentionPolicy;
24 
25 /**
26  * Standard constants and tools for placing an object within a potentially
27  * larger container.
28  */
29 public class Gravity
30 {
31     /** Constant indicating that no gravity has been set **/
32     public static final int NO_GRAVITY = 0x0000;
33 
34     /** Raw bit indicating the gravity for an axis has been specified. */
35     public static final int AXIS_SPECIFIED = 0x0001;
36 
37     /** Raw bit controlling how the left/top edge is placed. */
38     public static final int AXIS_PULL_BEFORE = 0x0002;
39     /** Raw bit controlling how the right/bottom edge is placed. */
40     public static final int AXIS_PULL_AFTER = 0x0004;
41     /** Raw bit controlling whether the right/bottom edge is clipped to its
42      * container, based on the gravity direction being applied. */
43     public static final int AXIS_CLIP = 0x0008;
44 
45     /** Bits defining the horizontal axis. */
46     public static final int AXIS_X_SHIFT = 0;
47     /** Bits defining the vertical axis. */
48     public static final int AXIS_Y_SHIFT = 4;
49 
50     /** Push object to the top of its container, not changing its size. */
51     public static final int TOP = (AXIS_PULL_BEFORE|AXIS_SPECIFIED)<<AXIS_Y_SHIFT;
52     /** Push object to the bottom of its container, not changing its size. */
53     public static final int BOTTOM = (AXIS_PULL_AFTER|AXIS_SPECIFIED)<<AXIS_Y_SHIFT;
54     /** Push object to the left of its container, not changing its size. */
55     public static final int LEFT = (AXIS_PULL_BEFORE|AXIS_SPECIFIED)<<AXIS_X_SHIFT;
56     /** Push object to the right of its container, not changing its size. */
57     public static final int RIGHT = (AXIS_PULL_AFTER|AXIS_SPECIFIED)<<AXIS_X_SHIFT;
58 
59     /** Place object in the vertical center of its container, not changing its
60      *  size. */
61     public static final int CENTER_VERTICAL = AXIS_SPECIFIED<<AXIS_Y_SHIFT;
62     /** Grow the vertical size of the object if needed so it completely fills
63      *  its container. */
64     public static final int FILL_VERTICAL = TOP|BOTTOM;
65 
66     /** Place object in the horizontal center of its container, not changing its
67      *  size. */
68     public static final int CENTER_HORIZONTAL = AXIS_SPECIFIED<<AXIS_X_SHIFT;
69     /** Grow the horizontal size of the object if needed so it completely fills
70      *  its container. */
71     public static final int FILL_HORIZONTAL = LEFT|RIGHT;
72 
73     /** Place the object in the center of its container in both the vertical
74      *  and horizontal axis, not changing its size. */
75     public static final int CENTER = CENTER_VERTICAL|CENTER_HORIZONTAL;
76 
77     /** Grow the horizontal and vertical size of the object if needed so it
78      *  completely fills its container. */
79     public static final int FILL = FILL_VERTICAL|FILL_HORIZONTAL;
80 
81     /** Flag to clip the edges of the object to its container along the
82      *  vertical axis. */
83     public static final int CLIP_VERTICAL = AXIS_CLIP<<AXIS_Y_SHIFT;
84 
85     /** Flag to clip the edges of the object to its container along the
86      *  horizontal axis. */
87     public static final int CLIP_HORIZONTAL = AXIS_CLIP<<AXIS_X_SHIFT;
88 
89     /** Raw bit controlling whether the layout direction is relative or not (START/END instead of
90      * absolute LEFT/RIGHT).
91      */
92     public static final int RELATIVE_LAYOUT_DIRECTION = 0x00800000;
93 
94     /**
95      * Binary mask to get the absolute horizontal gravity of a gravity.
96      */
97     public static final int HORIZONTAL_GRAVITY_MASK = (AXIS_SPECIFIED |
98             AXIS_PULL_BEFORE | AXIS_PULL_AFTER) << AXIS_X_SHIFT;
99     /**
100      * Binary mask to get the vertical gravity of a gravity.
101      */
102     public static final int VERTICAL_GRAVITY_MASK = (AXIS_SPECIFIED |
103             AXIS_PULL_BEFORE | AXIS_PULL_AFTER) << AXIS_Y_SHIFT;
104 
105     /** Special constant to enable clipping to an overall display along the
106      *  vertical dimension.  This is not applied by default by
107      *  {@link #apply(int, int, int, Rect, int, int, Rect)}; you must do so
108      *  yourself by calling {@link #applyDisplay}.
109      */
110     public static final int DISPLAY_CLIP_VERTICAL = 0x10000000;
111 
112     /** Special constant to enable clipping to an overall display along the
113      *  horizontal dimension.  This is not applied by default by
114      *  {@link #apply(int, int, int, Rect, int, int, Rect)}; you must do so
115      *  yourself by calling {@link #applyDisplay}.
116      */
117     public static final int DISPLAY_CLIP_HORIZONTAL = 0x01000000;
118 
119     /** Push object to x-axis position at the start of its container, not changing its size. */
120     public static final int START = RELATIVE_LAYOUT_DIRECTION | LEFT;
121 
122     /** Push object to x-axis position at the end of its container, not changing its size. */
123     public static final int END = RELATIVE_LAYOUT_DIRECTION | RIGHT;
124 
125     /**
126      * Binary mask for the horizontal gravity and script specific direction bit.
127      */
128     public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = START | END;
129 
130 
131     /**
132      * @hide
133      */
134     @Retention(RetentionPolicy.SOURCE)
135     @IntDef(flag = true, value = {
136         Gravity.FILL,
137         Gravity.FILL_HORIZONTAL,
138         Gravity.FILL_VERTICAL,
139         Gravity.START,
140         Gravity.END,
141         Gravity.LEFT,
142         Gravity.RIGHT,
143         Gravity.TOP,
144         Gravity.BOTTOM,
145         Gravity.CENTER,
146         Gravity.CENTER_HORIZONTAL,
147         Gravity.CENTER_VERTICAL,
148         Gravity.DISPLAY_CLIP_HORIZONTAL,
149         Gravity.DISPLAY_CLIP_VERTICAL,
150         Gravity.CLIP_HORIZONTAL,
151         Gravity.CLIP_VERTICAL,
152         Gravity.NO_GRAVITY
153     })
154     public @interface GravityFlags {}
155 
156     /**
157      * Apply a gravity constant to an object. This supposes that the layout direction is LTR.
158      *
159      * @param gravity The desired placement of the object, as defined by the
160      *                constants in this class.
161      * @param w The horizontal size of the object.
162      * @param h The vertical size of the object.
163      * @param container The frame of the containing space, in which the object
164      *                  will be placed.  Should be large enough to contain the
165      *                  width and height of the object.
166      * @param outRect Receives the computed frame of the object in its
167      *                container.
168      */
apply(int gravity, int w, int h, Rect container, Rect outRect)169     public static void apply(int gravity, int w, int h, Rect container, Rect outRect) {
170         apply(gravity, w, h, container, 0, 0, outRect);
171     }
172 
173     /**
174      * Apply a gravity constant to an object and take care if layout direction is RTL or not.
175      *
176      * @param gravity The desired placement of the object, as defined by the
177      *                constants in this class.
178      * @param w The horizontal size of the object.
179      * @param h The vertical size of the object.
180      * @param container The frame of the containing space, in which the object
181      *                  will be placed.  Should be large enough to contain the
182      *                  width and height of the object.
183      * @param outRect Receives the computed frame of the object in its
184      *                container.
185      * @param layoutDirection The layout direction.
186      *
187      * @see View#LAYOUT_DIRECTION_LTR
188      * @see View#LAYOUT_DIRECTION_RTL
189      */
apply(int gravity, int w, int h, Rect container, Rect outRect, int layoutDirection)190     public static void apply(int gravity, int w, int h, Rect container,
191             Rect outRect, int layoutDirection) {
192         int absGravity = getAbsoluteGravity(gravity, layoutDirection);
193         apply(absGravity, w, h, container, 0, 0, outRect);
194     }
195 
196     /**
197      * Apply a gravity constant to an object.
198      *
199      * @param gravity The desired placement of the object, as defined by the
200      *                constants in this class.
201      * @param w The horizontal size of the object.
202      * @param h The vertical size of the object.
203      * @param container The frame of the containing space, in which the object
204      *                  will be placed.  Should be large enough to contain the
205      *                  width and height of the object.
206      * @param xAdj Offset to apply to the X axis.  If gravity is LEFT this
207      *             pushes it to the right; if gravity is RIGHT it pushes it to
208      *             the left; if gravity is CENTER_HORIZONTAL it pushes it to the
209      *             right or left; otherwise it is ignored.
210      * @param yAdj Offset to apply to the Y axis.  If gravity is TOP this pushes
211      *             it down; if gravity is BOTTOM it pushes it up; if gravity is
212      *             CENTER_VERTICAL it pushes it down or up; otherwise it is
213      *             ignored.
214      * @param outRect Receives the computed frame of the object in its
215      *                container.
216      */
apply(int gravity, int w, int h, Rect container, int xAdj, int yAdj, Rect outRect)217     public static void apply(int gravity, int w, int h, Rect container,
218             int xAdj, int yAdj, Rect outRect) {
219         switch (gravity&((AXIS_PULL_BEFORE|AXIS_PULL_AFTER)<<AXIS_X_SHIFT)) {
220             case 0:
221                 outRect.left = container.left
222                         + ((container.right - container.left - w)/2) + xAdj;
223                 outRect.right = outRect.left + w;
224                 if ((gravity&(AXIS_CLIP<<AXIS_X_SHIFT))
225                         == (AXIS_CLIP<<AXIS_X_SHIFT)) {
226                     if (outRect.left < container.left) {
227                         outRect.left = container.left;
228                     }
229                     if (outRect.right > container.right) {
230                         outRect.right = container.right;
231                     }
232                 }
233                 break;
234             case AXIS_PULL_BEFORE<<AXIS_X_SHIFT:
235                 outRect.left = container.left + xAdj;
236                 outRect.right = outRect.left + w;
237                 if ((gravity&(AXIS_CLIP<<AXIS_X_SHIFT))
238                         == (AXIS_CLIP<<AXIS_X_SHIFT)) {
239                     if (outRect.right > container.right) {
240                         outRect.right = container.right;
241                     }
242                 }
243                 break;
244             case AXIS_PULL_AFTER<<AXIS_X_SHIFT:
245                 outRect.right = container.right - xAdj;
246                 outRect.left = outRect.right - w;
247                 if ((gravity&(AXIS_CLIP<<AXIS_X_SHIFT))
248                         == (AXIS_CLIP<<AXIS_X_SHIFT)) {
249                     if (outRect.left < container.left) {
250                         outRect.left = container.left;
251                     }
252                 }
253                 break;
254             default:
255                 outRect.left = container.left + xAdj;
256                 outRect.right = container.right + xAdj;
257                 break;
258         }
259 
260         switch (gravity&((AXIS_PULL_BEFORE|AXIS_PULL_AFTER)<<AXIS_Y_SHIFT)) {
261             case 0:
262                 outRect.top = container.top
263                         + ((container.bottom - container.top - h)/2) + yAdj;
264                 outRect.bottom = outRect.top + h;
265                 if ((gravity&(AXIS_CLIP<<AXIS_Y_SHIFT))
266                         == (AXIS_CLIP<<AXIS_Y_SHIFT)) {
267                     if (outRect.top < container.top) {
268                         outRect.top = container.top;
269                     }
270                     if (outRect.bottom > container.bottom) {
271                         outRect.bottom = container.bottom;
272                     }
273                 }
274                 break;
275             case AXIS_PULL_BEFORE<<AXIS_Y_SHIFT:
276                 outRect.top = container.top + yAdj;
277                 outRect.bottom = outRect.top + h;
278                 if ((gravity&(AXIS_CLIP<<AXIS_Y_SHIFT))
279                         == (AXIS_CLIP<<AXIS_Y_SHIFT)) {
280                     if (outRect.bottom > container.bottom) {
281                         outRect.bottom = container.bottom;
282                     }
283                 }
284                 break;
285             case AXIS_PULL_AFTER<<AXIS_Y_SHIFT:
286                 outRect.bottom = container.bottom - yAdj;
287                 outRect.top = outRect.bottom - h;
288                 if ((gravity&(AXIS_CLIP<<AXIS_Y_SHIFT))
289                         == (AXIS_CLIP<<AXIS_Y_SHIFT)) {
290                     if (outRect.top < container.top) {
291                         outRect.top = container.top;
292                     }
293                 }
294                 break;
295             default:
296                 outRect.top = container.top + yAdj;
297                 outRect.bottom = container.bottom + yAdj;
298                 break;
299         }
300     }
301 
302     /**
303      * Apply a gravity constant to an object.
304      *
305      * @param gravity The desired placement of the object, as defined by the
306      *                constants in this class.
307      * @param w The horizontal size of the object.
308      * @param h The vertical size of the object.
309      * @param container The frame of the containing space, in which the object
310      *                  will be placed.  Should be large enough to contain the
311      *                  width and height of the object.
312      * @param xAdj Offset to apply to the X axis.  If gravity is LEFT this
313      *             pushes it to the right; if gravity is RIGHT it pushes it to
314      *             the left; if gravity is CENTER_HORIZONTAL it pushes it to the
315      *             right or left; otherwise it is ignored.
316      * @param yAdj Offset to apply to the Y axis.  If gravity is TOP this pushes
317      *             it down; if gravity is BOTTOM it pushes it up; if gravity is
318      *             CENTER_VERTICAL it pushes it down or up; otherwise it is
319      *             ignored.
320      * @param outRect Receives the computed frame of the object in its
321      *                container.
322      * @param layoutDirection The layout direction.
323      *
324      * @see View#LAYOUT_DIRECTION_LTR
325      * @see View#LAYOUT_DIRECTION_RTL
326      */
apply(int gravity, int w, int h, Rect container, int xAdj, int yAdj, Rect outRect, int layoutDirection)327     public static void apply(int gravity, int w, int h, Rect container,
328                              int xAdj, int yAdj, Rect outRect, int layoutDirection) {
329         int absGravity = getAbsoluteGravity(gravity, layoutDirection);
330         apply(absGravity, w, h, container, xAdj, yAdj, outRect);
331     }
332 
333     /**
334      * Apply additional gravity behavior based on the overall "display" that an
335      * object exists in.  This can be used after
336      * {@link #apply(int, int, int, Rect, int, int, Rect)} to place the object
337      * within a visible display.  By default this moves or clips the object
338      * to be visible in the display; the gravity flags
339      * {@link #DISPLAY_CLIP_HORIZONTAL} and {@link #DISPLAY_CLIP_VERTICAL}
340      * can be used to change this behavior.
341      *
342      * @param gravity Gravity constants to modify the placement within the
343      * display.
344      * @param display The rectangle of the display in which the object is
345      * being placed.
346      * @param inoutObj Supplies the current object position; returns with it
347      * modified if needed to fit in the display.
348      */
applyDisplay(int gravity, Rect display, Rect inoutObj)349     public static void applyDisplay(int gravity, Rect display, Rect inoutObj) {
350         if ((gravity&DISPLAY_CLIP_VERTICAL) != 0) {
351             if (inoutObj.top < display.top) inoutObj.top = display.top;
352             if (inoutObj.bottom > display.bottom) inoutObj.bottom = display.bottom;
353         } else {
354             int off = 0;
355             if (inoutObj.top < display.top) off = display.top-inoutObj.top;
356             else if (inoutObj.bottom > display.bottom) off = display.bottom-inoutObj.bottom;
357             if (off != 0) {
358                 if (inoutObj.height() > (display.bottom-display.top)) {
359                     inoutObj.top = display.top;
360                     inoutObj.bottom = display.bottom;
361                 } else {
362                     inoutObj.top += off;
363                     inoutObj.bottom += off;
364                 }
365             }
366         }
367 
368         if ((gravity&DISPLAY_CLIP_HORIZONTAL) != 0) {
369             if (inoutObj.left < display.left) inoutObj.left = display.left;
370             if (inoutObj.right > display.right) inoutObj.right = display.right;
371         } else {
372             int off = 0;
373             if (inoutObj.left < display.left) off = display.left-inoutObj.left;
374             else if (inoutObj.right > display.right) off = display.right-inoutObj.right;
375             if (off != 0) {
376                 if (inoutObj.width() > (display.right-display.left)) {
377                     inoutObj.left = display.left;
378                     inoutObj.right = display.right;
379                 } else {
380                     inoutObj.left += off;
381                     inoutObj.right += off;
382                 }
383             }
384         }
385     }
386 
387     /**
388      * Apply additional gravity behavior based on the overall "display" that an
389      * object exists in.  This can be used after
390      * {@link #apply(int, int, int, Rect, int, int, Rect)} to place the object
391      * within a visible display.  By default this moves or clips the object
392      * to be visible in the display; the gravity flags
393      * {@link #DISPLAY_CLIP_HORIZONTAL} and {@link #DISPLAY_CLIP_VERTICAL}
394      * can be used to change this behavior.
395      *
396      * @param gravity Gravity constants to modify the placement within the
397      * display.
398      * @param display The rectangle of the display in which the object is
399      * being placed.
400      * @param inoutObj Supplies the current object position; returns with it
401      * modified if needed to fit in the display.
402      * @param layoutDirection The layout direction.
403      *
404      * @see View#LAYOUT_DIRECTION_LTR
405      * @see View#LAYOUT_DIRECTION_RTL
406      */
applyDisplay(int gravity, Rect display, Rect inoutObj, int layoutDirection)407     public static void applyDisplay(int gravity, Rect display, Rect inoutObj, int layoutDirection) {
408         int absGravity = getAbsoluteGravity(gravity, layoutDirection);
409         applyDisplay(absGravity, display, inoutObj);
410     }
411 
412     /**
413      * <p>Indicate whether the supplied gravity has a vertical pull.</p>
414      *
415      * @param gravity the gravity to check for vertical pull
416      * @return true if the supplied gravity has a vertical pull
417      */
isVertical(int gravity)418     public static boolean isVertical(int gravity) {
419         return gravity > 0 && (gravity & VERTICAL_GRAVITY_MASK) != 0;
420     }
421 
422     /**
423      * <p>Indicate whether the supplied gravity has an horizontal pull.</p>
424      *
425      * @param gravity the gravity to check for horizontal pull
426      * @return true if the supplied gravity has an horizontal pull
427      */
isHorizontal(int gravity)428     public static boolean isHorizontal(int gravity) {
429         return gravity > 0 && (gravity & RELATIVE_HORIZONTAL_GRAVITY_MASK) != 0;
430     }
431 
432     /**
433      * <p>Convert script specific gravity to absolute horizontal value.</p>
434      *
435      * if horizontal direction is LTR, then START will set LEFT and END will set RIGHT.
436      * if horizontal direction is RTL, then START will set RIGHT and END will set LEFT.
437      *
438      *
439      * @param gravity The gravity to convert to absolute (horizontal) values.
440      * @param layoutDirection The layout direction.
441      * @return gravity converted to absolute (horizontal) values.
442      */
getAbsoluteGravity(int gravity, int layoutDirection)443     public static int getAbsoluteGravity(int gravity, int layoutDirection) {
444         int result = gravity;
445         // If layout is script specific and gravity is horizontal relative (START or END)
446         if ((result & RELATIVE_LAYOUT_DIRECTION) > 0) {
447             if ((result & Gravity.START) == Gravity.START) {
448                 // Remove the START bit
449                 result &= ~START;
450                 if (layoutDirection == View.LAYOUT_DIRECTION_RTL) {
451                     // Set the RIGHT bit
452                     result |= RIGHT;
453                 } else {
454                     // Set the LEFT bit
455                     result |= LEFT;
456                 }
457             } else if ((result & Gravity.END) == Gravity.END) {
458                 // Remove the END bit
459                 result &= ~END;
460                 if (layoutDirection == View.LAYOUT_DIRECTION_RTL) {
461                     // Set the LEFT bit
462                     result |= LEFT;
463                 } else {
464                     // Set the RIGHT bit
465                     result |= RIGHT;
466                 }
467             }
468             // Don't need the script specific bit any more, so remove it as we are converting to
469             // absolute values (LEFT or RIGHT)
470             result &= ~RELATIVE_LAYOUT_DIRECTION;
471         }
472         return result;
473     }
474 
475     /**
476      * @hide
477      */
toString(int gravity)478     public static String toString(int gravity) {
479         final StringBuilder result = new StringBuilder();
480         if ((gravity & FILL) == FILL) {
481             result.append("FILL").append(' ');
482         } else {
483             if ((gravity & FILL_VERTICAL) == FILL_VERTICAL) {
484                 result.append("FILL_VERTICAL").append(' ');
485             } else {
486                 if ((gravity & TOP) == TOP) {
487                     result.append("TOP").append(' ');
488                 }
489                 if ((gravity & BOTTOM) == BOTTOM) {
490                     result.append("BOTTOM").append(' ');
491                 }
492             }
493             if ((gravity & FILL_HORIZONTAL) == FILL_HORIZONTAL) {
494                 result.append("FILL_HORIZONTAL").append(' ');
495             } else {
496                 if ((gravity & START) == START) {
497                     result.append("START").append(' ');
498                 } else if ((gravity & LEFT) == LEFT) {
499                     result.append("LEFT").append(' ');
500                 }
501                 if ((gravity & END) == END) {
502                     result.append("END").append(' ');
503                 } else if ((gravity & RIGHT) == RIGHT) {
504                     result.append("RIGHT").append(' ');
505                 }
506             }
507         }
508         if ((gravity & CENTER) == CENTER) {
509             result.append("CENTER").append(' ');
510         } else {
511             if ((gravity & CENTER_VERTICAL) == CENTER_VERTICAL) {
512                 result.append("CENTER_VERTICAL").append(' ');
513             }
514             if ((gravity & CENTER_HORIZONTAL) == CENTER_HORIZONTAL) {
515                 result.append("CENTER_HORIZONTAL").append(' ');
516             }
517         }
518         if (result.length() == 0) {
519             result.append("NO GRAVITY").append(' ');
520         }
521         if ((gravity & DISPLAY_CLIP_VERTICAL) == DISPLAY_CLIP_VERTICAL) {
522             result.append("DISPLAY_CLIP_VERTICAL").append(' ');
523         }
524         if ((gravity & DISPLAY_CLIP_HORIZONTAL) == DISPLAY_CLIP_HORIZONTAL) {
525             result.append("DISPLAY_CLIP_HORIZONTAL").append(' ');
526         }
527         result.deleteCharAt(result.length() - 1);
528         return result.toString();
529     }
530 }
531