1 /* 2 * Copyright (C) 2007 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.drawable.shapes; 18 19 import android.annotation.Nullable; 20 import android.graphics.Canvas; 21 import android.graphics.Outline; 22 import android.graphics.Paint; 23 import android.graphics.Path; 24 import android.graphics.RectF; 25 26 /** 27 * Creates a rounded-corner rectangle. Optionally, an inset (rounded) rectangle 28 * can be included (to make a sort of "O" shape). 29 * <p> 30 * The rounded rectangle can be drawn to a Canvas with its own draw() method, 31 * but more graphical control is available if you instead pass 32 * the RoundRectShape to a {@link android.graphics.drawable.ShapeDrawable}. 33 */ 34 public class RoundRectShape extends RectShape { 35 private float[] mOuterRadii; 36 private RectF mInset; 37 private float[] mInnerRadii; 38 39 private RectF mInnerRect; 40 private Path mPath; // this is what we actually draw 41 42 /** 43 * RoundRectShape constructor. 44 * <p> 45 * Specifies an outer (round)rect and an optional inner (round)rect. 46 * 47 * @param outerRadii An array of 8 radius values, for the outer roundrect. 48 * The first two floats are for the top-left corner 49 * (remaining pairs correspond clockwise). For no rounded 50 * corners on the outer rectangle, pass {@code null}. 51 * @param inset A RectF that specifies the distance from the inner 52 * rect to each side of the outer rect. For no inner, pass 53 * {@code null}. 54 * @param innerRadii An array of 8 radius values, for the inner roundrect. 55 * The first two floats are for the top-left corner 56 * (remaining pairs correspond clockwise). For no rounded 57 * corners on the inner rectangle, pass {@code null}. If 58 * inset parameter is {@code null}, this parameter is 59 * ignored. 60 */ RoundRectShape(@ullable float[] outerRadii, @Nullable RectF inset, @Nullable float[] innerRadii)61 public RoundRectShape(@Nullable float[] outerRadii, @Nullable RectF inset, 62 @Nullable float[] innerRadii) { 63 if (outerRadii != null && outerRadii.length < 8) { 64 throw new ArrayIndexOutOfBoundsException("outer radii must have >= 8 values"); 65 } 66 if (innerRadii != null && innerRadii.length < 8) { 67 throw new ArrayIndexOutOfBoundsException("inner radii must have >= 8 values"); 68 } 69 mOuterRadii = outerRadii; 70 mInset = inset; 71 mInnerRadii = innerRadii; 72 73 if (inset != null) { 74 mInnerRect = new RectF(); 75 } 76 mPath = new Path(); 77 } 78 79 @Override draw(Canvas canvas, Paint paint)80 public void draw(Canvas canvas, Paint paint) { 81 canvas.drawPath(mPath, paint); 82 } 83 84 @Override getOutline(Outline outline)85 public void getOutline(Outline outline) { 86 if (mInnerRect != null) return; // have a hole, can't produce valid outline 87 88 float radius = 0; 89 if (mOuterRadii != null) { 90 radius = mOuterRadii[0]; 91 for (int i = 1; i < 8; i++) { 92 if (mOuterRadii[i] != radius) { 93 // can't call simple constructors, use path 94 outline.setConvexPath(mPath); 95 return; 96 } 97 } 98 } 99 100 final RectF rect = rect(); 101 outline.setRoundRect((int) Math.ceil(rect.left), (int) Math.ceil(rect.top), 102 (int) Math.floor(rect.right), (int) Math.floor(rect.bottom), radius); 103 } 104 105 @Override onResize(float w, float h)106 protected void onResize(float w, float h) { 107 super.onResize(w, h); 108 109 RectF r = rect(); 110 mPath.reset(); 111 112 if (mOuterRadii != null) { 113 mPath.addRoundRect(r, mOuterRadii, Path.Direction.CW); 114 } else { 115 mPath.addRect(r, Path.Direction.CW); 116 } 117 if (mInnerRect != null) { 118 mInnerRect.set(r.left + mInset.left, r.top + mInset.top, 119 r.right - mInset.right, r.bottom - mInset.bottom); 120 if (mInnerRect.width() < w && mInnerRect.height() < h) { 121 if (mInnerRadii != null) { 122 mPath.addRoundRect(mInnerRect, mInnerRadii, Path.Direction.CCW); 123 } else { 124 mPath.addRect(mInnerRect, Path.Direction.CCW); 125 } 126 } 127 } 128 } 129 130 @Override clone()131 public RoundRectShape clone() throws CloneNotSupportedException { 132 final RoundRectShape shape = (RoundRectShape) super.clone(); 133 shape.mOuterRadii = mOuterRadii != null ? mOuterRadii.clone() : null; 134 shape.mInnerRadii = mInnerRadii != null ? mInnerRadii.clone() : null; 135 shape.mInset = new RectF(mInset); 136 shape.mInnerRect = new RectF(mInnerRect); 137 shape.mPath = new Path(mPath); 138 return shape; 139 } 140 } 141