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