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.graphics; 18 19 import android.annotation.CheckResult; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.TestApi; 23 import android.compat.annotation.UnsupportedAppUsage; 24 import android.os.Parcel; 25 import android.os.Parcelable; 26 import android.text.TextUtils; 27 import android.util.proto.ProtoInputStream; 28 import android.util.proto.ProtoOutputStream; 29 import android.util.proto.WireTypeMismatchException; 30 31 import java.io.IOException; 32 import java.io.PrintWriter; 33 import java.util.regex.Matcher; 34 import java.util.regex.Pattern; 35 36 /** 37 * Rect holds four integer coordinates for a rectangle. The rectangle is 38 * represented by the coordinates of its 4 edges (left, top, right bottom). 39 * These fields can be accessed directly. Use width() and height() to retrieve 40 * the rectangle's width and height. Note: most methods do not check to see that 41 * the coordinates are sorted correctly (i.e. left <= right and top <= bottom). 42 * <p> 43 * Note that the right and bottom coordinates are exclusive. This means a Rect 44 * being drawn untransformed onto a {@link android.graphics.Canvas} will draw 45 * into the column and row described by its left and top coordinates, but not 46 * those of its bottom and right. 47 */ 48 public final class Rect implements Parcelable { 49 public int left; 50 public int top; 51 public int right; 52 public int bottom; 53 54 /** 55 * A helper class for flattened rectange pattern recognition. A separate 56 * class to avoid an initialization dependency on a regular expression 57 * causing Rect to not be initializable with an ahead-of-time compilation 58 * scheme. 59 */ 60 private static final class UnflattenHelper { 61 private static final Pattern FLATTENED_PATTERN = Pattern.compile( 62 "(-?\\d+) (-?\\d+) (-?\\d+) (-?\\d+)"); 63 getMatcher(String str)64 static Matcher getMatcher(String str) { 65 return FLATTENED_PATTERN.matcher(str); 66 } 67 } 68 69 /** 70 * Create a new empty Rect. All coordinates are initialized to 0. 71 */ Rect()72 public Rect() {} 73 74 /** 75 * Create a new rectangle with the specified coordinates. Note: no range 76 * checking is performed, so the caller must ensure that left <= right and 77 * top <= bottom. 78 * 79 * @param left The X coordinate of the left side of the rectangle 80 * @param top The Y coordinate of the top of the rectangle 81 * @param right The X coordinate of the right side of the rectangle 82 * @param bottom The Y coordinate of the bottom of the rectangle 83 */ Rect(int left, int top, int right, int bottom)84 public Rect(int left, int top, int right, int bottom) { 85 this.left = left; 86 this.top = top; 87 this.right = right; 88 this.bottom = bottom; 89 } 90 91 /** 92 * Create a new rectangle, initialized with the values in the specified 93 * rectangle (which is left unmodified). 94 * 95 * @param r The rectangle whose coordinates are copied into the new 96 * rectangle. 97 */ Rect(@ullable Rect r)98 public Rect(@Nullable Rect r) { 99 if (r == null) { 100 left = top = right = bottom = 0; 101 } else { 102 left = r.left; 103 top = r.top; 104 right = r.right; 105 bottom = r.bottom; 106 } 107 } 108 109 /** 110 * @hide 111 */ Rect(@ullable Insets r)112 public Rect(@Nullable Insets r) { 113 if (r == null) { 114 left = top = right = bottom = 0; 115 } else { 116 left = r.left; 117 top = r.top; 118 right = r.right; 119 bottom = r.bottom; 120 } 121 } 122 123 /** 124 * Returns a copy of {@code r} if {@code r} is not {@code null}, or {@code null} otherwise. 125 * 126 * @hide 127 */ 128 @Nullable copyOrNull(@ullable Rect r)129 public static Rect copyOrNull(@Nullable Rect r) { 130 return r == null ? null : new Rect(r); 131 } 132 133 @Override equals(Object o)134 public boolean equals(Object o) { 135 if (this == o) return true; 136 if (o == null || getClass() != o.getClass()) return false; 137 138 Rect r = (Rect) o; 139 return left == r.left && top == r.top && right == r.right && bottom == r.bottom; 140 } 141 142 @Override hashCode()143 public int hashCode() { 144 int result = left; 145 result = 31 * result + top; 146 result = 31 * result + right; 147 result = 31 * result + bottom; 148 return result; 149 } 150 151 @Override toString()152 public String toString() { 153 StringBuilder sb = new StringBuilder(32); 154 sb.append("Rect("); sb.append(left); sb.append(", "); 155 sb.append(top); sb.append(" - "); sb.append(right); 156 sb.append(", "); sb.append(bottom); sb.append(")"); 157 return sb.toString(); 158 } 159 160 /** 161 * Return a string representation of the rectangle in a compact form. 162 */ 163 @NonNull toShortString()164 public String toShortString() { 165 return toShortString(new StringBuilder(32)); 166 } 167 168 /** 169 * Return a string representation of the rectangle in a compact form. 170 * @hide 171 */ 172 @NonNull toShortString(@onNull StringBuilder sb)173 public String toShortString(@NonNull StringBuilder sb) { 174 sb.setLength(0); 175 sb.append('['); sb.append(left); sb.append(','); 176 sb.append(top); sb.append("]["); sb.append(right); 177 sb.append(','); sb.append(bottom); sb.append(']'); 178 return sb.toString(); 179 } 180 181 /** 182 * Return a string representation of the rectangle in a well-defined format. 183 * 184 * <p>You can later recover the Rect from this string through 185 * {@link #unflattenFromString(String)}. 186 * 187 * @return Returns a new String of the form "left top right bottom" 188 */ 189 @NonNull flattenToString()190 public String flattenToString() { 191 StringBuilder sb = new StringBuilder(32); 192 // WARNING: Do not change the format of this string, it must be 193 // preserved because Rects are saved in this flattened format. 194 sb.append(left); 195 sb.append(' '); 196 sb.append(top); 197 sb.append(' '); 198 sb.append(right); 199 sb.append(' '); 200 sb.append(bottom); 201 return sb.toString(); 202 } 203 204 /** 205 * Returns a Rect from a string of the form returned by {@link #flattenToString}, 206 * or null if the string is not of that form. 207 */ 208 @Nullable unflattenFromString(@ullable String str)209 public static Rect unflattenFromString(@Nullable String str) { 210 if (TextUtils.isEmpty(str)) { 211 return null; 212 } 213 214 Matcher matcher = UnflattenHelper.getMatcher(str); 215 if (!matcher.matches()) { 216 return null; 217 } 218 return new Rect(Integer.parseInt(matcher.group(1)), 219 Integer.parseInt(matcher.group(2)), 220 Integer.parseInt(matcher.group(3)), 221 Integer.parseInt(matcher.group(4))); 222 } 223 224 /** 225 * Print short representation to given writer. 226 * @hide 227 */ 228 @UnsupportedAppUsage printShortString(@onNull PrintWriter pw)229 public void printShortString(@NonNull PrintWriter pw) { 230 pw.print('['); pw.print(left); pw.print(','); 231 pw.print(top); pw.print("]["); pw.print(right); 232 pw.print(','); pw.print(bottom); pw.print(']'); 233 } 234 235 /** 236 * Write to a protocol buffer output stream. 237 * Protocol buffer message definition at {@link android.graphics.RectProto} 238 * 239 * @param protoOutputStream Stream to write the Rect object to. 240 * @param fieldId Field Id of the Rect as defined in the parent message 241 * @hide 242 */ dumpDebug(@onNull ProtoOutputStream protoOutputStream, long fieldId)243 public void dumpDebug(@NonNull ProtoOutputStream protoOutputStream, long fieldId) { 244 final long token = protoOutputStream.start(fieldId); 245 protoOutputStream.write(RectProto.LEFT, left); 246 protoOutputStream.write(RectProto.TOP, top); 247 protoOutputStream.write(RectProto.RIGHT, right); 248 protoOutputStream.write(RectProto.BOTTOM, bottom); 249 protoOutputStream.end(token); 250 } 251 252 /** 253 * Read from a protocol buffer input stream. 254 * Protocol buffer message definition at {@link android.graphics.RectProto} 255 * 256 * @param proto Stream to read the Rect object from. 257 * @param fieldId Field Id of the Rect as defined in the parent message 258 * @hide 259 */ readFromProto(@onNull ProtoInputStream proto, long fieldId)260 public void readFromProto(@NonNull ProtoInputStream proto, long fieldId) throws IOException, 261 WireTypeMismatchException { 262 final long token = proto.start(fieldId); 263 try { 264 while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) { 265 switch (proto.getFieldNumber()) { 266 case (int) RectProto.LEFT: 267 left = proto.readInt(RectProto.LEFT); 268 break; 269 case (int) RectProto.TOP: 270 top = proto.readInt(RectProto.TOP); 271 break; 272 case (int) RectProto.RIGHT: 273 right = proto.readInt(RectProto.RIGHT); 274 break; 275 case (int) RectProto.BOTTOM: 276 bottom = proto.readInt(RectProto.BOTTOM); 277 break; 278 } 279 } 280 } finally { 281 // Let caller handle any exceptions 282 proto.end(token); 283 } 284 } 285 286 /** 287 * Returns true if the rectangle is empty (left >= right or top >= bottom) 288 */ isEmpty()289 public final boolean isEmpty() { 290 return left >= right || top >= bottom; 291 } 292 293 /** 294 * @return the rectangle's width. This does not check for a valid rectangle 295 * (i.e. left <= right) so the result may be negative. 296 */ width()297 public final int width() { 298 return right - left; 299 } 300 301 /** 302 * @return the rectangle's height. This does not check for a valid rectangle 303 * (i.e. top <= bottom) so the result may be negative. 304 */ height()305 public final int height() { 306 return bottom - top; 307 } 308 309 /** 310 * @return the horizontal center of the rectangle. If the computed value 311 * is fractional, this method returns the largest integer that is 312 * less than the computed value. 313 */ centerX()314 public final int centerX() { 315 return (left + right) >> 1; 316 } 317 318 /** 319 * @return the vertical center of the rectangle. If the computed value 320 * is fractional, this method returns the largest integer that is 321 * less than the computed value. 322 */ centerY()323 public final int centerY() { 324 return (top + bottom) >> 1; 325 } 326 327 /** 328 * @return the exact horizontal center of the rectangle as a float. 329 */ exactCenterX()330 public final float exactCenterX() { 331 return (left + right) * 0.5f; 332 } 333 334 /** 335 * @return the exact vertical center of the rectangle as a float. 336 */ exactCenterY()337 public final float exactCenterY() { 338 return (top + bottom) * 0.5f; 339 } 340 341 /** 342 * Set the rectangle to (0,0,0,0) 343 */ setEmpty()344 public void setEmpty() { 345 left = right = top = bottom = 0; 346 } 347 348 /** 349 * Set the rectangle's coordinates to the specified values. Note: no range 350 * checking is performed, so it is up to the caller to ensure that 351 * left <= right and top <= bottom. 352 * 353 * @param left The X coordinate of the left side of the rectangle 354 * @param top The Y coordinate of the top of the rectangle 355 * @param right The X coordinate of the right side of the rectangle 356 * @param bottom The Y coordinate of the bottom of the rectangle 357 */ set(int left, int top, int right, int bottom)358 public void set(int left, int top, int right, int bottom) { 359 this.left = left; 360 this.top = top; 361 this.right = right; 362 this.bottom = bottom; 363 } 364 365 /** 366 * Copy the coordinates from src into this rectangle. 367 * 368 * @param src The rectangle whose coordinates are copied into this 369 * rectangle. 370 */ set(@onNull Rect src)371 public void set(@NonNull Rect src) { 372 this.left = src.left; 373 this.top = src.top; 374 this.right = src.right; 375 this.bottom = src.bottom; 376 } 377 378 /** 379 * Offset the rectangle by adding dx to its left and right coordinates, and 380 * adding dy to its top and bottom coordinates. 381 * 382 * @param dx The amount to add to the rectangle's left and right coordinates 383 * @param dy The amount to add to the rectangle's top and bottom coordinates 384 */ offset(int dx, int dy)385 public void offset(int dx, int dy) { 386 left += dx; 387 top += dy; 388 right += dx; 389 bottom += dy; 390 } 391 392 /** 393 * Offset the rectangle to a specific (left, top) position, 394 * keeping its width and height the same. 395 * 396 * @param newLeft The new "left" coordinate for the rectangle 397 * @param newTop The new "top" coordinate for the rectangle 398 */ offsetTo(int newLeft, int newTop)399 public void offsetTo(int newLeft, int newTop) { 400 right += newLeft - left; 401 bottom += newTop - top; 402 left = newLeft; 403 top = newTop; 404 } 405 406 /** 407 * Inset the rectangle by (dx,dy). If dx is positive, then the sides are 408 * moved inwards, making the rectangle narrower. If dx is negative, then the 409 * sides are moved outwards, making the rectangle wider. The same holds true 410 * for dy and the top and bottom. 411 * 412 * @param dx The amount to add(subtract) from the rectangle's left(right) 413 * @param dy The amount to add(subtract) from the rectangle's top(bottom) 414 */ inset(int dx, int dy)415 public void inset(int dx, int dy) { 416 left += dx; 417 top += dy; 418 right -= dx; 419 bottom -= dy; 420 } 421 422 /** 423 * Insets the rectangle on all sides specified by the dimensions of the {@code insets} 424 * rectangle. 425 * @hide 426 * @param insets The rectangle specifying the insets on all side. 427 */ inset(@onNull Rect insets)428 public void inset(@NonNull Rect insets) { 429 left += insets.left; 430 top += insets.top; 431 right -= insets.right; 432 bottom -= insets.bottom; 433 } 434 435 /** 436 * Insets the rectangle on all sides specified by the dimensions of {@code insets}. 437 * 438 * @param insets The insets to inset the rect by. 439 */ inset(@onNull Insets insets)440 public void inset(@NonNull Insets insets) { 441 left += insets.left; 442 top += insets.top; 443 right -= insets.right; 444 bottom -= insets.bottom; 445 } 446 447 /** 448 * Insets the rectangle on all sides specified by the insets. 449 * 450 * @param left The amount to add from the rectangle's left 451 * @param top The amount to add from the rectangle's top 452 * @param right The amount to subtract from the rectangle's right 453 * @param bottom The amount to subtract from the rectangle's bottom 454 */ inset(int left, int top, int right, int bottom)455 public void inset(int left, int top, int right, int bottom) { 456 this.left += left; 457 this.top += top; 458 this.right -= right; 459 this.bottom -= bottom; 460 } 461 462 /** 463 * Returns true if (x,y) is inside the rectangle. The left and top are 464 * considered to be inside, while the right and bottom are not. This means 465 * that for a x,y to be contained: left <= x < right and top <= y < bottom. 466 * An empty rectangle never contains any point. 467 * 468 * @param x The X coordinate of the point being tested for containment 469 * @param y The Y coordinate of the point being tested for containment 470 * @return true iff (x,y) are contained by the rectangle, where containment 471 * means left <= x < right and top <= y < bottom 472 */ contains(int x, int y)473 public boolean contains(int x, int y) { 474 return left < right && top < bottom // check for empty first 475 && x >= left && x < right && y >= top && y < bottom; 476 } 477 478 /** 479 * Returns true iff the 4 specified sides of a rectangle are inside or equal 480 * to this rectangle. i.e. is this rectangle a superset of the specified 481 * rectangle. An empty rectangle never contains another rectangle. 482 * 483 * @param left The left side of the rectangle being tested for containment 484 * @param top The top of the rectangle being tested for containment 485 * @param right The right side of the rectangle being tested for containment 486 * @param bottom The bottom of the rectangle being tested for containment 487 * @return true iff the the 4 specified sides of a rectangle are inside or 488 * equal to this rectangle 489 */ contains(int left, int top, int right, int bottom)490 public boolean contains(int left, int top, int right, int bottom) { 491 // check for empty first 492 return this.left < this.right && this.top < this.bottom 493 // now check for containment 494 && this.left <= left && this.top <= top 495 && this.right >= right && this.bottom >= bottom; 496 } 497 498 /** 499 * Returns true iff the specified rectangle r is inside or equal to this 500 * rectangle. An empty rectangle never contains another rectangle. 501 * 502 * @param r The rectangle being tested for containment. 503 * @return true iff the specified rectangle r is inside or equal to this 504 * rectangle 505 */ contains(@onNull Rect r)506 public boolean contains(@NonNull Rect r) { 507 // check for empty first 508 return this.left < this.right && this.top < this.bottom 509 // now check for containment 510 && left <= r.left && top <= r.top && right >= r.right && bottom >= r.bottom; 511 } 512 513 /** 514 * If the rectangle specified by left,top,right,bottom intersects this 515 * rectangle, return true and set this rectangle to that intersection, 516 * otherwise return false and do not change this rectangle. No check is 517 * performed to see if either rectangle is empty. Note: To just test for 518 * intersection, use {@link #intersects(Rect, Rect)}. 519 * 520 * @param left The left side of the rectangle being intersected with this 521 * rectangle 522 * @param top The top of the rectangle being intersected with this rectangle 523 * @param right The right side of the rectangle being intersected with this 524 * rectangle. 525 * @param bottom The bottom of the rectangle being intersected with this 526 * rectangle. 527 * @return true if the specified rectangle and this rectangle intersect 528 * (and this rectangle is then set to that intersection) else 529 * return false and do not change this rectangle. 530 */ 531 @CheckResult intersect(int left, int top, int right, int bottom)532 public boolean intersect(int left, int top, int right, int bottom) { 533 if (this.left < right && left < this.right && this.top < bottom && top < this.bottom) { 534 if (this.left < left) this.left = left; 535 if (this.top < top) this.top = top; 536 if (this.right > right) this.right = right; 537 if (this.bottom > bottom) this.bottom = bottom; 538 return true; 539 } 540 return false; 541 } 542 543 /** 544 * If the specified rectangle intersects this rectangle, return true and set 545 * this rectangle to that intersection, otherwise return false and do not 546 * change this rectangle. No check is performed to see if either rectangle 547 * is empty. To just test for intersection, use intersects() 548 * 549 * @param r The rectangle being intersected with this rectangle. 550 * @return true if the specified rectangle and this rectangle intersect 551 * (and this rectangle is then set to that intersection) else 552 * return false and do not change this rectangle. 553 */ 554 @CheckResult intersect(@onNull Rect r)555 public boolean intersect(@NonNull Rect r) { 556 return intersect(r.left, r.top, r.right, r.bottom); 557 } 558 559 /** 560 * If the specified rectangle intersects this rectangle, set this rectangle to that 561 * intersection, otherwise set this rectangle to the empty rectangle. 562 * @see #inset(int, int, int, int) but without checking if the rects overlap. 563 * @hide 564 */ intersectUnchecked(@onNull Rect other)565 public void intersectUnchecked(@NonNull Rect other) { 566 left = Math.max(left, other.left); 567 top = Math.max(top, other.top); 568 right = Math.min(right, other.right); 569 bottom = Math.min(bottom, other.bottom); 570 } 571 572 /** 573 * If rectangles a and b intersect, return true and set this rectangle to 574 * that intersection, otherwise return false and do not change this 575 * rectangle. No check is performed to see if either rectangle is empty. 576 * To just test for intersection, use intersects() 577 * 578 * @param a The first rectangle being intersected with 579 * @param b The second rectangle being intersected with 580 * @return true iff the two specified rectangles intersect. If they do, set 581 * this rectangle to that intersection. If they do not, return 582 * false and do not change this rectangle. 583 */ 584 @CheckResult setIntersect(@onNull Rect a, @NonNull Rect b)585 public boolean setIntersect(@NonNull Rect a, @NonNull Rect b) { 586 if (a.left < b.right && b.left < a.right && a.top < b.bottom && b.top < a.bottom) { 587 left = Math.max(a.left, b.left); 588 top = Math.max(a.top, b.top); 589 right = Math.min(a.right, b.right); 590 bottom = Math.min(a.bottom, b.bottom); 591 return true; 592 } 593 return false; 594 } 595 596 /** 597 * Returns true if this rectangle intersects the specified rectangle. 598 * In no event is this rectangle modified. No check is performed to see 599 * if either rectangle is empty. To record the intersection, use intersect() 600 * or setIntersect(). 601 * 602 * @param left The left side of the rectangle being tested for intersection 603 * @param top The top of the rectangle being tested for intersection 604 * @param right The right side of the rectangle being tested for 605 * intersection 606 * @param bottom The bottom of the rectangle being tested for intersection 607 * @return true iff the specified rectangle intersects this rectangle. In 608 * no event is this rectangle modified. 609 */ intersects(int left, int top, int right, int bottom)610 public boolean intersects(int left, int top, int right, int bottom) { 611 return this.left < right && left < this.right && this.top < bottom && top < this.bottom; 612 } 613 614 /** 615 * Returns true iff the two specified rectangles intersect. In no event are 616 * either of the rectangles modified. To record the intersection, 617 * use {@link #intersect(Rect)} or {@link #setIntersect(Rect, Rect)}. 618 * 619 * @param a The first rectangle being tested for intersection 620 * @param b The second rectangle being tested for intersection 621 * @return true iff the two specified rectangles intersect. In no event are 622 * either of the rectangles modified. 623 */ intersects(@onNull Rect a, @NonNull Rect b)624 public static boolean intersects(@NonNull Rect a, @NonNull Rect b) { 625 return a.left < b.right && b.left < a.right && a.top < b.bottom && b.top < a.bottom; 626 } 627 628 /** 629 * Update this Rect to enclose itself and the specified rectangle. If the 630 * specified rectangle is empty, nothing is done. If this rectangle is empty 631 * it is set to the specified rectangle. 632 * 633 * @param left The left edge being unioned with this rectangle 634 * @param top The top edge being unioned with this rectangle 635 * @param right The right edge being unioned with this rectangle 636 * @param bottom The bottom edge being unioned with this rectangle 637 */ union(int left, int top, int right, int bottom)638 public void union(int left, int top, int right, int bottom) { 639 if ((left < right) && (top < bottom)) { 640 if ((this.left < this.right) && (this.top < this.bottom)) { 641 if (this.left > left) this.left = left; 642 if (this.top > top) this.top = top; 643 if (this.right < right) this.right = right; 644 if (this.bottom < bottom) this.bottom = bottom; 645 } else { 646 this.left = left; 647 this.top = top; 648 this.right = right; 649 this.bottom = bottom; 650 } 651 } 652 } 653 654 /** 655 * Update this Rect to enclose itself and the specified rectangle. If the 656 * specified rectangle is empty, nothing is done. If this rectangle is empty 657 * it is set to the specified rectangle. 658 * 659 * @param r The rectangle being unioned with this rectangle 660 */ union(@onNull Rect r)661 public void union(@NonNull Rect r) { 662 union(r.left, r.top, r.right, r.bottom); 663 } 664 665 /** 666 * Update this Rect to enclose itself and the [x,y] coordinate. There is no 667 * check to see that this rectangle is non-empty. 668 * 669 * @param x The x coordinate of the point to add to the rectangle 670 * @param y The y coordinate of the point to add to the rectangle 671 */ union(int x, int y)672 public void union(int x, int y) { 673 if (x < left) { 674 left = x; 675 } else if (x > right) { 676 right = x; 677 } 678 if (y < top) { 679 top = y; 680 } else if (y > bottom) { 681 bottom = y; 682 } 683 } 684 685 /** 686 * Swap top/bottom or left/right if there are flipped (i.e. left > right 687 * and/or top > bottom). This can be called if 688 * the edges are computed separately, and may have crossed over each other. 689 * If the edges are already correct (i.e. left <= right and top <= bottom) 690 * then nothing is done. 691 */ sort()692 public void sort() { 693 if (left > right) { 694 int temp = left; 695 left = right; 696 right = temp; 697 } 698 if (top > bottom) { 699 int temp = top; 700 top = bottom; 701 bottom = temp; 702 } 703 } 704 705 /** 706 * Splits this Rect into small rects of the same width. 707 * @hide 708 */ 709 @TestApi splitVertically(@onNull Rect ....splits)710 public void splitVertically(@NonNull Rect ...splits) { 711 final int count = splits.length; 712 final int splitWidth = width() / count; 713 for (int i = 0; i < count; i++) { 714 final Rect split = splits[i]; 715 split.left = left + (splitWidth * i); 716 split.top = top; 717 split.right = split.left + splitWidth; 718 split.bottom = bottom; 719 } 720 } 721 722 /** 723 * Splits this Rect into small rects of the same height. 724 * @hide 725 */ 726 @TestApi splitHorizontally(@onNull Rect ....outSplits)727 public void splitHorizontally(@NonNull Rect ...outSplits) { 728 final int count = outSplits.length; 729 final int splitHeight = height() / count; 730 for (int i = 0; i < count; i++) { 731 final Rect split = outSplits[i]; 732 split.left = left; 733 split.top = top + (splitHeight * i); 734 split.right = right; 735 split.bottom = split.top + splitHeight; 736 } 737 } 738 739 /** 740 * Parcelable interface methods 741 */ 742 @Override describeContents()743 public int describeContents() { 744 return 0; 745 } 746 747 /** 748 * Write this rectangle to the specified parcel. To restore a rectangle from 749 * a parcel, use readFromParcel() 750 * @param out The parcel to write the rectangle's coordinates into 751 */ 752 @Override writeToParcel(Parcel out, int flags)753 public void writeToParcel(Parcel out, int flags) { 754 out.writeInt(left); 755 out.writeInt(top); 756 out.writeInt(right); 757 out.writeInt(bottom); 758 } 759 760 public static final @android.annotation.NonNull Parcelable.Creator<Rect> CREATOR = new Parcelable.Creator<Rect>() { 761 /** 762 * Return a new rectangle from the data in the specified parcel. 763 */ 764 @Override 765 public Rect createFromParcel(Parcel in) { 766 Rect r = new Rect(); 767 r.readFromParcel(in); 768 return r; 769 } 770 771 /** 772 * Return an array of rectangles of the specified size. 773 */ 774 @Override 775 public Rect[] newArray(int size) { 776 return new Rect[size]; 777 } 778 }; 779 780 /** 781 * Set the rectangle's coordinates from the data stored in the specified 782 * parcel. To write a rectangle to a parcel, call writeToParcel(). 783 * 784 * @param in The parcel to read the rectangle's coordinates from 785 */ readFromParcel(@onNull Parcel in)786 public void readFromParcel(@NonNull Parcel in) { 787 left = in.readInt(); 788 top = in.readInt(); 789 right = in.readInt(); 790 bottom = in.readInt(); 791 } 792 793 /** 794 * Scales up the rect by the given scale. 795 * @hide 796 */ 797 @UnsupportedAppUsage scale(float scale)798 public void scale(float scale) { 799 if (scale != 1.0f) { 800 left = (int) (left * scale + 0.5f); 801 top = (int) (top * scale + 0.5f); 802 right = (int) (right * scale + 0.5f); 803 bottom = (int) (bottom * scale + 0.5f); 804 } 805 } 806 807 } 808