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