summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Chris Craik <ccraik@google.com> 2017-01-18 17:59:23 -0800
committer Chris Craik <ccraik@google.com> 2017-01-27 11:36:27 -0800
commit9de95db4f28f88c37b1443d20b308ce02407fd74 (patch)
tree0d82dae63c557a7928921fcccd1050beaf4a5a1c
parentfffa2eb0460dbb790d565b3e2a651b6a9fb7c9b9 (diff)
Replace invalidateChild/invalidateChildInParent
Fixes: 34361503 Test: CTS: android.view.cts.ViewGroupTest APCT: android.view.ViewInvalidateTest Simplify and unify HW accelerated invalidate/damage codepaths, since both simply walk up to ViewRootImpl and schedule a traversal. Adds a new overridable method 'onDescendantInvalidated' for observing subtree rendering updates. Change-Id: I7ef1f914c3411317692451787b3810b23e019591
-rw-r--r--api/current.txt9
-rw-r--r--api/system-current.txt9
-rw-r--r--api/test-current.txt9
-rw-r--r--core/java/android/view/View.java12
-rw-r--r--core/java/android/view/ViewGroup.java183
-rw-r--r--core/java/android/view/ViewOverlay.java18
-rw-r--r--core/java/android/view/ViewParent.java28
-rw-r--r--core/java/android/view/ViewRootImpl.java9
-rw-r--r--core/tests/coretests/src/android/view/ViewInvalidateTest.java24
9 files changed, 119 insertions, 182 deletions
diff --git a/api/current.txt b/api/current.txt
index 835ec534c6cf..f26b52b6c840 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -44641,8 +44641,8 @@ package android.view {
method public int getPersistentDrawingCache();
method public boolean getTouchscreenBlocksFocus();
method public int indexOfChild(android.view.View);
- method public final void invalidateChild(android.view.View, android.graphics.Rect);
- method public android.view.ViewParent invalidateChildInParent(int[], android.graphics.Rect);
+ method public final deprecated void invalidateChild(android.view.View, android.graphics.Rect);
+ method public deprecated android.view.ViewParent invalidateChildInParent(int[], android.graphics.Rect);
method public deprecated boolean isAlwaysDrawnWithCacheEnabled();
method public deprecated boolean isAnimationCacheEnabled();
method protected boolean isChildrenDrawingOrderEnabled();
@@ -44805,14 +44805,15 @@ package android.view {
method public abstract android.view.ViewParent getParentForAccessibility();
method public abstract int getTextAlignment();
method public abstract int getTextDirection();
- method public abstract void invalidateChild(android.view.View, android.graphics.Rect);
- method public abstract android.view.ViewParent invalidateChildInParent(int[], android.graphics.Rect);
+ method public abstract deprecated void invalidateChild(android.view.View, android.graphics.Rect);
+ method public abstract deprecated android.view.ViewParent invalidateChildInParent(int[], android.graphics.Rect);
method public abstract boolean isLayoutDirectionResolved();
method public abstract boolean isLayoutRequested();
method public abstract boolean isTextAlignmentResolved();
method public abstract boolean isTextDirectionResolved();
method public abstract android.view.View keyboardNavigationClusterSearch(android.view.View, int);
method public abstract void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int);
+ method public default void onDescendantInvalidated(android.view.View, android.view.View);
method public abstract boolean onNestedFling(android.view.View, float, float, boolean);
method public abstract boolean onNestedPreFling(android.view.View, float, float);
method public abstract boolean onNestedPrePerformAccessibilityAction(android.view.View, int, android.os.Bundle);
diff --git a/api/system-current.txt b/api/system-current.txt
index e7d441afb76f..dc6c1316a74e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -47999,8 +47999,8 @@ package android.view {
method public int getPersistentDrawingCache();
method public boolean getTouchscreenBlocksFocus();
method public int indexOfChild(android.view.View);
- method public final void invalidateChild(android.view.View, android.graphics.Rect);
- method public android.view.ViewParent invalidateChildInParent(int[], android.graphics.Rect);
+ method public final deprecated void invalidateChild(android.view.View, android.graphics.Rect);
+ method public deprecated android.view.ViewParent invalidateChildInParent(int[], android.graphics.Rect);
method public deprecated boolean isAlwaysDrawnWithCacheEnabled();
method public deprecated boolean isAnimationCacheEnabled();
method protected boolean isChildrenDrawingOrderEnabled();
@@ -48163,14 +48163,15 @@ package android.view {
method public abstract android.view.ViewParent getParentForAccessibility();
method public abstract int getTextAlignment();
method public abstract int getTextDirection();
- method public abstract void invalidateChild(android.view.View, android.graphics.Rect);
- method public abstract android.view.ViewParent invalidateChildInParent(int[], android.graphics.Rect);
+ method public abstract deprecated void invalidateChild(android.view.View, android.graphics.Rect);
+ method public abstract deprecated android.view.ViewParent invalidateChildInParent(int[], android.graphics.Rect);
method public abstract boolean isLayoutDirectionResolved();
method public abstract boolean isLayoutRequested();
method public abstract boolean isTextAlignmentResolved();
method public abstract boolean isTextDirectionResolved();
method public abstract android.view.View keyboardNavigationClusterSearch(android.view.View, int);
method public abstract void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int);
+ method public default void onDescendantInvalidated(android.view.View, android.view.View);
method public abstract boolean onNestedFling(android.view.View, float, float, boolean);
method public abstract boolean onNestedPreFling(android.view.View, float, float);
method public abstract boolean onNestedPrePerformAccessibilityAction(android.view.View, int, android.os.Bundle);
diff --git a/api/test-current.txt b/api/test-current.txt
index 243c49f668cb..d7fe3be4a55a 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -44937,8 +44937,8 @@ package android.view {
method public int getPersistentDrawingCache();
method public boolean getTouchscreenBlocksFocus();
method public int indexOfChild(android.view.View);
- method public final void invalidateChild(android.view.View, android.graphics.Rect);
- method public android.view.ViewParent invalidateChildInParent(int[], android.graphics.Rect);
+ method public final deprecated void invalidateChild(android.view.View, android.graphics.Rect);
+ method public deprecated android.view.ViewParent invalidateChildInParent(int[], android.graphics.Rect);
method public deprecated boolean isAlwaysDrawnWithCacheEnabled();
method public deprecated boolean isAnimationCacheEnabled();
method protected boolean isChildrenDrawingOrderEnabled();
@@ -45101,14 +45101,15 @@ package android.view {
method public abstract android.view.ViewParent getParentForAccessibility();
method public abstract int getTextAlignment();
method public abstract int getTextDirection();
- method public abstract void invalidateChild(android.view.View, android.graphics.Rect);
- method public abstract android.view.ViewParent invalidateChildInParent(int[], android.graphics.Rect);
+ method public abstract deprecated void invalidateChild(android.view.View, android.graphics.Rect);
+ method public abstract deprecated android.view.ViewParent invalidateChildInParent(int[], android.graphics.Rect);
method public abstract boolean isLayoutDirectionResolved();
method public abstract boolean isLayoutRequested();
method public abstract boolean isTextAlignmentResolved();
method public abstract boolean isTextDirectionResolved();
method public abstract android.view.View keyboardNavigationClusterSearch(android.view.View, int);
method public abstract void notifySubtreeAccessibilityStateChanged(android.view.View, android.view.View, int);
+ method public default void onDescendantInvalidated(android.view.View, android.view.View);
method public abstract boolean onNestedFling(android.view.View, float, float, boolean);
method public abstract boolean onNestedPreFling(android.view.View, float, float);
method public abstract boolean onNestedPrePerformAccessibilityAction(android.view.View, int, android.os.Bundle);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 547f7d8cb991..eaf08d0d3661 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -14334,16 +14334,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @hide
*/
protected void damageInParent() {
- final AttachInfo ai = mAttachInfo;
- final ViewParent p = mParent;
- if (p != null && ai != null) {
- final Rect r = ai.mTmpInvalRect;
- r.set(0, 0, mRight - mLeft, mBottom - mTop);
- if (mParent instanceof ViewGroup) {
- ((ViewGroup) mParent).damageChild(this, r);
- } else {
- mParent.invalidateChild(this, r);
- }
+ if (mParent != null && mAttachInfo != null) {
+ mParent.onDescendantInvalidated(this, this);
}
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index ba73c5f1be60..f1df7ba566e7 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -19,6 +19,7 @@ package android.view;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
import android.animation.LayoutTransition;
+import android.annotation.CallSuper;
import android.annotation.IdRes;
import android.annotation.NonNull;
import android.annotation.UiThread;
@@ -5372,100 +5373,60 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
}
- /**
- * HW-only, Rect-ignoring invalidation path.
- *
- * Returns false if this path was unable to complete successfully. This means
- * it hit a ViewParent it doesn't recognize and needs to fall back to calculating
- * damage area.
- *
- * Hardware acceleration ignores damage rectangles, since native computes damage for everything
- * drawn by HWUI (and SW layer / drawing cache doesn't keep track of damage area).
- *
- * Ignores opaque dirty optimizations, always using the full PFLAG_DIRTY flag.
- *
- * Ignores FLAG_OPTIMIZE_INVALIDATE, since we're not computing a rect,
- * so no point in optimizing that.
- * @hide
- */
- public boolean tryInvalidateChildHardware(View child) {
- final AttachInfo attachInfo = mAttachInfo;
- if (attachInfo == null || !attachInfo.mHardwareAccelerated) {
- return false;
- }
+ @Override
+ @CallSuper
+ public void onDescendantInvalidated(@NonNull View child, @NonNull View target) {
+ /*
+ * HW-only, Rect-ignoring damage codepath
+ *
+ * We don't deal with rectangles here, since RenderThread native code computes damage for
+ * everything drawn by HWUI (and SW layer / drawing cache doesn't keep track of damage area)
+ */
- // verify it's ViewGroups up to a ViewRootImpl
- ViewRootImpl viewRoot = null;
- ViewParent parent = getParent();
- while (parent != null) {
- if (parent instanceof ViewGroup) {
- parent = parent.getParent();
- } else if (parent instanceof ViewRootImpl) {
- viewRoot = (ViewRootImpl) parent;
- break;
- } else {
- // unknown parent type, abort
- return false;
- }
- }
- if (viewRoot == null) {
- // unable to find ViewRoot
- return false;
- }
+ // if set, combine the animation flag into the parent
+ mPrivateFlags |= (target.mPrivateFlags & PFLAG_DRAW_ANIMATION);
- final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0;
+ if ((target.mPrivateFlags & ~PFLAG_DIRTY_MASK) != 0) {
+ // We lazily use PFLAG_DIRTY, since computing opaque isn't worth the potential
+ // optimization in provides in a DisplayList world.
+ mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY;
- if (child.mLayerType != LAYER_TYPE_NONE) {
- mPrivateFlags |= PFLAG_INVALIDATED;
+ // simplified invalidateChildInParent behavior: clear cache validity to be safe...
+ mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
- parent = this;
- do {
- if (parent != viewRoot) {
- // Note: we cast here without checking isinstance, to avoid cost of isinstance again
- ViewGroup viewGroup = (ViewGroup) parent;
- if (drawAnimation) {
- viewGroup.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
- }
-
- // We lazily use PFLAG_DIRTY, since computing opaque isn't worth the potential
- // optimization in provides in a DisplayList world.
- viewGroup.mPrivateFlags =
- (viewGroup.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY;
-
- // simplified invalidateChildInParent behavior: clear cache validity to be safe,
- // and mark inval if in layer
- viewGroup.mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
- if (viewGroup.mLayerType != LAYER_TYPE_NONE) {
- viewGroup.mPrivateFlags |= PFLAG_INVALIDATED;
- }
- } else {
- if (drawAnimation) {
- viewRoot.mIsAnimating = true;
- }
- ((ViewRootImpl) parent).invalidate();
- return true;
- }
+ // ... and mark inval if in software layer that needs to repaint (hw handled in native)
+ if (mLayerType == LAYER_TYPE_SOFTWARE) {
+ // Layered parents should be invalidated. Escalate to a full invalidate (and note that
+ // we do this after consuming any relevant flags from the originating descendant)
+ mPrivateFlags |= PFLAG_INVALIDATED | PFLAG_DIRTY;
+ target = this;
+ }
- parent = parent.getParent();
- } while (parent != null);
- return true;
+ if (mParent != null) {
+ mParent.onDescendantInvalidated(this, target);
+ }
}
/**
* Don't call or override this method. It is used for the implementation of
* the view hierarchy.
+ *
+ * @deprecated Use {@link #onDescendantInvalidated(View, View)} instead to observe updates to
+ * draw state in descendants.
*/
+ @Deprecated
@Override
public final void invalidateChild(View child, final Rect dirty) {
- if (tryInvalidateChildHardware(child)) {
+ final AttachInfo attachInfo = mAttachInfo;
+ if (attachInfo != null && attachInfo.mHardwareAccelerated) {
+ // HW accelerated fast path
+ onDescendantInvalidated(child, child);
return;
}
ViewParent parent = this;
-
- final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
// If the child is drawing an animation, we want to copy this flag onto
// ourselves and the parent to make sure the invalidate request goes
@@ -5568,7 +5529,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* This implementation returns null if this ViewGroup does not have a parent,
* if this ViewGroup is already fully invalidated or if the dirty rectangle
* does not intersect with this ViewGroup's bounds.
+ *
+ * @deprecated Use {@link #onDescendantInvalidated(View, View)} instead to observe updates to
+ * draw state in descendants.
*/
+ @Deprecated
@Override
public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0) {
@@ -5617,74 +5582,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
/**
- * Native-calculated damage path
- * Returns false if this path was unable to complete successfully. This means
- * it hit a ViewParent it doesn't recognize and needs to fall back to calculating
- * damage area
- * @hide
- */
- public boolean damageChildDeferred() {
- ViewParent parent = getParent();
- while (parent != null) {
- if (parent instanceof ViewGroup) {
- parent = parent.getParent();
- } else if (parent instanceof ViewRootImpl) {
- ((ViewRootImpl) parent).invalidate();
- return true;
- } else {
- parent = null;
- }
- }
- return false;
- }
-
- /**
- * Quick invalidation method called by View.invalidateViewProperty. This doesn't set the
- * DRAWN flags and doesn't handle the Animation logic that the default invalidation methods
- * do; all we want to do here is schedule a traversal with the appropriate dirty rect.
- *
- * @hide
- */
- public void damageChild(View child, final Rect dirty) {
- if (damageChildDeferred()) {
- return;
- }
-
- ViewParent parent = this;
-
- final AttachInfo attachInfo = mAttachInfo;
- if (attachInfo != null) {
- int left = child.mLeft;
- int top = child.mTop;
- if (!child.getMatrix().isIdentity()) {
- child.transformRect(dirty);
- }
-
- do {
- if (parent instanceof ViewGroup) {
- ViewGroup parentVG = (ViewGroup) parent;
- if (parentVG.mLayerType != LAYER_TYPE_NONE) {
- // Layered parents should be recreated, not just re-issued
- parentVG.invalidate();
- parent = null;
- } else {
- parent = parentVG.damageChildInParent(left, top, dirty);
- left = parentVG.mLeft;
- top = parentVG.mTop;
- }
- } else {
- // Reached the top; this calls into the usual invalidate method in
- // ViewRootImpl, which schedules a traversal
- final int[] location = attachInfo.mInvalidateChildLocation;
- location[0] = left;
- location[1] = top;
- parent = parent.invalidateChildInParent(location, dirty);
- }
- } while (parent != null);
- }
- }
-
- /**
* Quick invalidation method that simply transforms the dirty rect into the parent's
* coordinate system, pruning the invalidation if the parent has already been invalidated.
*
diff --git a/core/java/android/view/ViewOverlay.java b/core/java/android/view/ViewOverlay.java
index 1676a004585b..f061370cc581 100644
--- a/core/java/android/view/ViewOverlay.java
+++ b/core/java/android/view/ViewOverlay.java
@@ -328,22 +328,10 @@ public class ViewOverlay {
}
}
- /**
- * @hide
- */
@Override
- public void damageChild(View child, final Rect dirty) {
- if (mHostView != null) {
- // Note: This is not a "fast" invalidation. Would be nice to instead invalidate
- // using DisplayList properties and a dirty rect instead of causing a real
- // invalidation of the host view
- int left = child.mLeft;
- int top = child.mTop;
- if (!child.getMatrix().isIdentity()) {
- child.transformRect(dirty);
- }
- dirty.offset(left, top);
- mHostView.invalidate(dirty);
+ public void onDescendantInvalidated(@NonNull View child, @NonNull View target) {
+ if (mHostView != null && mHostView.getParent() != null) {
+ mHostView.getParent().onDescendantInvalidated(mHostView, target);
}
}
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 79b05cdb6e50..cc11cb8205d5 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.NonNull;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.accessibility.AccessibilityEvent;
@@ -53,12 +54,36 @@ public interface ViewParent {
*/
public void requestTransparentRegion(View child);
+
+ /**
+ * The target View has been invalidated, or has had a drawing property changed that
+ * requires the hierarchy to re-render.
+ *
+ * This method is called by the View hierarchy to signal ancestors that a View either needs to
+ * re-record its drawing commands, or drawing properties have changed. This is how Views
+ * schedule a drawing traversal.
+ *
+ * This signal is generally only dispatched for attached Views, since only they need to draw.
+ *
+ * @param child Direct child of this ViewParent containing target
+ * @param target The view that needs to redraw
+ */
+ default void onDescendantInvalidated(@NonNull View child, @NonNull View target) {
+ if (getParent() != null) {
+ // Note: should pass 'this' as default, but can't since we may not be a View
+ getParent().onDescendantInvalidated(child, target);
+ }
+ }
+
/**
* All or part of a child is dirty and needs to be redrawn.
*
* @param child The child which is dirty
* @param r The area within the child that is invalid
+ *
+ * @deprecated Use {@link #onDescendantInvalidated(View, View)} instead.
*/
+ @Deprecated
public void invalidateChild(View child, Rect r);
/**
@@ -80,7 +105,10 @@ public interface ViewParent {
* @param r The area within the child that is invalid
*
* @return the parent of this ViewParent or null
+ *
+ * @deprecated Use {@link #onDescendantInvalidated(View, View)} instead.
*/
+ @Deprecated
public ViewParent invalidateChildInParent(int[] location, Rect r);
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3cbe82e8b6b9..2a90eeb62de0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.view.View.PFLAG_DRAW_ANIMATION;
import static android.view.WindowCallbacks.RESIZE_MODE_DOCKED_DIVIDER;
import static android.view.WindowCallbacks.RESIZE_MODE_FREEFORM;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
@@ -1065,6 +1066,14 @@ public final class ViewRootImpl implements ViewParent,
return mLayoutRequested;
}
+ @Override
+ public void onDescendantInvalidated(@NonNull View child, @NonNull View descendant) {
+ if ((descendant.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0) {
+ mIsAnimating = true;
+ }
+ invalidate();
+ }
+
void invalidate() {
mDirty.set(0, 0, mWidth, mHeight);
if (!mWillDrawSoon) {
diff --git a/core/tests/coretests/src/android/view/ViewInvalidateTest.java b/core/tests/coretests/src/android/view/ViewInvalidateTest.java
index 4db70ec4a1c4..9de7d9c6b375 100644
--- a/core/tests/coretests/src/android/view/ViewInvalidateTest.java
+++ b/core/tests/coretests/src/android/view/ViewInvalidateTest.java
@@ -219,7 +219,27 @@ public class ViewInvalidateTest {
public void testInvalidateChild_childHardwareLayer() throws Throwable {
WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mParent, () -> {
// do in runnable, so tree won't be dirty
- mChild.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mParent.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ });
+
+ mActivityRule.runOnUiThread(() -> {
+ validateInvalFlags(mParent,
+ View.PFLAG_DRAWING_CACHE_VALID,
+ View.PFLAG_DRAWN);
+
+ mParent.invalidateChild(mChild, new Rect(0, 0, 1, 1));
+
+ validateInvalFlags(mParent,
+ View.PFLAG_DIRTY,
+ View.PFLAG_DRAWN); // Note: note invalidated, since HW damage handled in native
+ });
+ }
+
+ @Test
+ public void testInvalidateChild_childSoftwareLayer() throws Throwable {
+ WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mParent, () -> {
+ // do in runnable, so tree won't be dirty
+ mParent.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
});
mActivityRule.runOnUiThread(() -> {
@@ -232,7 +252,7 @@ public class ViewInvalidateTest {
validateInvalFlags(mParent,
View.PFLAG_DIRTY,
View.PFLAG_DRAWN,
- View.PFLAG_INVALIDATED);
+ View.PFLAG_INVALIDATED); // Note: invalidated, since SW damage handled here
});
}