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