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