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.layoutlib.bridge.android.BridgeContext; 20 import com.android.resources.Density; 21 import com.android.tools.layoutlib.annotations.LayoutlibDelegate; 22 23 import android.content.Context; 24 import android.graphics.Bitmap; 25 import android.graphics.Bitmap_Delegate; 26 import android.graphics.Canvas; 27 import android.graphics.Outline; 28 import android.graphics.Path_Delegate; 29 import android.graphics.Rect; 30 import android.view.animation.Transformation; 31 import android.view.shadow.HighQualityShadowPainter; 32 33 import java.awt.Graphics2D; 34 import java.awt.image.BufferedImage; 35 36 /** 37 * Delegate used to provide new implementation of a select few methods of {@link ViewGroup} 38 * <p/> 39 * Through the layoutlib_create tool, the original methods of ViewGroup have been replaced by calls 40 * to methods of the same name in this delegate class. 41 */ 42 public class ViewGroup_Delegate { 43 44 /** 45 * Overrides the original drawChild call in ViewGroup to draw the shadow. 46 */ 47 @LayoutlibDelegate drawChild(ViewGroup thisVG, Canvas canvas, View child, long drawingTime)48 /*package*/ static boolean drawChild(ViewGroup thisVG, Canvas canvas, View child, 49 long drawingTime) { 50 if (child.getZ() > thisVG.getZ()) { 51 // The background's bounds are set lazily. Make sure they are set correctly so that 52 // the outline obtained is correct. 53 child.setBackgroundBounds(); 54 ViewOutlineProvider outlineProvider = child.getOutlineProvider(); 55 if (outlineProvider != null) { 56 Outline outline = child.mAttachInfo.mTmpOutline; 57 outlineProvider.getOutline(child, outline); 58 if (outline.mPath != null || (outline.mRect != null && !outline.mRect.isEmpty())) { 59 int restoreTo = transformCanvas(thisVG, canvas, child); 60 drawShadow(thisVG, canvas, child, outline); 61 canvas.restoreToCount(restoreTo); 62 } 63 } 64 } 65 return thisVG.drawChild_Original(canvas, child, drawingTime); 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 boolean highQualityShadow = false; 71 boolean enableShadow = true; 72 float elevation = getElevation(child, parent); 73 Context bridgeContext = parent.getContext(); 74 if (bridgeContext instanceof BridgeContext) { 75 highQualityShadow = ((BridgeContext) bridgeContext).isHighQualityShadows(); 76 enableShadow = ((BridgeContext) bridgeContext).isShadowsEnabled(); 77 } 78 79 if (!enableShadow) { 80 return; 81 } 82 83 if(outline.mMode == Outline.MODE_ROUND_RECT && outline.mRect != null) { 84 if (highQualityShadow) { 85 float densityDpi = bridgeContext.getResources().getDisplayMetrics().densityDpi; 86 HighQualityShadowPainter.paintRectShadow( 87 parent, outline, elevation, canvas, child.getAlpha(), densityDpi); 88 } else { 89 RectShadowPainter.paintShadow(outline, elevation, canvas, child.getAlpha()); 90 } 91 return; 92 } 93 94 BufferedImage shadow = null; 95 if (outline.mPath != null) { 96 shadow = getPathShadow(outline, canvas, elevation, child.getAlpha()); 97 } 98 if (shadow == null) { 99 return; 100 } 101 Bitmap bitmap = Bitmap_Delegate.createBitmap(shadow, false, 102 Density.getEnum(canvas.getDensity())); 103 canvas.save(); 104 Rect clipBounds = canvas.getClipBounds(); 105 Rect newBounds = new Rect(clipBounds); 106 newBounds.inset((int)-elevation, (int)-elevation); 107 canvas.clipRectUnion(newBounds); 108 canvas.drawBitmap(bitmap, 0, 0, null); 109 canvas.restore(); 110 } 111 getElevation(View child, ViewGroup parent)112 private static float getElevation(View child, ViewGroup parent) { 113 return child.getZ() - parent.getZ(); 114 } 115 getPathShadow(Outline outline, Canvas canvas, float elevation, float alpha)116 private static BufferedImage getPathShadow(Outline outline, Canvas canvas, float elevation, 117 float alpha) { 118 Rect clipBounds = canvas.getClipBounds(); 119 if (clipBounds.isEmpty()) { 120 return null; 121 } 122 BufferedImage image = new BufferedImage(clipBounds.width(), clipBounds.height(), 123 BufferedImage.TYPE_INT_ARGB); 124 Graphics2D graphics = image.createGraphics(); 125 graphics.draw(Path_Delegate.getDelegate(outline.mPath.mNativePath).getJavaShape()); 126 graphics.dispose(); 127 return ShadowPainter.createDropShadow(image, (int) elevation, alpha); 128 } 129 130 // Copied from android.view.View#draw(Canvas, ViewGroup, long) and removed code paths 131 // which were never taken. Ideally, we should hook up the shadow code in the same method so 132 // that we don't have to transform the canvas twice. transformCanvas(ViewGroup thisVG, Canvas canvas, View child)133 private static int transformCanvas(ViewGroup thisVG, Canvas canvas, View child) { 134 final int restoreTo = canvas.save(); 135 final boolean childHasIdentityMatrix = child.hasIdentityMatrix(); 136 int flags = thisVG.mGroupFlags; 137 Transformation transformToApply = null; 138 boolean concatMatrix = false; 139 if ((flags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) { 140 final Transformation t = thisVG.getChildTransformation(); 141 final boolean hasTransform = thisVG.getChildStaticTransformation(child, t); 142 if (hasTransform) { 143 final int transformType = t.getTransformationType(); 144 transformToApply = transformType != Transformation.TYPE_IDENTITY ? t : null; 145 concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0; 146 } 147 } 148 concatMatrix |= childHasIdentityMatrix; 149 150 canvas.translate(child.mLeft, child.mTop); 151 float alpha = child.getAlpha() * child.getTransitionAlpha(); 152 153 if (transformToApply != null || alpha < 1 || !childHasIdentityMatrix) { 154 if (transformToApply != null || !childHasIdentityMatrix) { 155 156 if (transformToApply != null) { 157 if (concatMatrix) { 158 canvas.concat(transformToApply.getMatrix()); 159 } 160 } 161 if (!childHasIdentityMatrix) { 162 canvas.concat(child.getMatrix()); 163 } 164 165 } 166 } 167 return restoreTo; 168 } 169 } 170