/** * Invalidate the whole view. If the view is visible, * {@link #onDraw(android.graphics.Canvas)} will be called at some point in * the future. * <p> * This must be called from a UI thread. To call from a non-UI thread, call * {@link #postInvalidate()}. */ publicvoidinvalidate(){ invalidate(true); }
/** * This is where the invalidate() work actually happens. A full invalidate() * causes the drawing cache to be invalidated, but this function can be * called with invalidateCache set to false to skip that invalidation step * for cases that do not need it (for example, a component that remains at * the same dimensions with the same content). * * @param invalidateCache Whether the drawing cache for this view should be * invalidated as well. This is usually true for a full * invalidate, but may be set to false if the View's contents or * dimensions have not changed. */ voidinvalidate(boolean invalidateCache){ invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true); }
if (invalidateCache) { mPrivateFlags |= PFLAG_INVALIDATED; mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID; }
// Propagate the damage rectangle to the parent view. final AttachInfo ai = mAttachInfo; final ViewParent p = mParent; if (p != null && ai != null && l < r && t < b) { final Rect damage = ai.mTmpInvalRect; damage.set(l, t, r, b); //往父view传递,调用父view的invalidateChild方法 p.invalidateChild(this, damage); }
/** * Don't call or override this method. It is used for the implementation of * the view hierarchy. */ publicfinalvoidinvalidateChild(View child, final Rect dirty){ ViewParent parent = this;
/** * Don't call or override this method. It is used for the implementation of * the view hierarchy. * * 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. */ public ViewParent invalidateChildInParent(finalint[] location, final Rect dirty){ if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN || (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) { if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) != FLAG_OPTIMIZE_INVALIDATE) { dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX, location[CHILD_TOP_INDEX] - mScrollY); if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) { dirty.union(0, 0, mRight - mLeft, mBottom - mTop); }
@Override public ViewParent invalidateChildInParent(int[] location, Rect dirty){ checkThread(); ... if (dirty == null) { invalidate(); returnnull; } elseif (dirty.isEmpty() && !mIsAnimating) { returnnull; }
...
invalidateRectOnScreen(dirty);
returnnull; }
最关键的一步在invalidateRectOnScreen中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
privatevoidinvalidateRectOnScreen(Rect dirty){ final Rect localDirty = mDirty; ...
// Add the new dirty rect to the current one localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom); // Intersect with the bounds of the window to skip // updates that lie outside of the visible region ... if (!mWillDrawSoon && (intersected || mIsAnimating)) { scheduleTraversals(); } }
/** * <p>Cause an invalidate to happen on a subsequent cycle through the event loop. * Use this to invalidate the View from a non-UI thread.</p> * * <p>This method can be invoked from outside of the UI thread * only when this View is attached to a window.</p> * * @see #invalidate() * @see #postInvalidateDelayed(long) */ publicvoidpostInvalidate(){ postInvalidateDelayed(0); }
/** * <p>Cause an invalidate to happen on a subsequent cycle through the event * loop. Waits for the specified amount of time.</p> * * <p>This method can be invoked from outside of the UI thread * only when this View is attached to a window.</p> * * @param delayMilliseconds the duration in milliseconds to delay the * invalidation by * * @see #invalidate() * @see #postInvalidate() */ publicvoidpostInvalidateDelayed(long delayMilliseconds){ // We try only with the AttachInfo because there's no point in invalidating // if we are not attached to our window final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds); } }
/** * Call this when something has changed which has invalidated the * layout of this view. This will schedule a layout pass of the view * tree. This should not be called while the view hierarchy is currently in a layout * pass ({@link #isInLayout()}. If layout is happening, the request may be honored at the * end of the current layout pass (and then layout will run again) or after the current * frame is drawn and the next layout occurs. * * <p>Subclasses which override this method should call the superclass method to * handle possible request-during-layout errors correctly.</p> */ @CallSuper publicvoidrequestLayout(){ if (mMeasureCache != null) mMeasureCache.clear();
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) { // Only trigger request-during-layout logic if this is the view requesting it, // not the views in its parent hierarchy ViewRootImpl viewRoot = getViewRootImpl(); if (viewRoot != null && viewRoot.isInLayout()) { if (!viewRoot.requestLayoutDuringLayout(this)) { return; } } mAttachInfo.mViewRequestingLayout = this; }
voidcheckThread(){ if (mThread != Thread.currentThread()) { thrownew CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } }