1 /*
2  * Copyright 2018 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 package androidx.cardview.widget;
17 
18 import android.content.Context;
19 import android.content.res.ColorStateList;
20 import android.graphics.Canvas;
21 import android.graphics.Paint;
22 import android.graphics.Rect;
23 import android.graphics.RectF;
24 
25 import androidx.annotation.Nullable;
26 
27 class CardViewBaseImpl implements CardViewImpl {
28 
29     private final RectF mCornerRect = new RectF();
30 
31     @Override
initStatic()32     public void initStatic() {
33         // Draws a round rect using 7 draw operations. This is faster than using
34         // canvas.drawRoundRect before JBMR1 because API 11-16 used alpha mask textures to draw
35         // shapes.
36         RoundRectDrawableWithShadow.sRoundRectHelper =
37                 new RoundRectDrawableWithShadow.RoundRectHelper() {
38             @Override
39             public void drawRoundRect(Canvas canvas, RectF bounds, float cornerRadius,
40                     Paint paint) {
41                 final float twoRadius = cornerRadius * 2;
42                 final float innerWidth = bounds.width() - twoRadius - 1;
43                 final float innerHeight = bounds.height() - twoRadius - 1;
44                 if (cornerRadius >= 1f) {
45                     // increment corner radius to account for half pixels.
46                     float roundedCornerRadius = cornerRadius + .5f;
47                     mCornerRect.set(-roundedCornerRadius, -roundedCornerRadius, roundedCornerRadius,
48                             roundedCornerRadius);
49                     int saved = canvas.save();
50                     canvas.translate(bounds.left + roundedCornerRadius,
51                             bounds.top + roundedCornerRadius);
52                     canvas.drawArc(mCornerRect, 180, 90, true, paint);
53                     canvas.translate(innerWidth, 0);
54                     canvas.rotate(90);
55                     canvas.drawArc(mCornerRect, 180, 90, true, paint);
56                     canvas.translate(innerHeight, 0);
57                     canvas.rotate(90);
58                     canvas.drawArc(mCornerRect, 180, 90, true, paint);
59                     canvas.translate(innerWidth, 0);
60                     canvas.rotate(90);
61                     canvas.drawArc(mCornerRect, 180, 90, true, paint);
62                     canvas.restoreToCount(saved);
63                     //draw top and bottom pieces
64                     canvas.drawRect(bounds.left + roundedCornerRadius - 1f, bounds.top,
65                             bounds.right - roundedCornerRadius + 1f,
66                             bounds.top + roundedCornerRadius, paint);
67 
68                     canvas.drawRect(bounds.left + roundedCornerRadius - 1f,
69                             bounds.bottom - roundedCornerRadius,
70                             bounds.right - roundedCornerRadius + 1f, bounds.bottom, paint);
71                 }
72                 // center
73                 canvas.drawRect(bounds.left, bounds.top + cornerRadius,
74                         bounds.right, bounds.bottom - cornerRadius , paint);
75             }
76         };
77     }
78 
79     @Override
initialize(CardViewDelegate cardView, Context context, ColorStateList backgroundColor, float radius, float elevation, float maxElevation)80     public void initialize(CardViewDelegate cardView, Context context,
81             ColorStateList backgroundColor, float radius, float elevation, float maxElevation) {
82         RoundRectDrawableWithShadow background = createBackground(context, backgroundColor, radius,
83                 elevation, maxElevation);
84         background.setAddPaddingForCorners(cardView.getPreventCornerOverlap());
85         cardView.setCardBackground(background);
86         updatePadding(cardView);
87     }
88 
createBackground(Context context, ColorStateList backgroundColor, float radius, float elevation, float maxElevation)89     private RoundRectDrawableWithShadow createBackground(Context context,
90                     ColorStateList backgroundColor, float radius, float elevation,
91                     float maxElevation) {
92         return new RoundRectDrawableWithShadow(context.getResources(), backgroundColor, radius,
93                 elevation, maxElevation);
94     }
95 
96     @Override
updatePadding(CardViewDelegate cardView)97     public void updatePadding(CardViewDelegate cardView) {
98         Rect shadowPadding = new Rect();
99         getShadowBackground(cardView).getMaxShadowAndCornerPadding(shadowPadding);
100         cardView.setMinWidthHeightInternal((int) Math.ceil(getMinWidth(cardView)),
101                 (int) Math.ceil(getMinHeight(cardView)));
102         cardView.setShadowPadding(shadowPadding.left, shadowPadding.top,
103                 shadowPadding.right, shadowPadding.bottom);
104     }
105 
106     @Override
onCompatPaddingChanged(CardViewDelegate cardView)107     public void onCompatPaddingChanged(CardViewDelegate cardView) {
108         // NO OP
109     }
110 
111     @Override
onPreventCornerOverlapChanged(CardViewDelegate cardView)112     public void onPreventCornerOverlapChanged(CardViewDelegate cardView) {
113         getShadowBackground(cardView).setAddPaddingForCorners(cardView.getPreventCornerOverlap());
114         updatePadding(cardView);
115     }
116 
117     @Override
setBackgroundColor(CardViewDelegate cardView, @Nullable ColorStateList color)118     public void setBackgroundColor(CardViewDelegate cardView, @Nullable ColorStateList color) {
119         getShadowBackground(cardView).setColor(color);
120     }
121 
122     @Override
getBackgroundColor(CardViewDelegate cardView)123     public ColorStateList getBackgroundColor(CardViewDelegate cardView) {
124         return getShadowBackground(cardView).getColor();
125     }
126 
127     @Override
setRadius(CardViewDelegate cardView, float radius)128     public void setRadius(CardViewDelegate cardView, float radius) {
129         getShadowBackground(cardView).setCornerRadius(radius);
130         updatePadding(cardView);
131     }
132 
133     @Override
getRadius(CardViewDelegate cardView)134     public float getRadius(CardViewDelegate cardView) {
135         return getShadowBackground(cardView).getCornerRadius();
136     }
137 
138     @Override
setElevation(CardViewDelegate cardView, float elevation)139     public void setElevation(CardViewDelegate cardView, float elevation) {
140         getShadowBackground(cardView).setShadowSize(elevation);
141     }
142 
143     @Override
getElevation(CardViewDelegate cardView)144     public float getElevation(CardViewDelegate cardView) {
145         return getShadowBackground(cardView).getShadowSize();
146     }
147 
148     @Override
setMaxElevation(CardViewDelegate cardView, float maxElevation)149     public void setMaxElevation(CardViewDelegate cardView, float maxElevation) {
150         getShadowBackground(cardView).setMaxShadowSize(maxElevation);
151         updatePadding(cardView);
152     }
153 
154     @Override
getMaxElevation(CardViewDelegate cardView)155     public float getMaxElevation(CardViewDelegate cardView) {
156         return getShadowBackground(cardView).getMaxShadowSize();
157     }
158 
159     @Override
getMinWidth(CardViewDelegate cardView)160     public float getMinWidth(CardViewDelegate cardView) {
161         return getShadowBackground(cardView).getMinWidth();
162     }
163 
164     @Override
getMinHeight(CardViewDelegate cardView)165     public float getMinHeight(CardViewDelegate cardView) {
166         return getShadowBackground(cardView).getMinHeight();
167     }
168 
getShadowBackground(CardViewDelegate cardView)169     private RoundRectDrawableWithShadow getShadowBackground(CardViewDelegate cardView) {
170         return ((RoundRectDrawableWithShadow) cardView.getCardBackground());
171     }
172 }
173