Gravity.java revision 935ae463d495d41155e27feb849768ad2b8b16db
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    /**
85     * Binary mask to get the horizontal gravity of a gravity.
86     */
87    public static final int HORIZONTAL_GRAVITY_MASK = (AXIS_SPECIFIED |
88            AXIS_PULL_BEFORE | AXIS_PULL_AFTER) << AXIS_X_SHIFT;
89    /**
90     * Binary mask to get the vertical gravity of a gravity.
91     */
92    public static final int VERTICAL_GRAVITY_MASK = (AXIS_SPECIFIED |
93            AXIS_PULL_BEFORE | AXIS_PULL_AFTER) << AXIS_Y_SHIFT;
94
95    /** Special constant to enable clipping to an overall display along the
96     *  vertical dimension.  This is not applied by default by
97     *  {@link #apply(int, int, int, Rect, int, int, Rect)}; you must do so
98     *  yourself by calling {@link #applyDisplay}.
99     */
100    public static final int DISPLAY_CLIP_VERTICAL = 0x10000000;
101
102    /** Special constant to enable clipping to an overall display along the
103     *  horizontal dimension.  This is not applied by default by
104     *  {@link #apply(int, int, int, Rect, int, int, Rect)}; you must do so
105     *  yourself by calling {@link #applyDisplay}.
106     */
107    public static final int DISPLAY_CLIP_HORIZONTAL = 0x01000000;
108
109    /**
110     * Apply a gravity constant to an object.
111     *
112     * @param gravity The desired placement of the object, as defined by the
113     *                constants in this class.
114     * @param w The horizontal size of the object.
115     * @param h The vertical size of the object.
116     * @param container The frame of the containing space, in which the object
117     *                  will be placed.  Should be large enough to contain the
118     *                  width and height of the object.
119     * @param outRect Receives the computed frame of the object in its
120     *                container.
121     */
122    public static void apply(int gravity, int w, int h, Rect container,
123                             Rect outRect) {
124        apply(gravity, w, h, container, 0, 0, outRect);
125    }
126
127    /**
128     * Apply a gravity constant to an object.
129     *
130     * @param gravity The desired placement of the object, as defined by the
131     *                constants in this class.
132     * @param w The horizontal size of the object.
133     * @param h The vertical size of the object.
134     * @param container The frame of the containing space, in which the object
135     *                  will be placed.  Should be large enough to contain the
136     *                  width and height of the object.
137     * @param xAdj Offset to apply to the X axis.  If gravity is LEFT this
138     *             pushes it to the right; if gravity is RIGHT it pushes it to
139     *             the left; if gravity is CENTER_HORIZONTAL it pushes it to the
140     *             right or left; otherwise it is ignored.
141     * @param yAdj Offset to apply to the Y axis.  If gravity is TOP this pushes
142     *             it down; if gravity is BOTTOM it pushes it up; if gravity is
143     *             CENTER_VERTICAL it pushes it down or up; otherwise it is
144     *             ignored.
145     * @param outRect Receives the computed frame of the object in its
146     *                container.
147     */
148    public static void apply(int gravity, int w, int h, Rect container,
149                             int xAdj, int yAdj, Rect outRect) {
150        switch (gravity&((AXIS_PULL_BEFORE|AXIS_PULL_AFTER)<<AXIS_X_SHIFT)) {
151            case 0:
152                outRect.left = container.left
153                        + ((container.right - container.left - w)/2) + xAdj;
154                outRect.right = outRect.left + w;
155                if ((gravity&(AXIS_CLIP<<AXIS_X_SHIFT))
156                        == (AXIS_CLIP<<AXIS_X_SHIFT)) {
157                    if (outRect.left < container.left) {
158                        outRect.left = container.left;
159                    }
160                    if (outRect.right > container.right) {
161                        outRect.right = container.right;
162                    }
163                }
164                break;
165            case AXIS_PULL_BEFORE<<AXIS_X_SHIFT:
166                outRect.left = container.left + xAdj;
167                outRect.right = outRect.left + w;
168                if ((gravity&(AXIS_CLIP<<AXIS_X_SHIFT))
169                        == (AXIS_CLIP<<AXIS_X_SHIFT)) {
170                    if (outRect.right > container.right) {
171                        outRect.right = container.right;
172                    }
173                }
174                break;
175            case AXIS_PULL_AFTER<<AXIS_X_SHIFT:
176                outRect.right = container.right - xAdj;
177                outRect.left = outRect.right - w;
178                if ((gravity&(AXIS_CLIP<<AXIS_X_SHIFT))
179                        == (AXIS_CLIP<<AXIS_X_SHIFT)) {
180                    if (outRect.left < container.left) {
181                        outRect.left = container.left;
182                    }
183                }
184                break;
185            default:
186                outRect.left = container.left + xAdj;
187                outRect.right = container.right + xAdj;
188                break;
189        }
190
191        switch (gravity&((AXIS_PULL_BEFORE|AXIS_PULL_AFTER)<<AXIS_Y_SHIFT)) {
192            case 0:
193                outRect.top = container.top
194                        + ((container.bottom - container.top - h)/2) + yAdj;
195                outRect.bottom = outRect.top + h;
196                if ((gravity&(AXIS_CLIP<<AXIS_Y_SHIFT))
197                        == (AXIS_CLIP<<AXIS_Y_SHIFT)) {
198                    if (outRect.top < container.top) {
199                        outRect.top = container.top;
200                    }
201                    if (outRect.bottom > container.bottom) {
202                        outRect.bottom = container.bottom;
203                    }
204                }
205                break;
206            case AXIS_PULL_BEFORE<<AXIS_Y_SHIFT:
207                outRect.top = container.top + yAdj;
208                outRect.bottom = outRect.top + h;
209                if ((gravity&(AXIS_CLIP<<AXIS_Y_SHIFT))
210                        == (AXIS_CLIP<<AXIS_Y_SHIFT)) {
211                    if (outRect.bottom > container.bottom) {
212                        outRect.bottom = container.bottom;
213                    }
214                }
215                break;
216            case AXIS_PULL_AFTER<<AXIS_Y_SHIFT:
217                outRect.bottom = container.bottom - yAdj;
218                outRect.top = outRect.bottom - h;
219                if ((gravity&(AXIS_CLIP<<AXIS_Y_SHIFT))
220                        == (AXIS_CLIP<<AXIS_Y_SHIFT)) {
221                    if (outRect.top < container.top) {
222                        outRect.top = container.top;
223                    }
224                }
225                break;
226            default:
227                outRect.top = container.top + yAdj;
228                outRect.bottom = container.bottom + yAdj;
229                break;
230        }
231    }
232
233    /**
234     * Apply additional gravity behavior based on the overall "display" that an
235     * object exists in.  This can be used after
236     * {@link #apply(int, int, int, Rect, int, int, Rect)} to place the object
237     * within a visible display.  By default this moves or clips the object
238     * to be visible in the display; the gravity flags
239     * {@link #DISPLAY_CLIP_HORIZONTAL} and {@link #DISPLAY_CLIP_VERTICAL}
240     * can be used to change this behavior.
241     *
242     * @param gravity Gravity constants to modify the placement within the
243     * display.
244     * @param display The rectangle of the display in which the object is
245     * being placed.
246     * @param inoutObj Supplies the current object position; returns with it
247     * modified if needed to fit in the display.
248     */
249    public static void applyDisplay(int gravity, Rect display, Rect inoutObj) {
250        if ((gravity&DISPLAY_CLIP_VERTICAL) != 0) {
251            if (inoutObj.top < display.top) inoutObj.top = display.top;
252            if (inoutObj.bottom > display.bottom) inoutObj.bottom = display.bottom;
253        } else {
254            int off = 0;
255            if (inoutObj.top < display.top) off = display.top-inoutObj.top;
256            else if (inoutObj.bottom > display.bottom) off = display.bottom-inoutObj.bottom;
257            if (off != 0) {
258                if (inoutObj.height() > (display.bottom-display.top)) {
259                    inoutObj.top = display.top;
260                    inoutObj.bottom = display.bottom;
261                } else {
262                    inoutObj.top += off;
263                    inoutObj.bottom += off;
264                }
265            }
266        }
267
268        if ((gravity&DISPLAY_CLIP_HORIZONTAL) != 0) {
269            if (inoutObj.left < display.left) inoutObj.left = display.left;
270            if (inoutObj.right > display.right) inoutObj.right = display.right;
271        } else {
272            int off = 0;
273            if (inoutObj.left < display.left) off = display.left-inoutObj.left;
274            else if (inoutObj.right > display.right) off = display.right-inoutObj.right;
275            if (off != 0) {
276                if (inoutObj.width() > (display.right-display.left)) {
277                    inoutObj.left = display.left;
278                    inoutObj.right = display.right;
279                } else {
280                    inoutObj.left += off;
281                    inoutObj.right += off;
282                }
283            }
284        }
285    }
286
287    /**
288     * <p>Indicate whether the supplied gravity has a vertical pull.</p>
289     *
290     * @param gravity the gravity to check for vertical pull
291     * @return true if the supplied gravity has a vertical pull
292     */
293    public static boolean isVertical(int gravity) {
294        return gravity > 0 && (gravity & VERTICAL_GRAVITY_MASK) != 0;
295    }
296
297    /**
298     * <p>Indicate whether the supplied gravity has an horizontal pull.</p>
299     *
300     * @param gravity the gravity to check for horizontal pull
301     * @return true if the supplied gravity has an horizontal pull
302     */
303    public static boolean isHorizontal(int gravity) {
304        return gravity > 0 && (gravity & HORIZONTAL_GRAVITY_MASK) != 0;
305    }
306}
307