OSDN Git Service

Make background projection a property of View
authorAlan Viverette <alanv@google.com>
Tue, 4 Feb 2014 19:17:32 +0000 (11:17 -0800)
committerAlan Viverette <alanv@google.com>
Tue, 4 Feb 2014 19:17:32 +0000 (11:17 -0800)
BUG: 12764584
Change-Id: Id80afd6d26c814d3fd551f6690d4a88c441b0b9f

core/java/android/view/View.java

index 239eda4..dc382a0 100644 (file)
@@ -2325,7 +2325,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
 
     /* End of masks for mPrivateFlags2 */
 
-    /* Masks for mPrivateFlags3 */
+    /**
+     * Masks for mPrivateFlags3, as generated by dumpFlags():
+     *
+     * |-------|-------|-------|-------|
+     *                                 1 PFLAG3_VIEW_IS_ANIMATING_TRANSFORM
+     *                                1  PFLAG3_VIEW_IS_ANIMATING_ALPHA
+     *                               1   PFLAG3_IS_LAID_OUT
+     *                              1    PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT
+     *                             1     PFLAG3_CALLED_SUPER
+     *                            1      PFLAG3_PROJECT_BACKGROUND
+     * |-------|-------|-------|-------|
+     */
 
     /**
      * Flag indicating that view has a transform animation set on it. This is used to track whether
@@ -2359,6 +2370,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
      */
     static final int PFLAG3_CALLED_SUPER = 0x10;
 
+    /**
+     * Flag indicating that the background of this view will be drawn into a
+     * display list and projected onto the closest parent projection surface.
+     */
+    static final int PFLAG3_PROJECT_BACKGROUND = 0x20;
 
     /* End of masks for mPrivateFlags3 */
 
@@ -3225,6 +3241,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     @ViewDebug.ExportedProperty(deepExport = true, prefix = "bg_")
     private Drawable mBackground;
 
+    /**
+     * Display list used for backgrounds.
+     * <p>
+     * When non-null and valid, this is expected to contain an up-to-date copy
+     * of the background drawable. It is cleared on temporary detach and reset
+     * on cleanup.
+     */
+    private DisplayList mBackgroundDisplayList;
+
     private int mBackgroundResource;
     private boolean mBackgroundSizeChanged;
 
@@ -3503,6 +3528,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     private Bitmap mDrawingCache;
     private Bitmap mUnscaledDrawingCache;
 
+    /**
+     * Display list used for the View content.
+     * <p>
+     * When non-null and valid, this is expected to contain an up-to-date copy
+     * of the View content. It is cleared on temporary detach and reset on
+     * cleanup.
+     */
     DisplayList mDisplayList;
 
     /**
@@ -13688,12 +13720,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
         if (mDisplayList != null) {
             mDisplayList.clear();
         }
+
+        if (mBackgroundDisplayList != null) {
+            mBackgroundDisplayList.clear();
+        }
     }
 
     private void resetDisplayList() {
         if (mDisplayList != null) {
             mDisplayList.reset();
         }
+
+        if (mBackgroundDisplayList != null) {
+            mBackgroundDisplayList.reset();
+        }
     }
 
     /**
@@ -14721,24 +14761,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
         int saveCount;
 
         if (!dirtyOpaque) {
-            final Drawable background = mBackground;
-            if (background != null) {
-                final int scrollX = mScrollX;
-                final int scrollY = mScrollY;
-
-                if (mBackgroundSizeChanged) {
-                    background.setBounds(0, 0,  mRight - mLeft, mBottom - mTop);
-                    mBackgroundSizeChanged = false;
-                }
-
-                if ((scrollX | scrollY) == 0) {
-                    background.draw(canvas);
-                } else {
-                    canvas.translate(scrollX, scrollY);
-                    background.draw(canvas);
-                    canvas.translate(-scrollX, -scrollY);
-                }
-            }
+            drawBackground(canvas);
         }
 
         // skip step 2 & 5 if possible (common case)
@@ -14905,6 +14928,90 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     }
 
     /**
+     * Draws the background onto the specified canvas.
+     *
+     * @param canvas Canvas on which to draw the background
+     */
+    private void drawBackground(Canvas canvas) {
+        final Drawable background = mBackground;
+        if (background == null) {
+            return;
+        }
+
+        if (mBackgroundSizeChanged) {
+            // We should see the background invalidate itself, but just to be
+            // careful we're going to clear the display list and force redraw.
+            mBackgroundDisplayList.clear();
+            background.setBounds(0, 0,  mRight - mLeft, mBottom - mTop);
+            mBackgroundSizeChanged = false;
+        }
+
+
+        // Attempt to use a display list if requested.
+        if (canvas != null && canvas.isHardwareAccelerated()) {
+            mBackgroundDisplayList = getDrawableDisplayList(background, mBackgroundDisplayList);
+
+            final DisplayList displayList = mBackgroundDisplayList;
+            if (displayList != null && displayList.isValid()) {
+                setBackgroundDisplayListProperties(displayList);
+                ((HardwareCanvas) canvas).drawDisplayList(displayList);
+                return;
+            }
+        }
+
+        final int scrollX = mScrollX;
+        final int scrollY = mScrollY;
+        if ((scrollX | scrollY) == 0) {
+            background.draw(canvas);
+        } else {
+            canvas.translate(scrollX, scrollY);
+            background.draw(canvas);
+            canvas.translate(-scrollX, -scrollY);
+        }
+    }
+
+    /**
+     * Set up background drawable display list properties.
+     *
+     * @param displayList Valid display list for the background drawable
+     */
+    private void setBackgroundDisplayListProperties(DisplayList displayList) {
+        displayList.setProjectBackwards((mPrivateFlags3 & PFLAG3_PROJECT_BACKGROUND) != 0);
+        displayList.setTranslationX(mScrollX);
+        displayList.setTranslationY(mScrollY);
+    }
+
+    /**
+     * Creates a new display list or updates the existing display list for the
+     * specified Drawable.
+     *
+     * @param drawable Drawable for which to create a display list
+     * @param displayList Existing display list, or {@code null}
+     * @return A valid display list for the specified drawable
+     */
+    private static DisplayList getDrawableDisplayList(Drawable drawable, DisplayList displayList) {
+        if (displayList != null && displayList.isValid()) {
+            return displayList;
+        }
+
+        if (displayList == null) {
+            displayList = DisplayList.create(drawable.getClass().getName());
+        }
+
+        final Rect bounds = drawable.getBounds();
+        final int width = bounds.width();
+        final int height = bounds.height();
+        final HardwareCanvas canvas = displayList.start(width, height);
+        drawable.draw(canvas);
+        displayList.end();
+
+        // Set up drawable properties that are view-independent.
+        displayList.setLeftTopRightBottom(bounds.left, bounds.top, bounds.right, bounds.bottom);
+        displayList.setClipToBounds(false);
+        return displayList;
+    }
+
+    /**
      * Returns the overlay for this view, creating it if it does not yet exist.
      * Adding drawables to the overlay will cause them to be displayed whenever
      * the view itself is redrawn. Objects in the overlay should be actively
@@ -15245,14 +15352,22 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
      *
      * @param drawable the drawable to invalidate
      */
+    @Override
     public void invalidateDrawable(Drawable drawable) {
         if (verifyDrawable(drawable)) {
-            final Rect dirty = drawable.getBounds();
-            final int scrollX = mScrollX;
-            final int scrollY = mScrollY;
+            if (drawable == mBackground && mBackgroundDisplayList != null) {
+                // If we're using a background display list, we only need to
+                // invalidate the display list and notify the parent to redraw.
+                mBackgroundDisplayList.clear();
+                invalidateViewProperty(true, false);
+            } else {
+                final Rect dirty = drawable.getBounds();
+                final int scrollX = mScrollX;
+                final int scrollY = mScrollY;
 
-            invalidate(dirty.left + scrollX, dirty.top + scrollY,
-                    dirty.right + scrollX, dirty.bottom + scrollY);
+                invalidate(dirty.left + scrollX, dirty.top + scrollY,
+                        dirty.right + scrollX, dirty.bottom + scrollY);
+            }
         }
     }
 
@@ -15399,7 +15514,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
      * @see Drawable#setState(int[])
      */
     protected void drawableStateChanged() {
-        Drawable d = mBackground;
+        final Drawable d = mBackground;
         if (d != null && d.isStateful()) {
             d.setState(getDrawableState());
         }