diff options
| -rw-r--r-- | api/current.txt | 9 | ||||
| -rw-r--r-- | api/system-current.txt | 9 | ||||
| -rw-r--r-- | api/test-current.txt | 9 | ||||
| -rw-r--r-- | core/java/android/view/View.java | 12 | ||||
| -rw-r--r-- | core/java/android/view/ViewGroup.java | 183 | ||||
| -rw-r--r-- | core/java/android/view/ViewOverlay.java | 18 | ||||
| -rw-r--r-- | core/java/android/view/ViewParent.java | 28 | ||||
| -rw-r--r-- | core/java/android/view/ViewRootImpl.java | 9 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/view/ViewInvalidateTest.java | 24 |
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 }); } |