diff options
| 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 |
| commit | 9de95db4f28f88c37b1443d20b308ce02407fd74 (patch) | |
| tree | 0d82dae63c557a7928921fcccd1050beaf4a5a1c /core/java | |
| parent | fffa2eb0460dbb790d565b3e2a651b6a9fb7c9b9 (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
Diffstat (limited to 'core/java')
| -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 |
5 files changed, 82 insertions, 168 deletions
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) { |
