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 */ apply(int gravity, int w, int h, Rect container, Rect outRect)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 */ apply(int gravity, int w, int h, Rect container, Rect outRect, int layoutDirection)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 */ apply(int gravity, int w, int h, Rect container, int xAdj, int yAdj, Rect outRect)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 */ apply(int gravity, int w, int h, Rect container, int xAdj, int yAdj, Rect outRect, int layoutDirection)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 */ applyDisplay(int gravity, Rect display, Rect inoutObj)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 */ applyDisplay(int gravity, Rect display, Rect inoutObj, int layoutDirection)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 */ isVertical(int gravity)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 */ isHorizontal(int gravity)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 */ getAbsoluteGravity(int gravity, int layoutDirection)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 */ toString(int gravity)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