diff --git a/api/current.txt b/api/current.txt index c962c0ae22fef..bc05bef11d821 100644 --- a/api/current.txt +++ b/api/current.txt @@ -10643,9 +10643,11 @@ package android.graphics { ctor public Outline(android.graphics.Outline); method public boolean canClip(); method public boolean isEmpty(); + method public boolean isFilled(); method public void set(android.graphics.Outline); method public void setConvexPath(android.graphics.Path); method public void setEmpty(); + method public void setFilled(boolean); method public void setOval(int, int, int, int); method public void setOval(android.graphics.Rect); method public void setRect(int, int, int, int); @@ -32731,6 +32733,7 @@ package android.view { method public int getNextFocusRightId(); method public int getNextFocusUpId(); method public android.view.View.OnFocusChangeListener getOnFocusChangeListener(); + method public android.view.ViewOutlineProvider getOutlineProvider(); method public int getOverScrollMode(); method public android.view.ViewOverlay getOverlay(); method public int getPaddingBottom(); @@ -32806,6 +32809,7 @@ package android.view { method public void invalidate(int, int, int, int); method public void invalidate(); method public void invalidateDrawable(android.graphics.drawable.Drawable); + method public void invalidateOutline(); method public boolean isAccessibilityFocused(); method public boolean isActivated(); method public boolean isAttachedToWindow(); @@ -33003,7 +33007,8 @@ package android.view { method public void setOnLongClickListener(android.view.View.OnLongClickListener); method public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener); method public void setOnTouchListener(android.view.View.OnTouchListener); - method public void setOutline(android.graphics.Outline); + method public deprecated void setOutline(android.graphics.Outline); + method public void setOutlineProvider(android.view.ViewOutlineProvider); method public void setOverScrollMode(int); method public void setPadding(int, int, int, int); method public void setPaddingRelative(int, int, int, int); @@ -33549,6 +33554,12 @@ package android.view { method public abstract void updateViewLayout(android.view.View, android.view.ViewGroup.LayoutParams); } + public abstract class ViewOutlineProvider { + ctor public ViewOutlineProvider(); + method public abstract boolean getOutline(android.view.View, android.graphics.Outline); + field public static final android.view.ViewOutlineProvider BACKGROUND; + } + public class ViewOverlay { method public void add(android.graphics.drawable.Drawable); method public void clear(); diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java index ddc185cfcef4e..0a1204d7f78a5 100644 --- a/core/java/android/view/RenderNode.java +++ b/core/java/android/view/RenderNode.java @@ -126,12 +126,10 @@ import android.graphics.Paint; public class RenderNode { /** * Flag used when calling - * {@link HardwareCanvas#drawDisplayList(RenderNode, android.graphics.Rect, int)} + * {@link HardwareCanvas#drawRenderNode(RenderNode, android.graphics.Rect, int)} * When this flag is set, draw operations lying outside of the bounds of the * display list will be culled early. It is recommeneded to always set this * flag. - * - * @hide */ public static final int FLAG_CLIP_CHILDREN = 0x1; @@ -140,37 +138,29 @@ public class RenderNode { /** * Indicates that the display list is done drawing. * - * @see HardwareCanvas#drawDisplayList(RenderNode, android.graphics.Rect, int) - * - * @hide + * @see HardwareCanvas#drawRenderNode(RenderNode, android.graphics.Rect, int) */ public static final int STATUS_DONE = 0x0; /** * Indicates that the display list needs another drawing pass. * - * @see HardwareCanvas#drawDisplayList(RenderNode, android.graphics.Rect, int) - * - * @hide + * @see HardwareCanvas#drawRenderNode(RenderNode, android.graphics.Rect, int) */ public static final int STATUS_DRAW = 0x1; /** * Indicates that the display list needs to re-execute its GL functors. * - * @see HardwareCanvas#drawDisplayList(RenderNode, android.graphics.Rect, int) + * @see HardwareCanvas#drawRenderNode(RenderNode, android.graphics.Rect, int) * @see HardwareCanvas#callDrawGLFunction(long) - * - * @hide */ public static final int STATUS_INVOKE = 0x2; /** * Indicates that the display list performed GL drawing operations. * - * @see HardwareCanvas#drawDisplayList(RenderNode, android.graphics.Rect, int) - * - * @hide + * @see HardwareCanvas#drawRenderNode(RenderNode, android.graphics.Rect, int) */ public static final int STATUS_DREW = 0x4; @@ -189,14 +179,12 @@ public class RenderNode { } /** - * Creates a new display list that can be used to record batches of - * drawing operations. + * Creates a new RenderNode that can be used to record batches of + * drawing operations, and store / apply render properties when drawn. * - * @param name The name of the display list, used for debugging purpose. May be null. + * @param name The name of the RenderNode, used for debugging purpose. May be null. * - * @return A new display list. - * - * @hide + * @return A new RenderNode. */ public static RenderNode create(String name) { return new RenderNode(name); @@ -263,8 +251,6 @@ public class RenderNode { * Reset native resources. This is called when cleaning up the state of display lists * during destruction of hardware resources, to ensure that we do not hold onto * obsolete resources after related resources are gone. - * - * @hide */ public void destroyDisplayListData() { if (!mValid) return; @@ -409,8 +395,6 @@ public class RenderNode { * for the matrix parameter. * * @param matrix The matrix, null indicates that the matrix should be cleared. - * - * @hide */ public boolean setAnimationMatrix(Matrix matrix) { return nSetAnimationMatrix(mNativeRenderNode, @@ -838,8 +822,6 @@ public class RenderNode { /** * Outputs the display list to the log. This method exists for use by * tools to output display lists for selected nodes to the log. - * - * @hide */ public void output() { nOutput(mNativeRenderNode); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 829e08971e930..d11d17e238936 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -2379,9 +2379,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ static final int PFLAG3_CALLED_SUPER = 0x10; - /** - * Flag indicating that a view's outline has been specifically defined. - */ + @Deprecated static final int PFLAG3_OUTLINE_DEFINED = 0x20; /** @@ -3275,11 +3273,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private int[] mDrawableState = null; - /** - * Stores the outline of the view, passed down to the DisplayList level for - * defining shadow shape. - */ + @Deprecated private Outline mOutline; + ViewOutlineProvider mOutlineProvider = ViewOutlineProvider.BACKGROUND; /** * Animator that automatically runs based on state changes. @@ -10757,24 +10753,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } - /** - * Sets the {@link Outline} of the view, which defines the shape of the shadow it - * casts, and enables outline clipping. - *
- * By default, a View queries its Outline from its background drawable, via - * {@link Drawable#getOutline(Outline)}. Manually setting the Outline with this method allows - * this behavior to be overridden. - *
- * If the outline is {@link Outline#isEmpty()} or is null,
- * shadows will not be cast.
- *
- * Only outlines that return true from {@link Outline#canClip()} may be used for clipping. - * - * @param outline The new outline of the view. - * - * @see #setClipToOutline(boolean) - * @see #getClipToOutline() - */ + @Deprecated public void setOutline(@Nullable Outline outline) { mPrivateFlags3 |= PFLAG3_OUTLINE_DEFINED; @@ -10798,7 +10777,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Note that this flag will only be respected if the View's Outline returns true from * {@link Outline#canClip()}. * - * @see #setOutline(Outline) + * @see #setOutlineProvider(ViewOutlineProvider) * @see #setClipToOutline(boolean) */ public final boolean getClipToOutline() { @@ -10811,7 +10790,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * Note that this flag will only be respected if the View's Outline returns true from * {@link Outline#canClip()}. * - * @see #setOutline(Outline) + * @see #setOutlineProvider(ViewOutlineProvider) * @see #getClipToOutline() */ public void setClipToOutline(boolean clipToOutline) { @@ -10821,25 +10800,74 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } - private void queryOutlineFromBackgroundIfUndefined() { - if ((mPrivateFlags3 & PFLAG3_OUTLINE_DEFINED) == 0) { - // Outline not currently defined, query from background - if (mOutline == null) { - mOutline = new Outline(); - } else { - //invalidate outline, to ensure background calculates it - mOutline.setEmpty(); - } - if (mBackground.getOutline(mOutline)) { - if (mOutline.isEmpty()) { - throw new IllegalStateException("Background drawable failed to build outline"); + /** + * Sets the {@link ViewOutlineProvider} of the view, which generates the Outline that defines + * the shape of the shadow it casts, and enables outline clipping. + *
+ * The default ViewOutlineProvider, {@link ViewOutlineProvider#BACKGROUND}, queries the Outline + * from the View's background drawable, via {@link Drawable#getOutline(Outline)}. Changing the + * outline provider with this method allows this behavior to be overridden. + *
+ * If the ViewOutlineProvider is null, if querying it for an outline returns false, + * or if the produced Outline is {@link Outline#isEmpty()}, shadows will not be cast. + *
+ * Only outlines that return true from {@link Outline#canClip()} may be used for clipping.
+ *
+ * @see #setClipToOutline(boolean)
+ * @see #getClipToOutline()
+ * @see #getOutlineProvider()
+ */
+ public void setOutlineProvider(ViewOutlineProvider provider) {
+ mOutlineProvider = provider;
+ invalidateOutline();
+ }
+
+ /**
+ * Returns the current {@link ViewOutlineProvider} of the view, which generates the Outline
+ * that defines the shape of the shadow it casts, and enables outline clipping.
+ *
+ * @see #setOutlineProvider(ViewOutlineProvider)
+ */
+ public ViewOutlineProvider getOutlineProvider() {
+ return mOutlineProvider;
+ }
+
+ /**
+ * Called to rebuild this View's Outline from its {@link ViewOutlineProvider outline provider}
+ *
+ * @see #setOutlineProvider(ViewOutlineProvider)
+ */
+ public void invalidateOutline() {
+ if ((mPrivateFlags3 & PFLAG3_OUTLINE_DEFINED) != 0) {
+ // TODO: remove this when removing old outline code
+ // setOutline() was called to manually set outline, ignore provider
+ return;
+ }
+
+ // Unattached views ignore this signal, and outline is recomputed in onAttachedToWindow()
+ if (mAttachInfo == null) return;
+
+ final Outline outline = mAttachInfo.mTmpOutline;
+ outline.setEmpty();
+
+ if (mOutlineProvider == null) {
+ // no provider, remove outline
+ mRenderNode.setOutline(null);
+ } else {
+ if (mOutlineProvider.getOutline(this, outline)) {
+ if (outline.isEmpty()) {
+ throw new IllegalStateException("Outline provider failed to build outline");
}
- mRenderNode.setOutline(mOutline);
+ // provider has provided
+ mRenderNode.setOutline(outline);
} else {
+ // provider failed to provide
mRenderNode.setOutline(null);
}
- notifySubtreeAccessibilityStateChangedIfNeeded();
}
+
+ notifySubtreeAccessibilityStateChangedIfNeeded();
+ invalidateViewProperty(false, false);
}
/**
@@ -12670,6 +12698,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
resetSubtreeAccessibilityStateChanged();
+ invalidateOutline();
+
if (isFocused()) {
InputMethodManager imm = InputMethodManager.peekInstance();
imm.focusIn(this);
@@ -14967,7 +14997,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (mBackgroundSizeChanged) {
background.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
mBackgroundSizeChanged = false;
- queryOutlineFromBackgroundIfUndefined();
}
// Attempt to use a display list if requested.
@@ -15012,7 +15041,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @param renderNode Existing RenderNode, or {@code null}
* @return A valid display list for the specified drawable
*/
- private RenderNode getDrawableRenderNode(Drawable drawable, RenderNode renderNode) {
+ private static RenderNode getDrawableRenderNode(Drawable drawable, RenderNode renderNode) {
if (renderNode == null) {
renderNode = RenderNode.create(drawable.getClass().getName());
}
@@ -15342,6 +15371,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mOverlay.getOverlayView().setRight(newWidth);
mOverlay.getOverlayView().setBottom(newHeight);
}
+ invalidateOutline();
}
/**
@@ -15378,9 +15408,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
invalidate(dirty.left + scrollX, dirty.top + scrollY,
dirty.right + scrollX, dirty.bottom + scrollY);
- if (drawable == mBackground) {
- queryOutlineFromBackgroundIfUndefined();
- }
+ invalidateOutline();
}
}
@@ -19959,6 +19987,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
final Transformation mTmpTransformation = new Transformation();
+ /**
+ * Temporary for use in querying outlines from OutlineProviders
+ */
+ final Outline mTmpOutline = new Outline();
+
/**
* Temporary list for use in collecting focusable descendents of a view.
*/
diff --git a/core/java/android/view/ViewOutlineProvider.java b/core/java/android/view/ViewOutlineProvider.java
new file mode 100644
index 0000000000000..9c154d5004e37
--- /dev/null
+++ b/core/java/android/view/ViewOutlineProvider.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.graphics.Outline;
+import android.graphics.drawable.Drawable;
+
+/**
+ * Interface by which a View builds its {@link Outline}, used for shadow casting and clipping.
+ */
+public abstract class ViewOutlineProvider {
+ /**
+ * Default outline provider for Views, which queries the Outline from the View's background,
+ * or returns false if the View does not have a background.
+ *
+ * @see Drawable#getOutline(Outline)
+ */
+ public static final ViewOutlineProvider BACKGROUND = new ViewOutlineProvider() {
+ @Override
+ public boolean getOutline(View view, Outline outline) {
+ Drawable background = view.getBackground();
+ if (background == null) {
+ // no background, no outline
+ return false;
+ }
+ return background.getOutline(outline);
+ }
+ };
+
+ /**
+ * Called to get the provider to populate the Outline.
+ *
+ * This method will be called by a View when its owned Drawables are invalidated, when the
+ * View's size changes, or if {@link View#invalidateOutline()} is called
+ * explicitly.
+ *
+ * @param view The view building the outline.
+ * @param outline The empty outline to be populated.
+ * @return true if this View should have an outline, else false. The outline must be
+ * populated by this method, and non-empty if true is returned.
+ */
+ public abstract boolean getOutline(View view, Outline outline);
+}
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index d87c3cb74ba7a..4bee5543e2ba7 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -17,7 +17,6 @@
package android.graphics;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.graphics.drawable.Drawable;
import android.view.View;
@@ -32,13 +31,15 @@ import android.view.View;
*/
public final class Outline {
/** @hide */
- public Rect mRect;
+ public Path mPath;
+ /** @hide */
+ public Rect mRect;
/** @hide */
public float mRadius;
/** @hide */
- public Path mPath;
+ public boolean mIsFilled;
/**
* Constructs an empty Outline. Call one of the setter methods to make
@@ -59,9 +60,10 @@ public final class Outline {
* @see #isEmpty()
*/
public void setEmpty() {
- mRadius = 0;
- mRect = null;
mPath = null;
+ mRect = null;
+ mRadius = 0;
+ mIsFilled = true;
}
/**
@@ -76,6 +78,36 @@ public final class Outline {
return mRect == null && mPath == null;
}
+
+ /**
+ * Returns whether the outline can be used to clip a View.
+ *
+ * Currently, only outlines that can be represented as a rectangle, circle, or round rect
+ * support clipping.
+ *
+ * @see {@link View#setClipToOutline(boolean)}
+ */
+ public boolean canClip() {
+ return !isEmpty() && mRect != null;
+ }
+
+ /**
+ * Sets whether the outline represents a fully opaque area.
+ *
+ * A filled outline is assumed, by the drawing system, to fully cover content beneath it,
+ * meaning content beneath may be optimized away.
+ */
+ public void setFilled(boolean isFilled) {
+ mIsFilled = isFilled;
+ }
+
+ /**
+ * Returns whether the outline represents a fully opaque area.
+ */
+ public boolean isFilled() {
+ return !isEmpty() && mIsFilled;
+ }
+
/**
* Replace the contents of this Outline with the contents of src.
*
@@ -96,6 +128,7 @@ public final class Outline {
mRect.set(src.mRect);
}
mRadius = src.mRadius;
+ mIsFilled = src.mIsFilled;
}
/**
@@ -122,6 +155,7 @@ public final class Outline {
mRect.set(left, top, right, bottom);
mRadius = radius;
mPath = null;
+ mIsFilled = true;
}
/**
@@ -140,10 +174,11 @@ public final class Outline {
setRoundRect(left, top, right, bottom, (bottom - top) / 2.0f);
return;
}
- mRect = null;
if (mPath == null) mPath = new Path();
mPath.reset();
mPath.addOval(left, top, right, bottom, Path.Direction.CW);
+ mRect = null;
+ mIsFilled = true;
}
/**
@@ -162,20 +197,9 @@ public final class Outline {
}
if (mPath == null) mPath = new Path();
+ mPath.set(convexPath);
mRect = null;
mRadius = -1.0f;
- mPath.set(convexPath);
- }
-
- /**
- * Returns whether the outline can be used to clip a View.
- *
- * Currently, only outlines that can be represented as a rectangle, circle, or round rect
- * support clipping.
- *
- * @see {@link View#setClipToOutline(boolean)}
- */
- public boolean canClip() {
- return mRect != null;
+ mIsFilled = true;
}
}
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 40b55a7f0afeb..c4cfd4afe9842 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -851,11 +851,10 @@ public abstract class Drawable {
}
/**
- * Called to get the drawable to populate the Outline.
+ * Called to get the drawable to populate the Outline that defines its drawing area.
*
- * This method will be called by a View on its background Drawable after bounds change, or its - * Drawable is invalidated, if the View's Outline isn't set explicitly. This allows the - * background Drawable to define the shape of the shadow cast by the View. + * This method is called by the default {@link android.view.ViewOutlineProvider} to define + * the outline of the View. *
* The default behavior defines the outline to be the bounding rectangle. Subclasses that wish * to convey a different shape must override this method. @@ -863,7 +862,7 @@ public abstract class Drawable { * @return true if this drawable actually has an outline, else false. The outline must be * populated by the drawable if true is returned. * - * @see View#setOutline(android.graphics.Outline) + * @see android.view.View#setOutlineProvider(android.view.ViewOutlineProvider) */ public boolean getOutline(@NonNull Outline outline) { outline.setRect(getBounds());