817 lines
28 KiB
Java
817 lines
28 KiB
Java
![]() |
/*
|
||
|
* Copyright (C) 2006 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package android.graphics;
|
||
|
|
||
|
import android.annotation.CheckResult;
|
||
|
import android.annotation.NonNull;
|
||
|
import android.annotation.Nullable;
|
||
|
import android.annotation.TestApi;
|
||
|
import android.compat.annotation.UnsupportedAppUsage;
|
||
|
import android.os.Parcel;
|
||
|
import android.os.Parcelable;
|
||
|
import android.text.TextUtils;
|
||
|
import android.util.proto.ProtoInputStream;
|
||
|
import android.util.proto.ProtoOutputStream;
|
||
|
import android.util.proto.WireTypeMismatchException;
|
||
|
|
||
|
import java.io.IOException;
|
||
|
import java.io.PrintWriter;
|
||
|
import java.util.regex.Matcher;
|
||
|
import java.util.regex.Pattern;
|
||
|
|
||
|
/**
|
||
|
* Rect holds four integer coordinates for a rectangle. The rectangle is
|
||
|
* represented by the coordinates of its 4 edges (left, top, right bottom).
|
||
|
* These fields can be accessed directly. Use width() and height() to retrieve
|
||
|
* the rectangle's width and height. Note: most methods do not check to see that
|
||
|
* the coordinates are sorted correctly (i.e. left <= right and top <= bottom).
|
||
|
* <p>
|
||
|
* Note that the right and bottom coordinates are exclusive. This means a Rect
|
||
|
* being drawn untransformed onto a {@link android.graphics.Canvas} will draw
|
||
|
* into the column and row described by its left and top coordinates, but not
|
||
|
* those of its bottom and right.
|
||
|
*/
|
||
|
@android.ravenwood.annotation.RavenwoodKeepWholeClass
|
||
|
public final class Rect implements Parcelable {
|
||
|
public int left;
|
||
|
public int top;
|
||
|
public int right;
|
||
|
public int bottom;
|
||
|
|
||
|
/**
|
||
|
* A helper class for flattened rectange pattern recognition. A separate
|
||
|
* class to avoid an initialization dependency on a regular expression
|
||
|
* causing Rect to not be initializable with an ahead-of-time compilation
|
||
|
* scheme.
|
||
|
*/
|
||
|
private static final class UnflattenHelper {
|
||
|
private static final Pattern FLATTENED_PATTERN = Pattern.compile(
|
||
|
"(-?\\d+) (-?\\d+) (-?\\d+) (-?\\d+)");
|
||
|
|
||
|
static Matcher getMatcher(String str) {
|
||
|
return FLATTENED_PATTERN.matcher(str);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create a new empty Rect. All coordinates are initialized to 0.
|
||
|
*/
|
||
|
public Rect() {}
|
||
|
|
||
|
/**
|
||
|
* Create a new rectangle with the specified coordinates. Note: no range
|
||
|
* checking is performed, so the caller must ensure that left <= right and
|
||
|
* top <= bottom.
|
||
|
*
|
||
|
* @param left The X coordinate of the left side of the rectangle
|
||
|
* @param top The Y coordinate of the top of the rectangle
|
||
|
* @param right The X coordinate of the right side of the rectangle
|
||
|
* @param bottom The Y coordinate of the bottom of the rectangle
|
||
|
*/
|
||
|
public Rect(int left, int top, int right, int bottom) {
|
||
|
this.left = left;
|
||
|
this.top = top;
|
||
|
this.right = right;
|
||
|
this.bottom = bottom;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create a new rectangle, initialized with the values in the specified
|
||
|
* rectangle (which is left unmodified).
|
||
|
*
|
||
|
* @param r The rectangle whose coordinates are copied into the new
|
||
|
* rectangle.
|
||
|
*/
|
||
|
public Rect(@Nullable Rect r) {
|
||
|
if (r == null) {
|
||
|
left = top = right = bottom = 0;
|
||
|
} else {
|
||
|
left = r.left;
|
||
|
top = r.top;
|
||
|
right = r.right;
|
||
|
bottom = r.bottom;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @hide
|
||
|
*/
|
||
|
public Rect(@Nullable Insets r) {
|
||
|
if (r == null) {
|
||
|
left = top = right = bottom = 0;
|
||
|
} else {
|
||
|
left = r.left;
|
||
|
top = r.top;
|
||
|
right = r.right;
|
||
|
bottom = r.bottom;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns a copy of {@code r} if {@code r} is not {@code null}, or {@code null} otherwise.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
@Nullable
|
||
|
public static Rect copyOrNull(@Nullable Rect r) {
|
||
|
return r == null ? null : new Rect(r);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public boolean equals(Object o) {
|
||
|
if (this == o) return true;
|
||
|
if (o == null || getClass() != o.getClass()) return false;
|
||
|
|
||
|
Rect r = (Rect) o;
|
||
|
return left == r.left && top == r.top && right == r.right && bottom == r.bottom;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int hashCode() {
|
||
|
int result = left;
|
||
|
result = 31 * result + top;
|
||
|
result = 31 * result + right;
|
||
|
result = 31 * result + bottom;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public String toString() {
|
||
|
StringBuilder sb = new StringBuilder(32);
|
||
|
sb.append("Rect("); sb.append(left); sb.append(", ");
|
||
|
sb.append(top); sb.append(" - "); sb.append(right);
|
||
|
sb.append(", "); sb.append(bottom); sb.append(")");
|
||
|
return sb.toString();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return a string representation of the rectangle in a compact form.
|
||
|
*/
|
||
|
@NonNull
|
||
|
public String toShortString() {
|
||
|
return toShortString(new StringBuilder(32));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return a string representation of the rectangle in a compact form.
|
||
|
* @hide
|
||
|
*/
|
||
|
@NonNull
|
||
|
public String toShortString(@NonNull StringBuilder sb) {
|
||
|
sb.setLength(0);
|
||
|
sb.append('['); sb.append(left); sb.append(',');
|
||
|
sb.append(top); sb.append("]["); sb.append(right);
|
||
|
sb.append(','); sb.append(bottom); sb.append(']');
|
||
|
return sb.toString();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return a string representation of the rectangle in a well-defined format.
|
||
|
*
|
||
|
* <p>You can later recover the Rect from this string through
|
||
|
* {@link #unflattenFromString(String)}.
|
||
|
*
|
||
|
* @return Returns a new String of the form "left top right bottom"
|
||
|
*/
|
||
|
@NonNull
|
||
|
public String flattenToString() {
|
||
|
StringBuilder sb = new StringBuilder(32);
|
||
|
// WARNING: Do not change the format of this string, it must be
|
||
|
// preserved because Rects are saved in this flattened format.
|
||
|
sb.append(left);
|
||
|
sb.append(' ');
|
||
|
sb.append(top);
|
||
|
sb.append(' ');
|
||
|
sb.append(right);
|
||
|
sb.append(' ');
|
||
|
sb.append(bottom);
|
||
|
return sb.toString();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns a Rect from a string of the form returned by {@link #flattenToString},
|
||
|
* or null if the string is not of that form.
|
||
|
*/
|
||
|
@Nullable
|
||
|
public static Rect unflattenFromString(@Nullable String str) {
|
||
|
if (TextUtils.isEmpty(str)) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
Matcher matcher = UnflattenHelper.getMatcher(str);
|
||
|
if (!matcher.matches()) {
|
||
|
return null;
|
||
|
}
|
||
|
return new Rect(Integer.parseInt(matcher.group(1)),
|
||
|
Integer.parseInt(matcher.group(2)),
|
||
|
Integer.parseInt(matcher.group(3)),
|
||
|
Integer.parseInt(matcher.group(4)));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Print short representation to given writer.
|
||
|
* @hide
|
||
|
*/
|
||
|
@UnsupportedAppUsage
|
||
|
public void printShortString(@NonNull PrintWriter pw) {
|
||
|
pw.print('['); pw.print(left); pw.print(',');
|
||
|
pw.print(top); pw.print("]["); pw.print(right);
|
||
|
pw.print(','); pw.print(bottom); pw.print(']');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Write to a protocol buffer output stream.
|
||
|
* Protocol buffer message definition at {@link android.graphics.RectProto}
|
||
|
*
|
||
|
* @param protoOutputStream Stream to write the Rect object to.
|
||
|
* @param fieldId Field Id of the Rect as defined in the parent message
|
||
|
* @hide
|
||
|
*/
|
||
|
public void dumpDebug(@NonNull ProtoOutputStream protoOutputStream, long fieldId) {
|
||
|
final long token = protoOutputStream.start(fieldId);
|
||
|
protoOutputStream.write(RectProto.LEFT, left);
|
||
|
protoOutputStream.write(RectProto.TOP, top);
|
||
|
protoOutputStream.write(RectProto.RIGHT, right);
|
||
|
protoOutputStream.write(RectProto.BOTTOM, bottom);
|
||
|
protoOutputStream.end(token);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Read from a protocol buffer input stream.
|
||
|
* Protocol buffer message definition at {@link android.graphics.RectProto}
|
||
|
*
|
||
|
* @param proto Stream to read the Rect object from.
|
||
|
* @param fieldId Field Id of the Rect as defined in the parent message
|
||
|
* @hide
|
||
|
*/
|
||
|
public void readFromProto(@NonNull ProtoInputStream proto, long fieldId) throws IOException,
|
||
|
WireTypeMismatchException {
|
||
|
final long token = proto.start(fieldId);
|
||
|
try {
|
||
|
while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
|
||
|
switch (proto.getFieldNumber()) {
|
||
|
case (int) RectProto.LEFT:
|
||
|
left = proto.readInt(RectProto.LEFT);
|
||
|
break;
|
||
|
case (int) RectProto.TOP:
|
||
|
top = proto.readInt(RectProto.TOP);
|
||
|
break;
|
||
|
case (int) RectProto.RIGHT:
|
||
|
right = proto.readInt(RectProto.RIGHT);
|
||
|
break;
|
||
|
case (int) RectProto.BOTTOM:
|
||
|
bottom = proto.readInt(RectProto.BOTTOM);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
} finally {
|
||
|
// Let caller handle any exceptions
|
||
|
proto.end(token);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns true if the rectangle is empty (left >= right or top >= bottom)
|
||
|
*/
|
||
|
public final boolean isEmpty() {
|
||
|
return left >= right || top >= bottom;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return {@code true} if the rectangle is valid (left <= right and top <= bottom).
|
||
|
* @hide
|
||
|
*/
|
||
|
public boolean isValid() {
|
||
|
return left <= right && top <= bottom;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return the rectangle's width. This does not check for a valid rectangle
|
||
|
* (i.e. left <= right) so the result may be negative.
|
||
|
*/
|
||
|
public final int width() {
|
||
|
return right - left;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return the rectangle's height. This does not check for a valid rectangle
|
||
|
* (i.e. top <= bottom) so the result may be negative.
|
||
|
*/
|
||
|
public final int height() {
|
||
|
return bottom - top;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return the horizontal center of the rectangle. If the computed value
|
||
|
* is fractional, this method returns the largest integer that is
|
||
|
* less than the computed value.
|
||
|
*/
|
||
|
public final int centerX() {
|
||
|
return (left + right) >> 1;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return the vertical center of the rectangle. If the computed value
|
||
|
* is fractional, this method returns the largest integer that is
|
||
|
* less than the computed value.
|
||
|
*/
|
||
|
public final int centerY() {
|
||
|
return (top + bottom) >> 1;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return the exact horizontal center of the rectangle as a float.
|
||
|
*/
|
||
|
public final float exactCenterX() {
|
||
|
return (left + right) * 0.5f;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return the exact vertical center of the rectangle as a float.
|
||
|
*/
|
||
|
public final float exactCenterY() {
|
||
|
return (top + bottom) * 0.5f;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the rectangle to (0,0,0,0)
|
||
|
*/
|
||
|
public void setEmpty() {
|
||
|
left = right = top = bottom = 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the rectangle's coordinates to the specified values. Note: no range
|
||
|
* checking is performed, so it is up to the caller to ensure that
|
||
|
* left <= right and top <= bottom.
|
||
|
*
|
||
|
* @param left The X coordinate of the left side of the rectangle
|
||
|
* @param top The Y coordinate of the top of the rectangle
|
||
|
* @param right The X coordinate of the right side of the rectangle
|
||
|
* @param bottom The Y coordinate of the bottom of the rectangle
|
||
|
*/
|
||
|
public void set(int left, int top, int right, int bottom) {
|
||
|
this.left = left;
|
||
|
this.top = top;
|
||
|
this.right = right;
|
||
|
this.bottom = bottom;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Copy the coordinates from src into this rectangle.
|
||
|
*
|
||
|
* @param src The rectangle whose coordinates are copied into this
|
||
|
* rectangle.
|
||
|
*/
|
||
|
public void set(@NonNull Rect src) {
|
||
|
this.left = src.left;
|
||
|
this.top = src.top;
|
||
|
this.right = src.right;
|
||
|
this.bottom = src.bottom;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Offset the rectangle by adding dx to its left and right coordinates, and
|
||
|
* adding dy to its top and bottom coordinates.
|
||
|
*
|
||
|
* @param dx The amount to add to the rectangle's left and right coordinates
|
||
|
* @param dy The amount to add to the rectangle's top and bottom coordinates
|
||
|
*/
|
||
|
public void offset(int dx, int dy) {
|
||
|
left += dx;
|
||
|
top += dy;
|
||
|
right += dx;
|
||
|
bottom += dy;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Offset the rectangle to a specific (left, top) position,
|
||
|
* keeping its width and height the same.
|
||
|
*
|
||
|
* @param newLeft The new "left" coordinate for the rectangle
|
||
|
* @param newTop The new "top" coordinate for the rectangle
|
||
|
*/
|
||
|
public void offsetTo(int newLeft, int newTop) {
|
||
|
right += newLeft - left;
|
||
|
bottom += newTop - top;
|
||
|
left = newLeft;
|
||
|
top = newTop;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Inset the rectangle by (dx,dy). If dx is positive, then the sides are
|
||
|
* moved inwards, making the rectangle narrower. If dx is negative, then the
|
||
|
* sides are moved outwards, making the rectangle wider. The same holds true
|
||
|
* for dy and the top and bottom.
|
||
|
*
|
||
|
* @param dx The amount to add(subtract) from the rectangle's left(right)
|
||
|
* @param dy The amount to add(subtract) from the rectangle's top(bottom)
|
||
|
*/
|
||
|
public void inset(int dx, int dy) {
|
||
|
left += dx;
|
||
|
top += dy;
|
||
|
right -= dx;
|
||
|
bottom -= dy;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Insets the rectangle on all sides specified by the dimensions of the {@code insets}
|
||
|
* rectangle.
|
||
|
* @hide
|
||
|
* @param insets The rectangle specifying the insets on all side.
|
||
|
*/
|
||
|
public void inset(@NonNull Rect insets) {
|
||
|
left += insets.left;
|
||
|
top += insets.top;
|
||
|
right -= insets.right;
|
||
|
bottom -= insets.bottom;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Insets the rectangle on all sides specified by the dimensions of {@code insets}.
|
||
|
*
|
||
|
* @param insets The insets to inset the rect by.
|
||
|
*/
|
||
|
public void inset(@NonNull Insets insets) {
|
||
|
left += insets.left;
|
||
|
top += insets.top;
|
||
|
right -= insets.right;
|
||
|
bottom -= insets.bottom;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Insets the rectangle on all sides specified by the insets.
|
||
|
*
|
||
|
* @param left The amount to add from the rectangle's left
|
||
|
* @param top The amount to add from the rectangle's top
|
||
|
* @param right The amount to subtract from the rectangle's right
|
||
|
* @param bottom The amount to subtract from the rectangle's bottom
|
||
|
*/
|
||
|
public void inset(int left, int top, int right, int bottom) {
|
||
|
this.left += left;
|
||
|
this.top += top;
|
||
|
this.right -= right;
|
||
|
this.bottom -= bottom;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns true if (x,y) is inside the rectangle. The left and top are
|
||
|
* considered to be inside, while the right and bottom are not. This means
|
||
|
* that for a x,y to be contained: left <= x < right and top <= y < bottom.
|
||
|
* An empty rectangle never contains any point.
|
||
|
*
|
||
|
* @param x The X coordinate of the point being tested for containment
|
||
|
* @param y The Y coordinate of the point being tested for containment
|
||
|
* @return true iff (x,y) are contained by the rectangle, where containment
|
||
|
* means left <= x < right and top <= y < bottom
|
||
|
*/
|
||
|
public boolean contains(int x, int y) {
|
||
|
return left < right && top < bottom // check for empty first
|
||
|
&& x >= left && x < right && y >= top && y < bottom;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns true iff the 4 specified sides of a rectangle are inside or equal
|
||
|
* to this rectangle. i.e. is this rectangle a superset of the specified
|
||
|
* rectangle. An empty rectangle never contains another rectangle.
|
||
|
*
|
||
|
* @param left The left side of the rectangle being tested for containment
|
||
|
* @param top The top of the rectangle being tested for containment
|
||
|
* @param right The right side of the rectangle being tested for containment
|
||
|
* @param bottom The bottom of the rectangle being tested for containment
|
||
|
* @return true iff the 4 specified sides of a rectangle are inside or
|
||
|
* equal to this rectangle
|
||
|
*/
|
||
|
public boolean contains(int left, int top, int right, int bottom) {
|
||
|
// check for empty first
|
||
|
return this.left < this.right && this.top < this.bottom
|
||
|
// now check for containment
|
||
|
&& this.left <= left && this.top <= top
|
||
|
&& this.right >= right && this.bottom >= bottom;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns true iff the specified rectangle r is inside or equal to this
|
||
|
* rectangle. An empty rectangle never contains another rectangle.
|
||
|
*
|
||
|
* @param r The rectangle being tested for containment.
|
||
|
* @return true iff the specified rectangle r is inside or equal to this
|
||
|
* rectangle
|
||
|
*/
|
||
|
public boolean contains(@NonNull Rect r) {
|
||
|
// check for empty first
|
||
|
return this.left < this.right && this.top < this.bottom
|
||
|
// now check for containment
|
||
|
&& left <= r.left && top <= r.top && right >= r.right && bottom >= r.bottom;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* If the rectangle specified by left,top,right,bottom intersects this
|
||
|
* rectangle, return true and set this rectangle to that intersection,
|
||
|
* otherwise return false and do not change this rectangle. No check is
|
||
|
* performed to see if either rectangle is empty. Note: To just test for
|
||
|
* intersection, use {@link #intersects(Rect, Rect)}.
|
||
|
*
|
||
|
* @param left The left side of the rectangle being intersected with this
|
||
|
* rectangle
|
||
|
* @param top The top of the rectangle being intersected with this rectangle
|
||
|
* @param right The right side of the rectangle being intersected with this
|
||
|
* rectangle.
|
||
|
* @param bottom The bottom of the rectangle being intersected with this
|
||
|
* rectangle.
|
||
|
* @return true if the specified rectangle and this rectangle intersect
|
||
|
* (and this rectangle is then set to that intersection) else
|
||
|
* return false and do not change this rectangle.
|
||
|
*/
|
||
|
@CheckResult
|
||
|
public boolean intersect(int left, int top, int right, int bottom) {
|
||
|
if (this.left < right && left < this.right && this.top < bottom && top < this.bottom) {
|
||
|
if (this.left < left) this.left = left;
|
||
|
if (this.top < top) this.top = top;
|
||
|
if (this.right > right) this.right = right;
|
||
|
if (this.bottom > bottom) this.bottom = bottom;
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* If the specified rectangle intersects this rectangle, return true and set
|
||
|
* this rectangle to that intersection, otherwise return false and do not
|
||
|
* change this rectangle. No check is performed to see if either rectangle
|
||
|
* is empty. To just test for intersection, use intersects()
|
||
|
*
|
||
|
* @param r The rectangle being intersected with this rectangle.
|
||
|
* @return true if the specified rectangle and this rectangle intersect
|
||
|
* (and this rectangle is then set to that intersection) else
|
||
|
* return false and do not change this rectangle.
|
||
|
*/
|
||
|
@CheckResult
|
||
|
public boolean intersect(@NonNull Rect r) {
|
||
|
return intersect(r.left, r.top, r.right, r.bottom);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* If the specified rectangle intersects this rectangle, set this rectangle to that
|
||
|
* intersection, otherwise set this rectangle to the empty rectangle.
|
||
|
* @see #inset(int, int, int, int) but without checking if the rects overlap.
|
||
|
* @hide
|
||
|
*/
|
||
|
public void intersectUnchecked(@NonNull Rect other) {
|
||
|
left = Math.max(left, other.left);
|
||
|
top = Math.max(top, other.top);
|
||
|
right = Math.min(right, other.right);
|
||
|
bottom = Math.min(bottom, other.bottom);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* If rectangles a and b intersect, return true and set this rectangle to
|
||
|
* that intersection, otherwise return false and do not change this
|
||
|
* rectangle. No check is performed to see if either rectangle is empty.
|
||
|
* To just test for intersection, use intersects()
|
||
|
*
|
||
|
* @param a The first rectangle being intersected with
|
||
|
* @param b The second rectangle being intersected with
|
||
|
* @return true iff the two specified rectangles intersect. If they do, set
|
||
|
* this rectangle to that intersection. If they do not, return
|
||
|
* false and do not change this rectangle.
|
||
|
*/
|
||
|
@CheckResult
|
||
|
public boolean setIntersect(@NonNull Rect a, @NonNull Rect b) {
|
||
|
if (a.left < b.right && b.left < a.right && a.top < b.bottom && b.top < a.bottom) {
|
||
|
left = Math.max(a.left, b.left);
|
||
|
top = Math.max(a.top, b.top);
|
||
|
right = Math.min(a.right, b.right);
|
||
|
bottom = Math.min(a.bottom, b.bottom);
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns true if this rectangle intersects the specified rectangle.
|
||
|
* In no event is this rectangle modified. No check is performed to see
|
||
|
* if either rectangle is empty. To record the intersection, use intersect()
|
||
|
* or setIntersect().
|
||
|
*
|
||
|
* @param left The left side of the rectangle being tested for intersection
|
||
|
* @param top The top of the rectangle being tested for intersection
|
||
|
* @param right The right side of the rectangle being tested for
|
||
|
* intersection
|
||
|
* @param bottom The bottom of the rectangle being tested for intersection
|
||
|
* @return true iff the specified rectangle intersects this rectangle. In
|
||
|
* no event is this rectangle modified.
|
||
|
*/
|
||
|
public boolean intersects(int left, int top, int right, int bottom) {
|
||
|
return this.left < right && left < this.right && this.top < bottom && top < this.bottom;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns true iff the two specified rectangles intersect. In no event are
|
||
|
* either of the rectangles modified. To record the intersection,
|
||
|
* use {@link #intersect(Rect)} or {@link #setIntersect(Rect, Rect)}.
|
||
|
*
|
||
|
* @param a The first rectangle being tested for intersection
|
||
|
* @param b The second rectangle being tested for intersection
|
||
|
* @return true iff the two specified rectangles intersect. In no event are
|
||
|
* either of the rectangles modified.
|
||
|
*/
|
||
|
public static boolean intersects(@NonNull Rect a, @NonNull Rect b) {
|
||
|
return a.left < b.right && b.left < a.right && a.top < b.bottom && b.top < a.bottom;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Update this Rect to enclose itself and the specified rectangle. If the
|
||
|
* specified rectangle is empty, nothing is done. If this rectangle is empty
|
||
|
* it is set to the specified rectangle.
|
||
|
*
|
||
|
* @param left The left edge being unioned with this rectangle
|
||
|
* @param top The top edge being unioned with this rectangle
|
||
|
* @param right The right edge being unioned with this rectangle
|
||
|
* @param bottom The bottom edge being unioned with this rectangle
|
||
|
*/
|
||
|
public void union(int left, int top, int right, int bottom) {
|
||
|
if ((left < right) && (top < bottom)) {
|
||
|
if ((this.left < this.right) && (this.top < this.bottom)) {
|
||
|
if (this.left > left) this.left = left;
|
||
|
if (this.top > top) this.top = top;
|
||
|
if (this.right < right) this.right = right;
|
||
|
if (this.bottom < bottom) this.bottom = bottom;
|
||
|
} else {
|
||
|
this.left = left;
|
||
|
this.top = top;
|
||
|
this.right = right;
|
||
|
this.bottom = bottom;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Update this Rect to enclose itself and the specified rectangle. If the
|
||
|
* specified rectangle is empty, nothing is done. If this rectangle is empty
|
||
|
* it is set to the specified rectangle.
|
||
|
*
|
||
|
* @param r The rectangle being unioned with this rectangle
|
||
|
*/
|
||
|
public void union(@NonNull Rect r) {
|
||
|
union(r.left, r.top, r.right, r.bottom);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Update this Rect to enclose itself and the [x,y] coordinate. There is no
|
||
|
* check to see that this rectangle is non-empty.
|
||
|
*
|
||
|
* @param x The x coordinate of the point to add to the rectangle
|
||
|
* @param y The y coordinate of the point to add to the rectangle
|
||
|
*/
|
||
|
public void union(int x, int y) {
|
||
|
if (x < left) {
|
||
|
left = x;
|
||
|
} else if (x > right) {
|
||
|
right = x;
|
||
|
}
|
||
|
if (y < top) {
|
||
|
top = y;
|
||
|
} else if (y > bottom) {
|
||
|
bottom = y;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Swap top/bottom or left/right if there are flipped (i.e. left > right
|
||
|
* and/or top > bottom). This can be called if
|
||
|
* the edges are computed separately, and may have crossed over each other.
|
||
|
* If the edges are already correct (i.e. left <= right and top <= bottom)
|
||
|
* then nothing is done.
|
||
|
*/
|
||
|
public void sort() {
|
||
|
if (left > right) {
|
||
|
int temp = left;
|
||
|
left = right;
|
||
|
right = temp;
|
||
|
}
|
||
|
if (top > bottom) {
|
||
|
int temp = top;
|
||
|
top = bottom;
|
||
|
bottom = temp;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Splits this Rect into small rects of the same width.
|
||
|
* @hide
|
||
|
*/
|
||
|
@TestApi
|
||
|
public void splitVertically(@NonNull Rect ...splits) {
|
||
|
final int count = splits.length;
|
||
|
final int splitWidth = width() / count;
|
||
|
for (int i = 0; i < count; i++) {
|
||
|
final Rect split = splits[i];
|
||
|
split.left = left + (splitWidth * i);
|
||
|
split.top = top;
|
||
|
split.right = split.left + splitWidth;
|
||
|
split.bottom = bottom;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Splits this Rect into small rects of the same height.
|
||
|
* @hide
|
||
|
*/
|
||
|
@TestApi
|
||
|
public void splitHorizontally(@NonNull Rect ...outSplits) {
|
||
|
final int count = outSplits.length;
|
||
|
final int splitHeight = height() / count;
|
||
|
for (int i = 0; i < count; i++) {
|
||
|
final Rect split = outSplits[i];
|
||
|
split.left = left;
|
||
|
split.top = top + (splitHeight * i);
|
||
|
split.right = right;
|
||
|
split.bottom = split.top + splitHeight;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Parcelable interface methods
|
||
|
*/
|
||
|
@Override
|
||
|
public int describeContents() {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Write this rectangle to the specified parcel. To restore a rectangle from
|
||
|
* a parcel, use readFromParcel()
|
||
|
* @param out The parcel to write the rectangle's coordinates into
|
||
|
*/
|
||
|
@Override
|
||
|
public void writeToParcel(Parcel out, int flags) {
|
||
|
out.writeInt(left);
|
||
|
out.writeInt(top);
|
||
|
out.writeInt(right);
|
||
|
out.writeInt(bottom);
|
||
|
}
|
||
|
|
||
|
public static final @android.annotation.NonNull Parcelable.Creator<Rect> CREATOR = new Parcelable.Creator<Rect>() {
|
||
|
/**
|
||
|
* Return a new rectangle from the data in the specified parcel.
|
||
|
*/
|
||
|
@Override
|
||
|
public Rect createFromParcel(Parcel in) {
|
||
|
Rect r = new Rect();
|
||
|
r.readFromParcel(in);
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return an array of rectangles of the specified size.
|
||
|
*/
|
||
|
@Override
|
||
|
public Rect[] newArray(int size) {
|
||
|
return new Rect[size];
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Set the rectangle's coordinates from the data stored in the specified
|
||
|
* parcel. To write a rectangle to a parcel, call writeToParcel().
|
||
|
*
|
||
|
* @param in The parcel to read the rectangle's coordinates from
|
||
|
*/
|
||
|
public void readFromParcel(@NonNull Parcel in) {
|
||
|
left = in.readInt();
|
||
|
top = in.readInt();
|
||
|
right = in.readInt();
|
||
|
bottom = in.readInt();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Scales up the rect by the given scale.
|
||
|
* @hide
|
||
|
*/
|
||
|
@UnsupportedAppUsage
|
||
|
public void scale(float scale) {
|
||
|
if (scale != 1.0f) {
|
||
|
left = (int) (left * scale + 0.5f);
|
||
|
top = (int) (top * scale + 0.5f);
|
||
|
right = (int) (right * scale + 0.5f);
|
||
|
bottom = (int) (bottom * scale + 0.5f);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|