invalidate
在自定view時(shí),經(jīng)常要調(diào)用invalidate方法去刷新ui,調(diào)用簡(jiǎn)單的invalidate方法就可以將ui重新刷新拨与,其實(shí)在源碼中相當(dāng)多的處理哩治,接下來(lái)就慢慢去看看invalidate方法的源碼秃踩;在調(diào)用invalidate方法后就會(huì)去調(diào)用View中invalidate方法,
/**
* 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()}.
*/
public void invalidate() {
invalidate(true);
}
通過(guò)invalidate方法的源碼注釋可以大致知道三點(diǎn):
1.調(diào)用invalidate方法會(huì)刷新ui锚扎,前提是該view是可見的吞瞪;
2.調(diào)用invalidate方法后,onDraw方法也會(huì)被調(diào)用驾孔;
3.invalidate方法的調(diào)用必須在ui線程中芍秆,如果在非ui線程中可以使用postInvalidate方法更新ui,這也是invalidate和postInvalidate方法的區(qū)別
調(diào)用View中的invalidate方法后翠勉,接著會(huì)調(diào)用View中的invalidateInternal方法妖啥,在invalidateInternal方法中會(huì)調(diào)用skinInvalidate方法判斷該view是否可見,該view是否有動(dòng)畫对碌,接著會(huì)調(diào)用ViewParent中的invalidateChild方法荆虱;
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
if (mGhostView != null) {
mGhostView.invalidate(true);
return;
}
//調(diào)用skinInvalidate方法判斷該view是否可見,是否有動(dòng)畫朽们,
//如果該view是不可見的怀读,并且是沒有動(dòng)畫的就是返回,不進(jìn)行更新
if (skipInvalidate()) {
return;
}
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
|| (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
|| (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
|| (fullInvalidate && isOpaque() != mLastIsOpaque)) {
if (fullInvalidate) {
mLastIsOpaque = isOpaque();
mPrivateFlags &= ~PFLAG_DRAWN;
}
mPrivateFlags |= PFLAG_DIRTY;
if (invalidateCache) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
// Propagate the damage rectangle to the parent view.
final AttachInfo ai = mAttachInfo;
//將mParent賦值給ViewParent對(duì)象
final ViewParent p = mParent;
if (p != null && ai != null && l < r && t < b) {
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
//調(diào)用ViewParent中的invalidateChild方法
p.invalidateChild(this, damage);
}
// Damage the entire projection receiver, if necessary.
if (mBackground != null && mBackground.isProjected()) {
final View receiver = getProjectionReceiver();
if (receiver != null) {
receiver.damageInParent();
}
}
// Damage the entire IsolatedZVolume receiving this view's shadow.
if (isHardwareAccelerated() && getZ() != 0) {
damageShadowReceiver();
}
}
}
現(xiàn)在就到了ViewParent中的invalidateChild方法骑脱,會(huì)發(fā)現(xiàn)ViewParent是一個(gè)接口磕仅,就需要去找它的實(shí)現(xiàn)類诀拭,ViewGroup和ViewRootImpl都是它的實(shí)現(xiàn)類左刽,所以就找ViewGroup中的invalidateChild方法冈止;
@Override
public final void invalidateChild(View child, final Rect dirty) {
//將當(dāng)前ViewGroup賦值給ViewParent,
//這樣ViewParent就不會(huì)為null拥娄,在下面的whlie循環(huán)中就可以進(jìn)行循環(huán)
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
// through
final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION)
== PFLAG_DRAW_ANIMATION;
// Check whether the child that requests the invalidate is fully opaque
// Views being animated or transformed are not considered opaque because we may
// be invalidating their old position and need the parent to paint behind them.
Matrix childMatrix = child.getMatrix();
final boolean isOpaque = child.isOpaque() && !drawAnimation &&
child.getAnimation() == null && childMatrix.isIdentity();
// Mark the child as dirty, using the appropriate flag
// Make sure we do not set both flags at the same time
int opaqueFlag = isOpaque ? PFLAG_DIRTY_OPAQUE : PFLAG_DIRTY;
if (child.mLayerType != LAYER_TYPE_NONE) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
final int[] location = attachInfo.mInvalidateChildLocation;
location[CHILD_LEFT_INDEX] = child.mLeft;
location[CHILD_TOP_INDEX] = child.mTop;
if (!childMatrix.isIdentity() ||
(mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
RectF boundingRect = attachInfo.mTmpTransformRect;
boundingRect.set(dirty);
Matrix transformMatrix;
if ((mGroupFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
Transformation t = attachInfo.mTmpTransformation;
boolean transformed = getChildStaticTransformation(child, t);
if (transformed) {
transformMatrix = attachInfo.mTmpMatrix;
transformMatrix.set(t.getMatrix());
if (!childMatrix.isIdentity()) {
transformMatrix.preConcat(childMatrix);
}
} else {
transformMatrix = childMatrix;
}
} else {
transformMatrix = childMatrix;
}
transformMatrix.mapRect(boundingRect);
dirty.set((int) Math.floor(boundingRect.left),
(int) Math.floor(boundingRect.top),
(int) Math.ceil(boundingRect.right),
(int) Math.ceil(boundingRect.bottom));
}
//進(jìn)行while循環(huán)
do {
View view = null;
if (parent instanceof View) {
view = (View) parent;
}
if (drawAnimation) {
if (view != null) {
view.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
} else if (parent instanceof ViewRootImpl) {
((ViewRootImpl) parent).mIsAnimating = true;
}
}
// If the parent is dirty opaque or not dirty, mark it dirty with the opaque
// flag coming from the child that initiated the invalidate
if (view != null) {
if ((view.mViewFlags & FADING_EDGE_MASK) != 0 &&
view.getSolidColor() == 0) {
opaqueFlag = PFLAG_DIRTY;
}
if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) {
view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | opaqueFlag;
}
}
//這里會(huì)不停的調(diào)用ViewParent中的invalidateChildInParent方法蚊锹,
//并返回一個(gè)ViewParent對(duì)象,會(huì)一直往外遍歷稚瘾,知道最外層ViewRootImpl牡昆,
//返回的ViewParent對(duì)象為null就跳出該循環(huán)
parent = parent.invalidateChildInParent(location, dirty);
if (view != null) {
// Account for transform on current parent
Matrix m = view.getMatrix();
if (!m.isIdentity()) {
RectF boundingRect = attachInfo.mTmpTransformRect;
boundingRect.set(dirty);
m.mapRect(boundingRect);
dirty.set((int) Math.floor(boundingRect.left),
(int) Math.floor(boundingRect.top),
(int) Math.ceil(boundingRect.right),
(int) Math.ceil(boundingRect.bottom));
}
}
} while (parent != null);
}
}
invalidateChild是不能被重寫的,在該方法中的while循環(huán)中會(huì)不聽的調(diào)用ViewParent中的invalidateChildInParent方法孟抗,并返回一個(gè)ViewParent對(duì)象迁杨,會(huì)一直往外層遍歷钻心,直到ViewRootImpl中的invalidateChildInParent方法,如果返回ViewParent為null就跳出當(dāng)前循環(huán)铅协;
遍歷到最外層后就會(huì)去調(diào)動(dòng)ViewRootImpl中的invalidateChildInParent方法捷沸;
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
//調(diào)用checkThread去檢測(cè)當(dāng)前線程是不是ui線程,如果不是就會(huì)拋異常
checkThread();
if (DEBUG_DRAW) Log.v(mTag, "Invalidate child: " + dirty);
if (dirty == null) {
invalidate();
return null;
} else if (dirty.isEmpty() && !mIsAnimating) {
return null;
}
if (mCurScrollY != 0 || mTranslator != null) {
mTempRect.set(dirty);
dirty = mTempRect;
if (mCurScrollY != 0) {
dirty.offset(0, -mCurScrollY);
}
if (mTranslator != null) {
mTranslator.translateRectInAppWindowToScreen(dirty);
}
if (mAttachInfo.mScalingRequired) {
dirty.inset(-1, -1);
}
}
//調(diào)動(dòng)invalidateRectOnScreen方法去更新繪制ui
invalidateRectOnScreen(dirty);
return null;
}
在ViewRootImpl的invalidateChildInParent方法中首先會(huì)調(diào)用checkThread方法去檢測(cè)當(dāng)前線程是否是ui線程狐史,如果不是ui線程痒给,就會(huì)拋異常,這也是不能在非ui線程更新ui的原因骏全;接著就會(huì)調(diào)用invalidateRectOnScreen方法苍柏;在invalidateRectOnScreen方法中就會(huì)去調(diào)用scheduleTraversals方法;在scheduleTraversals方法中就會(huì)調(diào)用postCallBack并傳入一個(gè)TraversalRunnable對(duì)象姜贡;在TraversalRunnable對(duì)象的run方法中會(huì)去調(diào)用doTraversal方法试吁;
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
//調(diào)用performTraversals方法進(jìn)行繪制準(zhǔn)備工作;
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
在performTraversals方法中就會(huì)去調(diào)用performMearsure楼咳、performLayout熄捍、performDraw等方法,不過(guò)在invalidate刷新ui的時(shí)候只會(huì)調(diào)用performDraw方法母怜,所有只去看performDraw方法就可以了余耽;在performDraw中就會(huì)去調(diào)用draw方法;在draw中就會(huì)去調(diào)用drawSofeware方法苹熏;
**
* @return true if drawing was successful, false if an error occurred
*/
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
// Draw with software renderer.
//定義一個(gè)Canvas畫布
final Canvas canvas;
try {
final int left = dirty.left;
final int top = dirty.top;
final int right = dirty.right;
final int bottom = dirty.bottom;
canvas = mSurface.lockCanvas(dirty);
// The dirty rectangle can be modified by Surface.lockCanvas()
//noinspection ConstantConditions
if (left != dirty.left || top != dirty.top || right != dirty.right
|| bottom != dirty.bottom) {
attachInfo.mIgnoreDirtyState = true;
}
// TODO: Do this in native
canvas.setDensity(mDensity);
} catch (Surface.OutOfResourcesException e) {
handleOutOfResourcesException(e);
return false;
} catch (IllegalArgumentException e) {
Log.e(mTag, "Could not lock surface", e);
// Don't assume this is due to out of memory, it could be
// something else, and if it is something else then we could
// kill stuff (or ourself) for no reason.
mLayoutRequested = true; // ask wm for a new surface next time.
return false;
}
try {
if (DEBUG_ORIENTATION || DEBUG_DRAW) {
Log.v(mTag, "Surface " + surface + " drawing to bitmap w="
+ canvas.getWidth() + ", h=" + canvas.getHeight());
//canvas.drawARGB(255, 255, 0, 0);
}
// If this bitmap's format includes an alpha channel, we
// need to clear it before drawing so that the child will
// properly re-composite its drawing on a transparent
// background. This automatically respects the clip/dirty region
// or
// If we are applying an offset, we need to clear the area
// where the offset doesn't appear to avoid having garbage
// left in the blank areas.
if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
canvas.drawColor(0, PorterDuff.Mode.CLEAR);
}
dirty.setEmpty();
mIsAnimating = false;
mView.mPrivateFlags |= View.PFLAG_DRAWN;
if (DEBUG_DRAW) {
Context cxt = mView.getContext();
Log.i(mTag, "Drawing: package:" + cxt.getPackageName() +
", metrics=" + cxt.getResources().getDisplayMetrics() +
", compatibilityInfo=" + cxt.getResources().getCompatibilityInfo());
}
try {
canvas.translate(-xoff, -yoff);
if (mTranslator != null) {
mTranslator.translateCanvas(canvas);
}
canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
attachInfo.mSetIgnoreDirtyState = false;
//調(diào)用view中的draw方法碟贾,并將定義的canvas畫布傳入
mView.draw(canvas);
drawAccessibilityFocusedDrawableIfNeeded(canvas);
} finally {
if (!attachInfo.mSetIgnoreDirtyState) {
// Only clear the flag if it was not set during the mView.draw() call
attachInfo.mIgnoreDirtyState = false;
}
}
} finally {
try {
surface.unlockCanvasAndPost(canvas);
} catch (IllegalArgumentException e) {
Log.e(mTag, "Could not unlock surface", e);
mLayoutRequested = true; // ask wm for a new surface next time.
//noinspection ReturnInsideFinallyBlock
return false;
}
if (LOCAL_LOGV) {
Log.v(mTag, "Surface " + surface + " unlockCanvasAndPost");
}
}
return true;
}
終于到了View中的draw方法了;
@CallSuper
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
//定義dirtyOpaque 標(biāo)識(shí)轨域,用來(lái)控制drawBackground onDraw等方法的調(diào)用
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background 繪制背景
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content 繪制內(nèi)容
* 4. Draw children 繪制子view
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/
// Step 1, draw the background, if needed
int saveCount;
if (!dirtyOpaque) {
//沒有設(shè)置背景袱耽,默認(rèn)的ViewGroup不會(huì)調(diào)用該方法
drawBackground(canvas);
}
// skip step 2 & 5 if possible (common case)
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the content 默認(rèn)的ViewGroup不會(huì)調(diào)用該方法
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
// we're done...
return;
}
/*
* Here we do the full fledged routine...
* (this is an uncommon case where speed matters less,
* this is why we repeat some of the tests that have been
* done above)
*/
boolean drawTop = false;
boolean drawBottom = false;
boolean drawLeft = false;
boolean drawRight = false;
float topFadeStrength = 0.0f;
float bottomFadeStrength = 0.0f;
float leftFadeStrength = 0.0f;
float rightFadeStrength = 0.0f;
// Step 2, save the canvas' layers
int paddingLeft = mPaddingLeft;
final boolean offsetRequired = isPaddingOffsetRequired();
if (offsetRequired) {
paddingLeft += getLeftPaddingOffset();
}
int left = mScrollX + paddingLeft;
int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
int top = mScrollY + getFadeTop(offsetRequired);
int bottom = top + getFadeHeight(offsetRequired);
if (offsetRequired) {
right += getRightPaddingOffset();
bottom += getBottomPaddingOffset();
}
final ScrollabilityCache scrollabilityCache = mScrollCache;
final float fadeHeight = scrollabilityCache.fadingEdgeLength;
int length = (int) fadeHeight;
// clip the fade length if top and bottom fades overlap
// overlapping fades produce odd-looking artifacts
if (verticalEdges && (top + length > bottom - length)) {
length = (bottom - top) / 2;
}
// also clip horizontal fades if necessary
if (horizontalEdges && (left + length > right - length)) {
length = (right - left) / 2;
}
if (verticalEdges) {
topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
drawTop = topFadeStrength * fadeHeight > 1.0f;
bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
}
if (horizontalEdges) {
leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
drawLeft = leftFadeStrength * fadeHeight > 1.0f;
rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
drawRight = rightFadeStrength * fadeHeight > 1.0f;
}
saveCount = canvas.getSaveCount();
int solidColor = getSolidColor();
if (solidColor == 0) {
final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
if (drawTop) {
canvas.saveLayer(left, top, right, top + length, null, flags);
}
if (drawBottom) {
canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
}
if (drawLeft) {
canvas.saveLayer(left, top, left + length, bottom, null, flags);
}
if (drawRight) {
canvas.saveLayer(right - length, top, right, bottom, null, flags);
}
} else {
scrollabilityCache.setFadeColor(solidColor);
}
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
// Step 5, draw the fade effect and restore layers
final Paint p = scrollabilityCache.paint;
final Matrix matrix = scrollabilityCache.matrix;
final Shader fade = scrollabilityCache.shader;
if (drawTop) {
matrix.setScale(1, fadeHeight * topFadeStrength);
matrix.postTranslate(left, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, top, right, top + length, p);
}
if (drawBottom) {
matrix.setScale(1, fadeHeight * bottomFadeStrength);
matrix.postRotate(180);
matrix.postTranslate(left, bottom);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, bottom - length, right, bottom, p);
}
if (drawLeft) {
matrix.setScale(1, fadeHeight * leftFadeStrength);
matrix.postRotate(-90);
matrix.postTranslate(left, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, top, left + length, bottom, p);
}
if (drawRight) {
matrix.setScale(1, fadeHeight * rightFadeStrength);
matrix.postRotate(90);
matrix.postTranslate(right, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(right - length, top, right, bottom, p);
}
canvas.restoreToCount(saveCount);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
}
最終會(huì)去調(diào)用View中的onDraw方法,然后在自定定義view中重寫onDraw方法干发,進(jìn)行ui刷新和繪制扛邑,在View的draw方法中繪制工作分為以下:
1.調(diào)用drawBackground方法繪制背景
2.調(diào)用onDraw方法繪制內(nèi)容
3.調(diào)用dispatchDraw方法繪制子view
4.調(diào)用onDrawForeground繪制裝飾
這些方法中默認(rèn)的ViewGroup不會(huì)走drawBackground和onDraw方法,在繼承自ViewGroup的自定義view中要繪制的話铐然,可以重寫onDraw方法,并設(shè)置背景恶座,或者重寫dispatchDraw方法搀暑,在里面進(jìn)行繪制,方法有很多跨琳;
上面大致就是對(duì)invalidate的源碼分析自点,接下來(lái)看postInvalidate方法;
postInvalidate
/**
* <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)
*/
public void postInvalidate() {
postInvalidateDelayed(0);
}
通過(guò)源碼注釋很明顯該方法被調(diào)用于ui線程以外去更新繪制ui脉让;接著會(huì)去調(diào)用postInvalidateDelayed方法桂敛;
public void postInvalidateDelayed(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);
}
}
在postInvalidateDelayed方法中就直接去調(diào)用ViewRootImpl中的dispatchInvalidateDelayed方法功炮;
public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
//實(shí)例化一個(gè)Message消息對(duì)象
Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
//發(fā)送消息
mHandler.sendMessageDelayed(msg, delayMilliseconds);
}
在dispatchInvalidateDelayed方法中會(huì)通過(guò)handler實(shí)例化一個(gè)Message對(duì)象,然后通過(guò)handler發(fā)送消息术唬;這里的hanlder是ViewRootHandler薪伏,在ViewRootHandler的handleMessage方法中就會(huì)根據(jù)消息標(biāo)識(shí)進(jìn)行處理;
final class ViewRootHandler extends Handler {
@Override
public String getMessageName(Message message) {
......
return super.getMessageName(message);
}
@Override
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
if (msg.what == MSG_REQUEST_KEYBOARD_SHORTCUTS && msg.obj == null) {
// Debugging for b/27963013
throw new NullPointerException(
"Attempted to call MSG_REQUEST_KEYBOARD_SHORTCUTS with null receiver:");
}
return super.sendMessageAtTime(msg, uptimeMillis);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
//根據(jù)消息標(biāo)識(shí)來(lái)進(jìn)行處理
case MSG_INVALIDATE:
//調(diào)用view中invalidate方法
((View) msg.obj).invalidate();
break;
......
}
}
}
走到這里會(huì)看到postInvalidate最終還是通過(guò)調(diào)用view中的invaidate方法進(jìn)行view繪制更新粗仓,后面流程可以看上面invalidate的繪制流程嫁怀。
結(jié)論
invalidate是在ui線程中使用,postInvalidate是在非ui線程中使用借浊,postInvalidate通過(guò)ViewRootHandler消息切換到主線程中塘淑,最終還是通過(guò)調(diào)用View中的invalidate方法進(jìn)行ui的繪制更新,不管是invalidate還是postInvalidate只有當(dāng)該View為可見時(shí)才會(huì)進(jìn)行view的更新繪制蚂斤,調(diào)用invalidate或者postInvalidate更新繪制view時(shí)存捺,只會(huì)調(diào)用onDraw方法,不會(huì)調(diào)用onMeasure和onLayout方法曙蒸。