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