Gravity.java revision 6a03640539405afbdefe72894759281b98aa6e6f
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
17package android.view;
18import android.graphics.Rect;
19
20/**
21 * Standard constants and tools for placing an object within a potentially
22 * larger container.
23 */
24public class Gravity
25{
26    /** Constant indicating that no gravity has been set **/
27    public static final int NO_GRAVITY = 0x0000;
28
29    /** Raw bit indicating the gravity for an axis has been specified. */
30    public static final int AXIS_SPECIFIED = 0x0001;
31
32    /** Raw bit controlling how the left/top edge is placed. */
33    public static final int AXIS_PULL_BEFORE = 0x0002;
34    /** Raw bit controlling how the right/bottom edge is placed. */
35    public static final int AXIS_PULL_AFTER = 0x0004;
36    /** Raw bit controlling whether the right/bottom edge is clipped to its
37     * container, based on the gravity direction being applied. */
38    public static final int AXIS_CLIP = 0x0008;
39
40    /** Bits defining the horizontal axis. */
41    public static final int AXIS_X_SHIFT = 0;
42    /** Bits defining the vertical axis. */
43    public static final int AXIS_Y_SHIFT = 4;
44
45    /** Push object to the top of its container, not changing its size. */
46    public static final int TOP = (AXIS_PULL_BEFORE|AXIS_SPECIFIED)<<AXIS_Y_SHIFT;
47    /** Push object to the bottom of its container, not changing its size. */
48    public static final int BOTTOM = (AXIS_PULL_AFTER|AXIS_SPECIFIED)<<AXIS_Y_SHIFT;
49    /** Push object to the left of its container, not changing its size. */
50    public static final int LEFT = (AXIS_PULL_BEFORE|AXIS_SPECIFIED)<<AXIS_X_SHIFT;
51    /** Push object to the right of its container, not changing its size. */
52    public static final int RIGHT = (AXIS_PULL_AFTER|AXIS_SPECIFIED)<<AXIS_X_SHIFT;
53
54    /** Place object in the vertical center of its container, not changing its
55     *  size. */
56    public static final int CENTER_VERTICAL = AXIS_SPECIFIED<<AXIS_Y_SHIFT;
57    /** Grow the vertical size of the object if needed so it completely fills
58     *  its container. */
59    public static final int FILL_VERTICAL = TOP|BOTTOM;
60
61    /** Place object in the horizontal center of its container, not changing its
62     *  size. */
63    public static final int CENTER_HORIZONTAL = AXIS_SPECIFIED<<AXIS_X_SHIFT;
64    /** Grow the horizontal size of the object if needed so it completely fills
65     *  its container. */
66    public static final int FILL_HORIZONTAL = LEFT|RIGHT;
67
68    /** Place the object in the center of its container in both the vertical
69     *  and horizontal axis, not changing its size. */
70    public static final int CENTER = CENTER_VERTICAL|CENTER_HORIZONTAL;
71
72    /** Grow the horizontal and vertical size of the object if needed so it
73     *  completely fills its container. */
74    public static final int FILL = FILL_VERTICAL|FILL_HORIZONTAL;
75
76    /** Flag to clip the edges of the object to its container along the
77     *  vertical axis. */
78    public static final int CLIP_VERTICAL = AXIS_CLIP<<AXIS_Y_SHIFT;
79
80    /** Flag to clip the edges of the object to its container along the
81     *  horizontal axis. */
82    public static final int CLIP_HORIZONTAL = AXIS_CLIP<<AXIS_X_SHIFT;
83
84    /** Raw bit controlling whether the horizontal direction is relative (before/after) or not. */
85    public static final int RELATIVE_HORIZONTAL_DIRECTION = 0x00800000;
86
87    /**
88     * Binary mask to get the absolute horizontal gravity of a gravity.
89     */
90    public static final int HORIZONTAL_GRAVITY_MASK = (AXIS_SPECIFIED |
91            AXIS_PULL_BEFORE | AXIS_PULL_AFTER) << AXIS_X_SHIFT;
92    /**
93     * Binary mask to get the vertical gravity of a gravity.
94     */
95    public static final int VERTICAL_GRAVITY_MASK = (AXIS_SPECIFIED |
96            AXIS_PULL_BEFORE | AXIS_PULL_AFTER) << AXIS_Y_SHIFT;
97
98    /** Special constant to enable clipping to an overall display along the
99     *  vertical dimension.  This is not applied by default by
100     *  {@link #apply(int, int, int, Rect, int, int, Rect)}; you must do so
101     *  yourself by calling {@link #applyDisplay}.
102     */
103    public static final int DISPLAY_CLIP_VERTICAL = 0x10000000;
104
105    /** Special constant to enable clipping to an overall display along the
106     *  horizontal 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_HORIZONTAL = 0x01000000;
111
112    /** Push object to x-axis position before its container, not changing its size. */
113    public static final int BEFORE = RELATIVE_HORIZONTAL_DIRECTION | LEFT;
114
115    /** Push object to x-axis position after its container, not changing its size. */
116    public static final int AFTER = RELATIVE_HORIZONTAL_DIRECTION | RIGHT;
117
118    /**
119     * Binary mask for the horizontal gravity and script specific direction bit.
120     */
121    public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = BEFORE | AFTER;
122
123    /**
124     * Apply a gravity constant to an object. This suppose that the layout direction is LTR.
125     *
126     * @param gravity The desired placement of the object, as defined by the
127     *                constants in this class.
128     * @param w The horizontal size of the object.
129     * @param h The vertical size of the object.
130     * @param container The frame of the containing space, in which the object
131     *                  will be placed.  Should be large enough to contain the
132     *                  width and height of the object.
133     * @param outRect Receives the computed frame of the object in its
134     *                container.
135     */
136    public static void apply(int gravity, int w, int h, Rect container, Rect outRect) {
137        apply(gravity, w, h, container, 0, 0, outRect);
138    }
139
140    /**
141     * Apply a gravity constant to an object and take care if layout direction is RTL or not.
142     *
143     * @param gravity The desired placement of the object, as defined by the
144     *                constants in this class.
145     * @param w The horizontal size of the object.
146     * @param h The vertical size of the object.
147     * @param container The frame of the containing space, in which the object
148     *                  will be placed.  Should be large enough to contain the
149     *                  width and height of the object.
150     * @param outRect Receives the computed frame of the object in its
151     *                container.
152     * @param isRtl Whether the layout is right-to-left.
153     *
154     * @hide
155     */
156    public static void apply(int gravity, int w, int h, Rect container,
157            Rect outRect, boolean isRtl) {
158        int absGravity = getAbsoluteGravity(gravity, isRtl);
159        apply(absGravity, w, h, container, 0, 0, outRect);
160    }
161
162    /**
163     * Apply a gravity constant to an object.
164     *
165     * @param gravity The desired placement of the object, as defined by the
166     *                constants in this class.
167     * @param w The horizontal size of the object.
168     * @param h The vertical size of the object.
169     * @param container The frame of the containing space, in which the object
170     *                  will be placed.  Should be large enough to contain the
171     *                  width and height of the object.
172     * @param xAdj Offset to apply to the X axis.  If gravity is LEFT this
173     *             pushes it to the right; if gravity is RIGHT it pushes it to
174     *             the left; if gravity is CENTER_HORIZONTAL it pushes it to the
175     *             right or left; otherwise it is ignored.
176     * @param yAdj Offset to apply to the Y axis.  If gravity is TOP this pushes
177     *             it down; if gravity is BOTTOM it pushes it up; if gravity is
178     *             CENTER_VERTICAL it pushes it down or up; otherwise it is
179     *             ignored.
180     * @param outRect Receives the computed frame of the object in its
181     *                container.
182     */
183    public static void apply(int gravity, int w, int h, Rect container,
184            int xAdj, int yAdj, Rect outRect) {
185        switch (gravity&((AXIS_PULL_BEFORE|AXIS_PULL_AFTER)<<AXIS_X_SHIFT)) {
186            case 0:
187                outRect.left = container.left
188                        + ((container.right - container.left - w)/2) + xAdj;
189                outRect.right = outRect.left + w;
190                if ((gravity&(AXIS_CLIP<<AXIS_X_SHIFT))
191                        == (AXIS_CLIP<<AXIS_X_SHIFT)) {
192                    if (outRect.left < container.left) {
193                        outRect.left = container.left;
194                    }
195                    if (outRect.right > container.right) {
196                        outRect.right = container.right;
197                    }
198                }
199                break;
200            case AXIS_PULL_BEFORE<<AXIS_X_SHIFT:
201                outRect.left = container.left + xAdj;
202                outRect.right = outRect.left + w;
203                if ((gravity&(AXIS_CLIP<<AXIS_X_SHIFT))
204                        == (AXIS_CLIP<<AXIS_X_SHIFT)) {
205                    if (outRect.right > container.right) {
206                        outRect.right = container.right;
207                    }
208                }
209                break;
210            case AXIS_PULL_AFTER<<AXIS_X_SHIFT:
211                outRect.right = container.right - xAdj;
212                outRect.left = outRect.right - w;
213                if ((gravity&(AXIS_CLIP<<AXIS_X_SHIFT))
214                        == (AXIS_CLIP<<AXIS_X_SHIFT)) {
215                    if (outRect.left < container.left) {
216                        outRect.left = container.left;
217                    }
218                }
219                break;
220            default:
221                outRect.left = container.left + xAdj;
222                outRect.right = container.right + xAdj;
223                break;
224        }
225
226        switch (gravity&((AXIS_PULL_BEFORE|AXIS_PULL_AFTER)<<AXIS_Y_SHIFT)) {
227            case 0:
228                outRect.top = container.top
229                        + ((container.bottom - container.top - h)/2) + yAdj;
230                outRect.bottom = outRect.top + h;
231                if ((gravity&(AXIS_CLIP<<AXIS_Y_SHIFT))
232                        == (AXIS_CLIP<<AXIS_Y_SHIFT)) {
233                    if (outRect.top < container.top) {
234                        outRect.top = container.top;
235                    }
236                    if (outRect.bottom > container.bottom) {
237                        outRect.bottom = container.bottom;
238                    }
239                }
240                break;
241            case AXIS_PULL_BEFORE<<AXIS_Y_SHIFT:
242                outRect.top = container.top + yAdj;
243                outRect.bottom = outRect.top + h;
244                if ((gravity&(AXIS_CLIP<<AXIS_Y_SHIFT))
245                        == (AXIS_CLIP<<AXIS_Y_SHIFT)) {
246                    if (outRect.bottom > container.bottom) {
247                        outRect.bottom = container.bottom;
248                    }
249                }
250                break;
251            case AXIS_PULL_AFTER<<AXIS_Y_SHIFT:
252                outRect.bottom = container.bottom - yAdj;
253                outRect.top = outRect.bottom - h;
254                if ((gravity&(AXIS_CLIP<<AXIS_Y_SHIFT))
255                        == (AXIS_CLIP<<AXIS_Y_SHIFT)) {
256                    if (outRect.top < container.top) {
257                        outRect.top = container.top;
258                    }
259                }
260                break;
261            default:
262                outRect.top = container.top + yAdj;
263                outRect.bottom = container.bottom + yAdj;
264                break;
265        }
266    }
267
268    /**
269     * Apply additional gravity behavior based on the overall "display" that an
270     * object exists in.  This can be used after
271     * {@link #apply(int, int, int, Rect, int, int, Rect)} to place the object
272     * within a visible display.  By default this moves or clips the object
273     * to be visible in the display; the gravity flags
274     * {@link #DISPLAY_CLIP_HORIZONTAL} and {@link #DISPLAY_CLIP_VERTICAL}
275     * can be used to change this behavior.
276     *
277     * @param gravity Gravity constants to modify the placement within the
278     * display.
279     * @param display The rectangle of the display in which the object is
280     * being placed.
281     * @param inoutObj Supplies the current object position; returns with it
282     * modified if needed to fit in the display.
283     */
284    public static void applyDisplay(int gravity, Rect display, Rect inoutObj) {
285        if ((gravity&DISPLAY_CLIP_VERTICAL) != 0) {
286            if (inoutObj.top < display.top) inoutObj.top = display.top;
287            if (inoutObj.bottom > display.bottom) inoutObj.bottom = display.bottom;
288        } else {
289            int off = 0;
290            if (inoutObj.top < display.top) off = display.top-inoutObj.top;
291            else if (inoutObj.bottom > display.bottom) off = display.bottom-inoutObj.bottom;
292            if (off != 0) {
293                if (inoutObj.height() > (display.bottom-display.top)) {
294                    inoutObj.top = display.top;
295                    inoutObj.bottom = display.bottom;
296                } else {
297                    inoutObj.top += off;
298                    inoutObj.bottom += off;
299                }
300            }
301        }
302
303        if ((gravity&DISPLAY_CLIP_HORIZONTAL) != 0) {
304            if (inoutObj.left < display.left) inoutObj.left = display.left;
305            if (inoutObj.right > display.right) inoutObj.right = display.right;
306        } else {
307            int off = 0;
308            if (inoutObj.left < display.left) off = display.left-inoutObj.left;
309            else if (inoutObj.right > display.right) off = display.right-inoutObj.right;
310            if (off != 0) {
311                if (inoutObj.width() > (display.right-display.left)) {
312                    inoutObj.left = display.left;
313                    inoutObj.right = display.right;
314                } else {
315                    inoutObj.left += off;
316                    inoutObj.right += off;
317                }
318            }
319        }
320    }
321
322    /**
323     * <p>Indicate whether the supplied gravity has a vertical pull.</p>
324     *
325     * @param gravity the gravity to check for vertical pull
326     * @return true if the supplied gravity has a vertical pull
327     */
328    public static boolean isVertical(int gravity) {
329        return gravity > 0 && (gravity & VERTICAL_GRAVITY_MASK) != 0;
330    }
331
332    /**
333     * <p>Indicate whether the supplied gravity has an horizontal pull.</p>
334     *
335     * @param gravity the gravity to check for horizontal pull
336     * @return true if the supplied gravity has an horizontal pull
337     */
338    public static boolean isHorizontal(int gravity) {
339        return gravity > 0 && (gravity & RELATIVE_HORIZONTAL_GRAVITY_MASK) != 0;
340    }
341
342    /**
343     * <p>Convert script specific gravity to absolute horizontal value.</p>
344     *
345     * if horizontal direction is LTR, then BEFORE will set LEFT and AFTER will set RIGHT.
346     * if horizontal direction is RTL, then BEFORE will set RIGHT and AFTER will set LEFT.
347     *
348     * If no horizontal direction is found, then just add LEFT to the existing gravity
349     *
350     * @param gravity The gravity to convert to absolute (horizontal) values.
351     * @param isRtl Whether the layout is right-to-left.
352     * @return gravity converted to absolute (horizontal) values.
353     */
354    public static int getAbsoluteGravity(int gravity, boolean isRtl) {
355        int result = gravity;
356        // Set default gravity, if no horizontal gravity is specified
357        if ((result & HORIZONTAL_GRAVITY_MASK) == 0) {
358            result |= Gravity.LEFT;
359        }
360        // If layout is script specific and gravity is horizontal relative (BEFORE or AFTER)
361        if ((result & RELATIVE_HORIZONTAL_DIRECTION) > 0) {
362            if ((result & Gravity.BEFORE) == Gravity.BEFORE) {
363                // Remove the BEFORE bit
364                result &= ~BEFORE;
365                if (isRtl) {
366                    // Set the RIGHT bit
367                    result |= RIGHT;
368                } else {
369                    // Set the LEFT bit
370                    result |= LEFT;
371                }
372            } else if ((result & Gravity.AFTER) == Gravity.AFTER) {
373                // Remove the AFTER bit
374                result &= ~AFTER;
375                if (isRtl) {
376                    // Set the LEFT bit
377                    result |= LEFT;
378                } else {
379                    // Set the RIGHT bit
380                    result |= RIGHT;
381                }
382            }
383            // Don't need the script specific bit any more, so remove it as we are converting to
384            // absolute values (LEFT or RIGHT)
385            result &= ~RELATIVE_HORIZONTAL_DIRECTION;
386        }
387        return result;
388    }
389}
390