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