• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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.view;
18 
19 import com.android.annotations.NonNull;
20 import com.android.layoutlib.bridge.android.BridgeContext;
21 import com.android.resources.Density;
22 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
23 
24 import android.content.Context;
25 import android.graphics.Bitmap;
26 import android.graphics.Bitmap_Delegate;
27 import android.graphics.Canvas;
28 import android.graphics.Outline;
29 import android.graphics.Path_Delegate;
30 import android.graphics.Rect;
31 import android.graphics.Region.Op;
32 import android.util.DisplayMetrics;
33 import android.util.TypedValue;
34 import android.view.animation.Transformation;
35 
36 import java.awt.Graphics2D;
37 import java.awt.image.BufferedImage;
38 
39 /**
40  * Delegate used to provide new implementation of a select few methods of {@link ViewGroup}
41  * <p/>
42  * Through the layoutlib_create tool, the original  methods of ViewGroup have been replaced by calls
43  * to methods of the same name in this delegate class.
44  */
45 public class ViewGroup_Delegate {
46 
47     /**
48      * Overrides the original drawChild call in ViewGroup to draw the shadow.
49      */
50     @LayoutlibDelegate
drawChild(ViewGroup thisVG, Canvas canvas, View child, long drawingTime)51     /*package*/ static boolean drawChild(ViewGroup thisVG, Canvas canvas, View child,
52             long drawingTime) {
53         boolean retVal = thisVG.drawChild_Original(canvas, child, drawingTime);
54         if (child.getZ() > thisVG.getZ()) {
55             ViewOutlineProvider outlineProvider = child.getOutlineProvider();
56             Outline outline = new Outline();
57             outlineProvider.getOutline(child, outline);
58 
59             if (outline.mPath != null || (outline.mRect != null && !outline.mRect.isEmpty())) {
60                 int restoreTo = transformCanvas(thisVG, canvas, child);
61                 drawShadow(thisVG, canvas, child, outline);
62                 canvas.restoreToCount(restoreTo);
63             }
64         }
65         return retVal;
66     }
67 
drawShadow(ViewGroup parent, Canvas canvas, View child, Outline outline)68     private static void drawShadow(ViewGroup parent, Canvas canvas, View child,
69             Outline outline) {
70         BufferedImage shadow = null;
71         int x = 0;
72         if (outline.mRect != null) {
73             Shadow s = getRectShadow(parent, canvas, child, outline);
74             shadow = s.mShadow;
75             x = -s.mShadowWidth;
76         } else if (outline.mPath != null) {
77             shadow = getPathShadow(child, outline, canvas);
78         }
79         if (shadow == null) {
80             return;
81         }
82         Bitmap bitmap = Bitmap_Delegate.createBitmap(shadow, false,
83                 Density.getEnum(canvas.getDensity()));
84         Rect clipBounds = canvas.getClipBounds();
85         Rect newBounds = new Rect(clipBounds);
86         newBounds.left = newBounds.left + x;
87         canvas.clipRect(newBounds, Op.REPLACE);
88         canvas.drawBitmap(bitmap, x, 0, null);
89         canvas.clipRect(clipBounds, Op.REPLACE);
90     }
91 
getRectShadow(ViewGroup parent, Canvas canvas, View child, Outline outline)92     private static Shadow getRectShadow(ViewGroup parent, Canvas canvas, View child,
93             Outline outline) {
94         BufferedImage shadow;
95         Rect clipBounds = canvas.getClipBounds();
96         if (clipBounds.isEmpty()) {
97             return null;
98         }
99         float height = child.getZ() - parent.getZ();
100         // Draw large shadow if difference in z index is more than 10dp
101         float largeShadowThreshold = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10f,
102                 getMetrics(child));
103         boolean largeShadow = height > largeShadowThreshold;
104         int shadowSize = largeShadow ? ShadowPainter.SHADOW_SIZE : ShadowPainter.SMALL_SHADOW_SIZE;
105         shadow = new BufferedImage(clipBounds.width() + shadowSize, clipBounds.height(),
106                 BufferedImage.TYPE_INT_ARGB);
107         Graphics2D graphics = shadow.createGraphics();
108         Rect rect = outline.mRect;
109         if (largeShadow) {
110             ShadowPainter.drawRectangleShadow(graphics,
111                     rect.left + shadowSize, rect.top, rect.width(), rect.height());
112         } else {
113             ShadowPainter.drawSmallRectangleShadow(graphics,
114                     rect.left + shadowSize, rect.top, rect.width(), rect.height());
115         }
116         graphics.dispose();
117         return new Shadow(shadow, shadowSize);
118     }
119 
120     @NonNull
getMetrics(View view)121     private static DisplayMetrics getMetrics(View view) {
122         Context context = view.getContext();
123         while (context instanceof ContextThemeWrapper) {
124             context = ((ContextThemeWrapper) context).getBaseContext();
125         }
126         if (context instanceof BridgeContext) {
127             return ((BridgeContext) context).getMetrics();
128         }
129         throw new RuntimeException("View " + view.getClass().getName() + " not created with the " +
130                 "right context");
131     }
132 
getPathShadow(View child, Outline outline, Canvas canvas)133     private static BufferedImage getPathShadow(View child, Outline outline, Canvas canvas) {
134         Rect clipBounds = canvas.getClipBounds();
135         BufferedImage image = new BufferedImage(clipBounds.width(), clipBounds.height(),
136                 BufferedImage.TYPE_INT_ARGB);
137         Graphics2D graphics = image.createGraphics();
138         graphics.draw(Path_Delegate.getDelegate(outline.mPath.mNativePath).getJavaShape());
139         graphics.dispose();
140         return ShadowPainter.createDropShadow(image, ((int) child.getZ()));
141     }
142 
143     // Copied from android.view.View#draw(Canvas, ViewGroup, long) and removed code paths
144     // which were never taken. Ideally, we should hook up the shadow code in the same method so
145     // that we don't have to transform the canvas twice.
transformCanvas(ViewGroup thisVG, Canvas canvas, View child)146     private static int transformCanvas(ViewGroup thisVG, Canvas canvas, View child) {
147         final int restoreTo = canvas.save();
148         final boolean childHasIdentityMatrix = child.hasIdentityMatrix();
149         int flags = thisVG.mGroupFlags;
150         Transformation transformToApply = null;
151         boolean concatMatrix = false;
152         if ((flags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
153             final Transformation t = thisVG.getChildTransformation();
154             final boolean hasTransform = thisVG.getChildStaticTransformation(child, t);
155             if (hasTransform) {
156                 final int transformType = t.getTransformationType();
157                 transformToApply = transformType != Transformation.TYPE_IDENTITY ? t : null;
158                 concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
159             }
160         }
161         concatMatrix |= childHasIdentityMatrix;
162 
163         child.computeScroll();
164         int sx = child.mScrollX;
165         int sy = child.mScrollY;
166 
167         canvas.translate(child.mLeft - sx, child.mTop - sy);
168         float alpha = child.getAlpha() * child.getTransitionAlpha();
169 
170         if (transformToApply != null || alpha < 1 || !childHasIdentityMatrix) {
171             if (transformToApply != null || !childHasIdentityMatrix) {
172                 int transX = -sx;
173                 int transY = -sy;
174 
175                 if (transformToApply != null) {
176                     if (concatMatrix) {
177                         // Undo the scroll translation, apply the transformation matrix,
178                         // then redo the scroll translate to get the correct result.
179                         canvas.translate(-transX, -transY);
180                         canvas.concat(transformToApply.getMatrix());
181                         canvas.translate(transX, transY);
182                     }
183                     if (!childHasIdentityMatrix) {
184                         canvas.translate(-transX, -transY);
185                         canvas.concat(child.getMatrix());
186                         canvas.translate(transX, transY);
187                     }
188                 }
189 
190             }
191         }
192         return restoreTo;
193     }
194 
195     private static class Shadow {
196         public BufferedImage mShadow;
197         public int mShadowWidth;
198 
Shadow(BufferedImage shadow, int shadowWidth)199         public Shadow(BufferedImage shadow, int shadowWidth) {
200             mShadow = shadow;
201             mShadowWidth = shadowWidth;
202         }
203 
204     }
205 }
206