Gravity.java revision c05aace48af1104dd917e8b5ad2dd76a02645ada
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 * @param gravity The gravity to convert to absolute (horizontal) values. 349 * @param isRtl Whether the layout is right-to-left. 350 * @return gravity converted to absolute (horizontal) values. 351 */ 352 public static int getAbsoluteGravity(int gravity, boolean isRtl) { 353 int result = gravity; 354 // If layout is script specific and gravity is horizontal relative (BEFORE or AFTER) 355 if ((result & RELATIVE_HORIZONTAL_DIRECTION) > 0) { 356 if ((result & Gravity.BEFORE) == Gravity.BEFORE) { 357 // Remove the BEFORE bit 358 result &= ~BEFORE; 359 if (isRtl) { 360 // Set the RIGHT bit 361 result |= RIGHT; 362 } else { 363 // Set the LEFT bit 364 result |= LEFT; 365 } 366 } else if ((result & Gravity.AFTER) == Gravity.AFTER) { 367 // Remove the AFTER bit 368 result &= ~AFTER; 369 if (isRtl) { 370 // Set the LEFT bit 371 result |= LEFT; 372 } else { 373 // Set the RIGHT bit 374 result |= RIGHT; 375 } 376 } 377 // Don't need the script specific bit any more, so remove it as we are converting to 378 // absolute values (LEFT or RIGHT) 379 result &= ~RELATIVE_HORIZONTAL_DIRECTION; 380 } 381 return result; 382 } 383} 384