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.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
     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 
     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 
     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
    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 
    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.
    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 
    199         public Shadow(BufferedImage shadow, int shadowWidth) {
    200             mShadow = shadow;
    201             mShadowWidth = shadowWidth;
    202         }
    203 
    204     }
    205 }
    206