blob: 475e0bb70f2b8eb30fb2b843449eb5d61a5b8ee5 [file] [log] [blame]
/*
* Copyright (C) 2007 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.drawable.shapes;
import android.annotation.Nullable;
import android.graphics.Canvas;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import java.util.Arrays;
import java.util.Objects;
/**
* Creates a rounded-corner rectangle. Optionally, an inset (rounded) rectangle
* can be included (to make a sort of "O" shape).
* <p>
* The rounded rectangle can be drawn to a Canvas with its own draw() method,
* but more graphical control is available if you instead pass
* the RoundRectShape to a {@link android.graphics.drawable.ShapeDrawable}.
*/
public class RoundRectShape extends RectShape {
private float[] mOuterRadii;
private RectF mInset;
private float[] mInnerRadii;
private RectF mInnerRect;
private Path mPath; // this is what we actually draw
/**
* RoundRectShape constructor.
* <p>
* Specifies an outer (round)rect and an optional inner (round)rect.
*
* @param outerRadii An array of 8 radius values, for the outer roundrect.
* The first two floats are for the top-left corner
* (remaining pairs correspond clockwise). For no rounded
* corners on the outer rectangle, pass {@code null}.
* @param inset A RectF that specifies the distance from the inner
* rect to each side of the outer rect. For no inner, pass
* {@code null}.
* @param innerRadii An array of 8 radius values, for the inner roundrect.
* The first two floats are for the top-left corner
* (remaining pairs correspond clockwise). For no rounded
* corners on the inner rectangle, pass {@code null}. If
* inset parameter is {@code null}, this parameter is
* ignored.
*/
public RoundRectShape(@Nullable float[] outerRadii, @Nullable RectF inset,
@Nullable float[] innerRadii) {
if (outerRadii != null && outerRadii.length < 8) {
throw new ArrayIndexOutOfBoundsException("outer radii must have >= 8 values");
}
if (innerRadii != null && innerRadii.length < 8) {
throw new ArrayIndexOutOfBoundsException("inner radii must have >= 8 values");
}
mOuterRadii = outerRadii;
mInset = inset;
mInnerRadii = innerRadii;
if (inset != null) {
mInnerRect = new RectF();
}
mPath = new Path();
}
@Override
public void draw(Canvas canvas, Paint paint) {
canvas.drawPath(mPath, paint);
}
@Override
public void getOutline(Outline outline) {
if (mInnerRect != null) return; // have a hole, can't produce valid outline
float radius = 0;
if (mOuterRadii != null) {
radius = mOuterRadii[0];
for (int i = 1; i < 8; i++) {
if (mOuterRadii[i] != radius) {
// can't call simple constructors, use path
outline.setConvexPath(mPath);
return;
}
}
}
final RectF rect = rect();
outline.setRoundRect((int) Math.ceil(rect.left), (int) Math.ceil(rect.top),
(int) Math.floor(rect.right), (int) Math.floor(rect.bottom), radius);
}
@Override
protected void onResize(float w, float h) {
super.onResize(w, h);
RectF r = rect();
mPath.reset();
if (mOuterRadii != null) {
mPath.addRoundRect(r, mOuterRadii, Path.Direction.CW);
} else {
mPath.addRect(r, Path.Direction.CW);
}
if (mInnerRect != null) {
mInnerRect.set(r.left + mInset.left, r.top + mInset.top,
r.right - mInset.right, r.bottom - mInset.bottom);
if (mInnerRect.width() < w && mInnerRect.height() < h) {
if (mInnerRadii != null) {
mPath.addRoundRect(mInnerRect, mInnerRadii, Path.Direction.CCW);
} else {
mPath.addRect(mInnerRect, Path.Direction.CCW);
}
}
}
}
@Override
public RoundRectShape clone() throws CloneNotSupportedException {
final RoundRectShape shape = (RoundRectShape) super.clone();
shape.mOuterRadii = mOuterRadii != null ? mOuterRadii.clone() : null;
shape.mInnerRadii = mInnerRadii != null ? mInnerRadii.clone() : null;
shape.mInset = new RectF(mInset);
shape.mInnerRect = new RectF(mInnerRect);
shape.mPath = new Path(mPath);
return shape;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
RoundRectShape that = (RoundRectShape) o;
return Arrays.equals(mOuterRadii, that.mOuterRadii)
&& Objects.equals(mInset, that.mInset)
&& Arrays.equals(mInnerRadii, that.mInnerRadii)
&& Objects.equals(mInnerRect, that.mInnerRect)
&& Objects.equals(mPath, that.mPath);
}
@Override
public int hashCode() {
int result = Objects.hash(super.hashCode(), mInset, mInnerRect, mPath);
result = 31 * result + Arrays.hashCode(mOuterRadii);
result = 31 * result + Arrays.hashCode(mInnerRadii);
return result;
}
}