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 suppose 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 * @hide 157 */ apply(int gravity, int w, int h, Rect container, Rect outRect, int layoutDirection)158 public static void apply(int gravity, int w, int h, Rect container, 159 Rect outRect, int layoutDirection) { 160 int absGravity = getAbsoluteGravity(gravity, layoutDirection); 161 apply(absGravity, w, h, container, 0, 0, outRect); 162 } 163 164 /** 165 * Apply a gravity constant to an object. 166 * 167 * @param gravity The desired placement of the object, as defined by the 168 * constants in this class. 169 * @param w The horizontal size of the object. 170 * @param h The vertical size of the object. 171 * @param container The frame of the containing space, in which the object 172 * will be placed. Should be large enough to contain the 173 * width and height of the object. 174 * @param xAdj Offset to apply to the X axis. If gravity is LEFT this 175 * pushes it to the right; if gravity is RIGHT it pushes it to 176 * the left; if gravity is CENTER_HORIZONTAL it pushes it to the 177 * right or left; otherwise it is ignored. 178 * @param yAdj Offset to apply to the Y axis. If gravity is TOP this pushes 179 * it down; if gravity is BOTTOM it pushes it up; if gravity is 180 * CENTER_VERTICAL it pushes it down or up; otherwise it is 181 * ignored. 182 * @param outRect Receives the computed frame of the object in its 183 * container. 184 */ apply(int gravity, int w, int h, Rect container, int xAdj, int yAdj, Rect outRect)185 public static void apply(int gravity, int w, int h, Rect container, 186 int xAdj, int yAdj, Rect outRect) { 187 switch (gravity&((AXIS_PULL_BEFORE|AXIS_PULL_AFTER)<<AXIS_X_SHIFT)) { 188 case 0: 189 outRect.left = container.left 190 + ((container.right - container.left - w)/2) + xAdj; 191 outRect.right = outRect.left + w; 192 if ((gravity&(AXIS_CLIP<<AXIS_X_SHIFT)) 193 == (AXIS_CLIP<<AXIS_X_SHIFT)) { 194 if (outRect.left < container.left) { 195 outRect.left = container.left; 196 } 197 if (outRect.right > container.right) { 198 outRect.right = container.right; 199 } 200 } 201 break; 202 case AXIS_PULL_BEFORE<<AXIS_X_SHIFT: 203 outRect.left = container.left + xAdj; 204 outRect.right = outRect.left + w; 205 if ((gravity&(AXIS_CLIP<<AXIS_X_SHIFT)) 206 == (AXIS_CLIP<<AXIS_X_SHIFT)) { 207 if (outRect.right > container.right) { 208 outRect.right = container.right; 209 } 210 } 211 break; 212 case AXIS_PULL_AFTER<<AXIS_X_SHIFT: 213 outRect.right = container.right - xAdj; 214 outRect.left = outRect.right - w; 215 if ((gravity&(AXIS_CLIP<<AXIS_X_SHIFT)) 216 == (AXIS_CLIP<<AXIS_X_SHIFT)) { 217 if (outRect.left < container.left) { 218 outRect.left = container.left; 219 } 220 } 221 break; 222 default: 223 outRect.left = container.left + xAdj; 224 outRect.right = container.right + xAdj; 225 break; 226 } 227 228 switch (gravity&((AXIS_PULL_BEFORE|AXIS_PULL_AFTER)<<AXIS_Y_SHIFT)) { 229 case 0: 230 outRect.top = container.top 231 + ((container.bottom - container.top - h)/2) + yAdj; 232 outRect.bottom = outRect.top + h; 233 if ((gravity&(AXIS_CLIP<<AXIS_Y_SHIFT)) 234 == (AXIS_CLIP<<AXIS_Y_SHIFT)) { 235 if (outRect.top < container.top) { 236 outRect.top = container.top; 237 } 238 if (outRect.bottom > container.bottom) { 239 outRect.bottom = container.bottom; 240 } 241 } 242 break; 243 case AXIS_PULL_BEFORE<<AXIS_Y_SHIFT: 244 outRect.top = container.top + yAdj; 245 outRect.bottom = outRect.top + h; 246 if ((gravity&(AXIS_CLIP<<AXIS_Y_SHIFT)) 247 == (AXIS_CLIP<<AXIS_Y_SHIFT)) { 248 if (outRect.bottom > container.bottom) { 249 outRect.bottom = container.bottom; 250 } 251 } 252 break; 253 case AXIS_PULL_AFTER<<AXIS_Y_SHIFT: 254 outRect.bottom = container.bottom - yAdj; 255 outRect.top = outRect.bottom - h; 256 if ((gravity&(AXIS_CLIP<<AXIS_Y_SHIFT)) 257 == (AXIS_CLIP<<AXIS_Y_SHIFT)) { 258 if (outRect.top < container.top) { 259 outRect.top = container.top; 260 } 261 } 262 break; 263 default: 264 outRect.top = container.top + yAdj; 265 outRect.bottom = container.bottom + yAdj; 266 break; 267 } 268 } 269 270 /** 271 * Apply additional gravity behavior based on the overall "display" that an 272 * object exists in. This can be used after 273 * {@link #apply(int, int, int, Rect, int, int, Rect)} to place the object 274 * within a visible display. By default this moves or clips the object 275 * to be visible in the display; the gravity flags 276 * {@link #DISPLAY_CLIP_HORIZONTAL} and {@link #DISPLAY_CLIP_VERTICAL} 277 * can be used to change this behavior. 278 * 279 * @param gravity Gravity constants to modify the placement within the 280 * display. 281 * @param display The rectangle of the display in which the object is 282 * being placed. 283 * @param inoutObj Supplies the current object position; returns with it 284 * modified if needed to fit in the display. 285 */ applyDisplay(int gravity, Rect display, Rect inoutObj)286 public static void applyDisplay(int gravity, Rect display, Rect inoutObj) { 287 if ((gravity&DISPLAY_CLIP_VERTICAL) != 0) { 288 if (inoutObj.top < display.top) inoutObj.top = display.top; 289 if (inoutObj.bottom > display.bottom) inoutObj.bottom = display.bottom; 290 } else { 291 int off = 0; 292 if (inoutObj.top < display.top) off = display.top-inoutObj.top; 293 else if (inoutObj.bottom > display.bottom) off = display.bottom-inoutObj.bottom; 294 if (off != 0) { 295 if (inoutObj.height() > (display.bottom-display.top)) { 296 inoutObj.top = display.top; 297 inoutObj.bottom = display.bottom; 298 } else { 299 inoutObj.top += off; 300 inoutObj.bottom += off; 301 } 302 } 303 } 304 305 if ((gravity&DISPLAY_CLIP_HORIZONTAL) != 0) { 306 if (inoutObj.left < display.left) inoutObj.left = display.left; 307 if (inoutObj.right > display.right) inoutObj.right = display.right; 308 } else { 309 int off = 0; 310 if (inoutObj.left < display.left) off = display.left-inoutObj.left; 311 else if (inoutObj.right > display.right) off = display.right-inoutObj.right; 312 if (off != 0) { 313 if (inoutObj.width() > (display.right-display.left)) { 314 inoutObj.left = display.left; 315 inoutObj.right = display.right; 316 } else { 317 inoutObj.left += off; 318 inoutObj.right += off; 319 } 320 } 321 } 322 } 323 324 /** 325 * <p>Indicate whether the supplied gravity has a vertical pull.</p> 326 * 327 * @param gravity the gravity to check for vertical pull 328 * @return true if the supplied gravity has a vertical pull 329 */ isVertical(int gravity)330 public static boolean isVertical(int gravity) { 331 return gravity > 0 && (gravity & VERTICAL_GRAVITY_MASK) != 0; 332 } 333 334 /** 335 * <p>Indicate whether the supplied gravity has an horizontal pull.</p> 336 * 337 * @param gravity the gravity to check for horizontal pull 338 * @return true if the supplied gravity has an horizontal pull 339 */ isHorizontal(int gravity)340 public static boolean isHorizontal(int gravity) { 341 return gravity > 0 && (gravity & RELATIVE_HORIZONTAL_GRAVITY_MASK) != 0; 342 } 343 344 /** 345 * <p>Convert script specific gravity to absolute horizontal value.</p> 346 * 347 * if horizontal direction is LTR, then START will set LEFT and END will set RIGHT. 348 * if horizontal direction is RTL, then START will set RIGHT and END will set LEFT. 349 * 350 * 351 * @param gravity The gravity to convert to absolute (horizontal) values. 352 * @param layoutDirection The layout direction. 353 * @return gravity converted to absolute (horizontal) values. 354 */ getAbsoluteGravity(int gravity, int layoutDirection)355 public static int getAbsoluteGravity(int gravity, int layoutDirection) { 356 int result = gravity; 357 // If layout is script specific and gravity is horizontal relative (START or END) 358 if ((result & RELATIVE_LAYOUT_DIRECTION) > 0) { 359 if ((result & Gravity.START) == Gravity.START) { 360 // Remove the START bit 361 result &= ~START; 362 if (layoutDirection == View.LAYOUT_DIRECTION_RTL) { 363 // Set the RIGHT bit 364 result |= RIGHT; 365 } else { 366 // Set the LEFT bit 367 result |= LEFT; 368 } 369 } else if ((result & Gravity.END) == Gravity.END) { 370 // Remove the END bit 371 result &= ~END; 372 if (layoutDirection == View.LAYOUT_DIRECTION_RTL) { 373 // Set the LEFT bit 374 result |= LEFT; 375 } else { 376 // Set the RIGHT bit 377 result |= RIGHT; 378 } 379 } 380 // Don't need the script specific bit any more, so remove it as we are converting to 381 // absolute values (LEFT or RIGHT) 382 result &= ~RELATIVE_LAYOUT_DIRECTION; 383 } 384 return result; 385 } 386 } 387