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 {@code true} if the rectangle is valid (left <= right and top <= bottom). 295 * @hide 296 */ isValid()297 public boolean isValid() { 298 return left <= right && top <= bottom; 299 } 300 301 /** 302 * @return the rectangle's width. This does not check for a valid rectangle 303 * (i.e. left <= right) so the result may be negative. 304 */ width()305 public final int width() { 306 return right - left; 307 } 308 309 /** 310 * @return the rectangle's height. This does not check for a valid rectangle 311 * (i.e. top <= bottom) so the result may be negative. 312 */ height()313 public final int height() { 314 return bottom - top; 315 } 316 317 /** 318 * @return the horizontal center of the rectangle. If the computed value 319 * is fractional, this method returns the largest integer that is 320 * less than the computed value. 321 */ centerX()322 public final int centerX() { 323 return (left + right) >> 1; 324 } 325 326 /** 327 * @return the vertical center of the rectangle. If the computed value 328 * is fractional, this method returns the largest integer that is 329 * less than the computed value. 330 */ centerY()331 public final int centerY() { 332 return (top + bottom) >> 1; 333 } 334 335 /** 336 * @return the exact horizontal center of the rectangle as a float. 337 */ exactCenterX()338 public final float exactCenterX() { 339 return (left + right) * 0.5f; 340 } 341 342 /** 343 * @return the exact vertical center of the rectangle as a float. 344 */ exactCenterY()345 public final float exactCenterY() { 346 return (top + bottom) * 0.5f; 347 } 348 349 /** 350 * Set the rectangle to (0,0,0,0) 351 */ setEmpty()352 public void setEmpty() { 353 left = right = top = bottom = 0; 354 } 355 356 /** 357 * Set the rectangle's coordinates to the specified values. Note: no range 358 * checking is performed, so it is up to the caller to ensure that 359 * left <= right and top <= bottom. 360 * 361 * @param left The X coordinate of the left side of the rectangle 362 * @param top The Y coordinate of the top of the rectangle 363 * @param right The X coordinate of the right side of the rectangle 364 * @param bottom The Y coordinate of the bottom of the rectangle 365 */ set(int left, int top, int right, int bottom)366 public void set(int left, int top, int right, int bottom) { 367 this.left = left; 368 this.top = top; 369 this.right = right; 370 this.bottom = bottom; 371 } 372 373 /** 374 * Copy the coordinates from src into this rectangle. 375 * 376 * @param src The rectangle whose coordinates are copied into this 377 * rectangle. 378 */ set(@onNull Rect src)379 public void set(@NonNull Rect src) { 380 this.left = src.left; 381 this.top = src.top; 382 this.right = src.right; 383 this.bottom = src.bottom; 384 } 385 386 /** 387 * Offset the rectangle by adding dx to its left and right coordinates, and 388 * adding dy to its top and bottom coordinates. 389 * 390 * @param dx The amount to add to the rectangle's left and right coordinates 391 * @param dy The amount to add to the rectangle's top and bottom coordinates 392 */ offset(int dx, int dy)393 public void offset(int dx, int dy) { 394 left += dx; 395 top += dy; 396 right += dx; 397 bottom += dy; 398 } 399 400 /** 401 * Offset the rectangle to a specific (left, top) position, 402 * keeping its width and height the same. 403 * 404 * @param newLeft The new "left" coordinate for the rectangle 405 * @param newTop The new "top" coordinate for the rectangle 406 */ offsetTo(int newLeft, int newTop)407 public void offsetTo(int newLeft, int newTop) { 408 right += newLeft - left; 409 bottom += newTop - top; 410 left = newLeft; 411 top = newTop; 412 } 413 414 /** 415 * Inset the rectangle by (dx,dy). If dx is positive, then the sides are 416 * moved inwards, making the rectangle narrower. If dx is negative, then the 417 * sides are moved outwards, making the rectangle wider. The same holds true 418 * for dy and the top and bottom. 419 * 420 * @param dx The amount to add(subtract) from the rectangle's left(right) 421 * @param dy The amount to add(subtract) from the rectangle's top(bottom) 422 */ inset(int dx, int dy)423 public void inset(int dx, int dy) { 424 left += dx; 425 top += dy; 426 right -= dx; 427 bottom -= dy; 428 } 429 430 /** 431 * Insets the rectangle on all sides specified by the dimensions of the {@code insets} 432 * rectangle. 433 * @hide 434 * @param insets The rectangle specifying the insets on all side. 435 */ inset(@onNull Rect insets)436 public void inset(@NonNull Rect insets) { 437 left += insets.left; 438 top += insets.top; 439 right -= insets.right; 440 bottom -= insets.bottom; 441 } 442 443 /** 444 * Insets the rectangle on all sides specified by the dimensions of {@code insets}. 445 * 446 * @param insets The insets to inset the rect by. 447 */ inset(@onNull Insets insets)448 public void inset(@NonNull Insets insets) { 449 left += insets.left; 450 top += insets.top; 451 right -= insets.right; 452 bottom -= insets.bottom; 453 } 454 455 /** 456 * Insets the rectangle on all sides specified by the insets. 457 * 458 * @param left The amount to add from the rectangle's left 459 * @param top The amount to add from the rectangle's top 460 * @param right The amount to subtract from the rectangle's right 461 * @param bottom The amount to subtract from the rectangle's bottom 462 */ inset(int left, int top, int right, int bottom)463 public void inset(int left, int top, int right, int bottom) { 464 this.left += left; 465 this.top += top; 466 this.right -= right; 467 this.bottom -= bottom; 468 } 469 470 /** 471 * Returns true if (x,y) is inside the rectangle. The left and top are 472 * considered to be inside, while the right and bottom are not. This means 473 * that for a x,y to be contained: left <= x < right and top <= y < bottom. 474 * An empty rectangle never contains any point. 475 * 476 * @param x The X coordinate of the point being tested for containment 477 * @param y The Y coordinate of the point being tested for containment 478 * @return true iff (x,y) are contained by the rectangle, where containment 479 * means left <= x < right and top <= y < bottom 480 */ contains(int x, int y)481 public boolean contains(int x, int y) { 482 return left < right && top < bottom // check for empty first 483 && x >= left && x < right && y >= top && y < bottom; 484 } 485 486 /** 487 * Returns true iff the 4 specified sides of a rectangle are inside or equal 488 * to this rectangle. i.e. is this rectangle a superset of the specified 489 * rectangle. An empty rectangle never contains another rectangle. 490 * 491 * @param left The left side of the rectangle being tested for containment 492 * @param top The top of the rectangle being tested for containment 493 * @param right The right side of the rectangle being tested for containment 494 * @param bottom The bottom of the rectangle being tested for containment 495 * @return true iff the the 4 specified sides of a rectangle are inside or 496 * equal to this rectangle 497 */ contains(int left, int top, int right, int bottom)498 public boolean contains(int left, int top, int right, int bottom) { 499 // check for empty first 500 return this.left < this.right && this.top < this.bottom 501 // now check for containment 502 && this.left <= left && this.top <= top 503 && this.right >= right && this.bottom >= bottom; 504 } 505 506 /** 507 * Returns true iff the specified rectangle r is inside or equal to this 508 * rectangle. An empty rectangle never contains another rectangle. 509 * 510 * @param r The rectangle being tested for containment. 511 * @return true iff the specified rectangle r is inside or equal to this 512 * rectangle 513 */ contains(@onNull Rect r)514 public boolean contains(@NonNull Rect r) { 515 // check for empty first 516 return this.left < this.right && this.top < this.bottom 517 // now check for containment 518 && left <= r.left && top <= r.top && right >= r.right && bottom >= r.bottom; 519 } 520 521 /** 522 * If the rectangle specified by left,top,right,bottom intersects this 523 * rectangle, return true and set this rectangle to that intersection, 524 * otherwise return false and do not change this rectangle. No check is 525 * performed to see if either rectangle is empty. Note: To just test for 526 * intersection, use {@link #intersects(Rect, Rect)}. 527 * 528 * @param left The left side of the rectangle being intersected with this 529 * rectangle 530 * @param top The top of the rectangle being intersected with this rectangle 531 * @param right The right side of the rectangle being intersected with this 532 * rectangle. 533 * @param bottom The bottom of the rectangle being intersected with this 534 * rectangle. 535 * @return true if the specified rectangle and this rectangle intersect 536 * (and this rectangle is then set to that intersection) else 537 * return false and do not change this rectangle. 538 */ 539 @CheckResult intersect(int left, int top, int right, int bottom)540 public boolean intersect(int left, int top, int right, int bottom) { 541 if (this.left < right && left < this.right && this.top < bottom && top < this.bottom) { 542 if (this.left < left) this.left = left; 543 if (this.top < top) this.top = top; 544 if (this.right > right) this.right = right; 545 if (this.bottom > bottom) this.bottom = bottom; 546 return true; 547 } 548 return false; 549 } 550 551 /** 552 * If the specified rectangle intersects this rectangle, return true and set 553 * this rectangle to that intersection, otherwise return false and do not 554 * change this rectangle. No check is performed to see if either rectangle 555 * is empty. To just test for intersection, use intersects() 556 * 557 * @param r The rectangle being intersected with this rectangle. 558 * @return true if the specified rectangle and this rectangle intersect 559 * (and this rectangle is then set to that intersection) else 560 * return false and do not change this rectangle. 561 */ 562 @CheckResult intersect(@onNull Rect r)563 public boolean intersect(@NonNull Rect r) { 564 return intersect(r.left, r.top, r.right, r.bottom); 565 } 566 567 /** 568 * If the specified rectangle intersects this rectangle, set this rectangle to that 569 * intersection, otherwise set this rectangle to the empty rectangle. 570 * @see #inset(int, int, int, int) but without checking if the rects overlap. 571 * @hide 572 */ intersectUnchecked(@onNull Rect other)573 public void intersectUnchecked(@NonNull Rect other) { 574 left = Math.max(left, other.left); 575 top = Math.max(top, other.top); 576 right = Math.min(right, other.right); 577 bottom = Math.min(bottom, other.bottom); 578 } 579 580 /** 581 * If rectangles a and b intersect, return true and set this rectangle to 582 * that intersection, otherwise return false and do not change this 583 * rectangle. No check is performed to see if either rectangle is empty. 584 * To just test for intersection, use intersects() 585 * 586 * @param a The first rectangle being intersected with 587 * @param b The second rectangle being intersected with 588 * @return true iff the two specified rectangles intersect. If they do, set 589 * this rectangle to that intersection. If they do not, return 590 * false and do not change this rectangle. 591 */ 592 @CheckResult setIntersect(@onNull Rect a, @NonNull Rect b)593 public boolean setIntersect(@NonNull Rect a, @NonNull Rect b) { 594 if (a.left < b.right && b.left < a.right && a.top < b.bottom && b.top < a.bottom) { 595 left = Math.max(a.left, b.left); 596 top = Math.max(a.top, b.top); 597 right = Math.min(a.right, b.right); 598 bottom = Math.min(a.bottom, b.bottom); 599 return true; 600 } 601 return false; 602 } 603 604 /** 605 * Returns true if this rectangle intersects the specified rectangle. 606 * In no event is this rectangle modified. No check is performed to see 607 * if either rectangle is empty. To record the intersection, use intersect() 608 * or setIntersect(). 609 * 610 * @param left The left side of the rectangle being tested for intersection 611 * @param top The top of the rectangle being tested for intersection 612 * @param right The right side of the rectangle being tested for 613 * intersection 614 * @param bottom The bottom of the rectangle being tested for intersection 615 * @return true iff the specified rectangle intersects this rectangle. In 616 * no event is this rectangle modified. 617 */ intersects(int left, int top, int right, int bottom)618 public boolean intersects(int left, int top, int right, int bottom) { 619 return this.left < right && left < this.right && this.top < bottom && top < this.bottom; 620 } 621 622 /** 623 * Returns true iff the two specified rectangles intersect. In no event are 624 * either of the rectangles modified. To record the intersection, 625 * use {@link #intersect(Rect)} or {@link #setIntersect(Rect, Rect)}. 626 * 627 * @param a The first rectangle being tested for intersection 628 * @param b The second rectangle being tested for intersection 629 * @return true iff the two specified rectangles intersect. In no event are 630 * either of the rectangles modified. 631 */ intersects(@onNull Rect a, @NonNull Rect b)632 public static boolean intersects(@NonNull Rect a, @NonNull Rect b) { 633 return a.left < b.right && b.left < a.right && a.top < b.bottom && b.top < a.bottom; 634 } 635 636 /** 637 * Update this Rect to enclose itself and the specified rectangle. If the 638 * specified rectangle is empty, nothing is done. If this rectangle is empty 639 * it is set to the specified rectangle. 640 * 641 * @param left The left edge being unioned with this rectangle 642 * @param top The top edge being unioned with this rectangle 643 * @param right The right edge being unioned with this rectangle 644 * @param bottom The bottom edge being unioned with this rectangle 645 */ union(int left, int top, int right, int bottom)646 public void union(int left, int top, int right, int bottom) { 647 if ((left < right) && (top < bottom)) { 648 if ((this.left < this.right) && (this.top < this.bottom)) { 649 if (this.left > left) this.left = left; 650 if (this.top > top) this.top = top; 651 if (this.right < right) this.right = right; 652 if (this.bottom < bottom) this.bottom = bottom; 653 } else { 654 this.left = left; 655 this.top = top; 656 this.right = right; 657 this.bottom = bottom; 658 } 659 } 660 } 661 662 /** 663 * Update this Rect to enclose itself and the specified rectangle. If the 664 * specified rectangle is empty, nothing is done. If this rectangle is empty 665 * it is set to the specified rectangle. 666 * 667 * @param r The rectangle being unioned with this rectangle 668 */ union(@onNull Rect r)669 public void union(@NonNull Rect r) { 670 union(r.left, r.top, r.right, r.bottom); 671 } 672 673 /** 674 * Update this Rect to enclose itself and the [x,y] coordinate. There is no 675 * check to see that this rectangle is non-empty. 676 * 677 * @param x The x coordinate of the point to add to the rectangle 678 * @param y The y coordinate of the point to add to the rectangle 679 */ union(int x, int y)680 public void union(int x, int y) { 681 if (x < left) { 682 left = x; 683 } else if (x > right) { 684 right = x; 685 } 686 if (y < top) { 687 top = y; 688 } else if (y > bottom) { 689 bottom = y; 690 } 691 } 692 693 /** 694 * Swap top/bottom or left/right if there are flipped (i.e. left > right 695 * and/or top > bottom). This can be called if 696 * the edges are computed separately, and may have crossed over each other. 697 * If the edges are already correct (i.e. left <= right and top <= bottom) 698 * then nothing is done. 699 */ sort()700 public void sort() { 701 if (left > right) { 702 int temp = left; 703 left = right; 704 right = temp; 705 } 706 if (top > bottom) { 707 int temp = top; 708 top = bottom; 709 bottom = temp; 710 } 711 } 712 713 /** 714 * Splits this Rect into small rects of the same width. 715 * @hide 716 */ 717 @TestApi splitVertically(@onNull Rect ....splits)718 public void splitVertically(@NonNull Rect ...splits) { 719 final int count = splits.length; 720 final int splitWidth = width() / count; 721 for (int i = 0; i < count; i++) { 722 final Rect split = splits[i]; 723 split.left = left + (splitWidth * i); 724 split.top = top; 725 split.right = split.left + splitWidth; 726 split.bottom = bottom; 727 } 728 } 729 730 /** 731 * Splits this Rect into small rects of the same height. 732 * @hide 733 */ 734 @TestApi splitHorizontally(@onNull Rect ....outSplits)735 public void splitHorizontally(@NonNull Rect ...outSplits) { 736 final int count = outSplits.length; 737 final int splitHeight = height() / count; 738 for (int i = 0; i < count; i++) { 739 final Rect split = outSplits[i]; 740 split.left = left; 741 split.top = top + (splitHeight * i); 742 split.right = right; 743 split.bottom = split.top + splitHeight; 744 } 745 } 746 747 /** 748 * Parcelable interface methods 749 */ 750 @Override describeContents()751 public int describeContents() { 752 return 0; 753 } 754 755 /** 756 * Write this rectangle to the specified parcel. To restore a rectangle from 757 * a parcel, use readFromParcel() 758 * @param out The parcel to write the rectangle's coordinates into 759 */ 760 @Override writeToParcel(Parcel out, int flags)761 public void writeToParcel(Parcel out, int flags) { 762 out.writeInt(left); 763 out.writeInt(top); 764 out.writeInt(right); 765 out.writeInt(bottom); 766 } 767 768 public static final @android.annotation.NonNull Parcelable.Creator<Rect> CREATOR = new Parcelable.Creator<Rect>() { 769 /** 770 * Return a new rectangle from the data in the specified parcel. 771 */ 772 @Override 773 public Rect createFromParcel(Parcel in) { 774 Rect r = new Rect(); 775 r.readFromParcel(in); 776 return r; 777 } 778 779 /** 780 * Return an array of rectangles of the specified size. 781 */ 782 @Override 783 public Rect[] newArray(int size) { 784 return new Rect[size]; 785 } 786 }; 787 788 /** 789 * Set the rectangle's coordinates from the data stored in the specified 790 * parcel. To write a rectangle to a parcel, call writeToParcel(). 791 * 792 * @param in The parcel to read the rectangle's coordinates from 793 */ readFromParcel(@onNull Parcel in)794 public void readFromParcel(@NonNull Parcel in) { 795 left = in.readInt(); 796 top = in.readInt(); 797 right = in.readInt(); 798 bottom = in.readInt(); 799 } 800 801 /** 802 * Scales up the rect by the given scale. 803 * @hide 804 */ 805 @UnsupportedAppUsage scale(float scale)806 public void scale(float scale) { 807 if (scale != 1.0f) { 808 left = (int) (left * scale + 0.5f); 809 top = (int) (top * scale + 0.5f); 810 right = (int) (right * scale + 0.5f); 811 bottom = (int) (bottom * scale + 0.5f); 812 } 813 } 814 815 } 816