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 17package android.graphics.drawable.shapes; 18 19import android.annotation.Nullable; 20import android.graphics.Canvas; 21import android.graphics.Outline; 22import android.graphics.Paint; 23import android.graphics.Path; 24import 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 */ 34public 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 */ 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 80 public void draw(Canvas canvas, Paint paint) { 81 canvas.drawPath(mPath, paint); 82 } 83 84 @Override 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 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 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