- 看看invalidate源碼,如下所示
- invalidateCache為true表示全部重繪。View的invalidate方法最后調(diào)用的是invalidateInternal方法,invalidateInternal方法中的重點邏輯在源碼上添加注釋呢。
-
public void invalidate() {
invalidate(true);
}
public void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
if (mGhostView != null) {
mGhostView.invalidate(true);
return;
}
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;
final ViewParent p = mParent;
if (p != null && ai != null && l < r && t < b) {
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
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();
}
}
}
}
- 看看ViewGroup中的invalidateChild方法
- 在ViewGroup的invalidateChild方法中有一個循環(huán),循環(huán)里面會一直調(diào)用父布局的invalidateChildInParent方法,而View和ViewGroup的最終父布局都是ViewRootImpl。
@UiThread
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
@Override
public final void invalidateChild(View child, final Rect dirty) {
ViewParent parent = this;
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
//這是一個從當(dāng)前的布局View向上不斷遍歷當(dāng)前布局View的父布局,最后遍歷到ViewRootImpl的循環(huán)
do {
View view = null;
if (parent instanceof View) {
view = (View) parent;
}
//這里調(diào)用的是父布局的invalidateChildInParent方法
parent = parent.invalidateChildInParent(location, dirty);
} while (parent != null);
}
}
@Override
public ViewParent invalidateChildInParent(final int[] 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) {
...
//這里也是一些計算繪制區(qū)域的內(nèi)容
return mParent;
} else {
mPrivateFlags &= ~PFLAG_DRAWN & ~PFLAG_DRAWING_CACHE_VALID;
//這里也是一些計算繪制區(qū)域的內(nèi)容
return mParent;
}
}
return null;
}
}
- View中的invalidateChild方法和ViewGroup中的invalidateChildInParent方法最后殊途同歸,都會調(diào)用到ViewRootImpl中的方法
- 可以看到在ViewRootImpl中最后都會調(diào)用scheduleTraversals方法進(jìn)行繪制。按照對于View的繪制過程的理解,View的繪制流程是從ViewRootImpl的performTraversals方法開始的
public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.HardwareDrawCallbacks {
//如果View沒有父布局,那invalidateInternal方法就會調(diào)用這個方法
@Override
public void invalidateChild(View child, Rect dirty) {
invalidateChildInParent(null, dirty);
}
//ViewGroup的invalidateChild方法最后會調(diào)用到這里
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
checkThread();
//如果dirty為null就表示要重繪當(dāng)前ViewRootImpl指示的整個區(qū)域
if (dirty == null) {
invalidate();
return null;
//如果dirty為empty則表示經(jīng)過計算需要重繪的區(qū)域不需要繪制
} else if (dirty.isEmpty() && !mIsAnimating) {
return null;
}
return null;
}
private void invalidateRectOnScreen(Rect dirty) {
final Rect localDirty = mDirty;
...
if (!mWillDrawSoon && (intersected || mIsAnimating)) {
//調(diào)用scheduleTraversals方法進(jìn)行繪制
scheduleTraversals();
}
}
//繪制整個ViewRootImpl區(qū)域
void invalidate() {
mDirty.set(0, 0, mWidth, mHeight);
if (!mWillDrawSoon) {
//調(diào)用scheduleTraversals方法進(jìn)行繪制
scheduleTraversals();
}
}
}
- 下面我們來看看ViewRootImpl中的scheduleTraversals方法
- 看到scheduleTraversals方法中調(diào)用了mChoreographer.postCallback方法
- Choreoprapher類的作用是編排輸入事件、動畫事件和繪制事件的執(zhí)行,它的postCallback方法的作用就是將要執(zhí)行的事件放入內(nèi)部的一個隊列中,最后會執(zhí)行傳入的Runnable,這里也就是mTraversalRunnable。
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
- 來看看TraversalRunnable這個類做了什么?
- 可以看到最終調(diào)用了performTraversals()方法進(jìn)行繪制
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
- 大概總結(jié)一下
- invalidate方法最終調(diào)用的是ViewRootImpl中的performTraversals來重新繪制View
- 在自定義View時,當(dāng)需要刷新View時,如果是在UI線程中,那就直接調(diào)用invalidate方法,如果是在非UI線程中,那就通過postInvalidate方法來刷新View
- 源碼如下所示
- 在View的requestLayout方法中,首先會設(shè)置View的標(biāo)記位,PFLAG_FORCE_LAYOUT表示當(dāng)前View要進(jìn)行重新布局,PFLAG_INVALIDATED表示要進(jìn)行重新繪制。
- requestLayout方法中會一層層向上調(diào)用父布局的requestLayout方法,設(shè)置PFLAG_FORCE_LAYOUT標(biāo)記,最終調(diào)用的是ViewRootImpl中的requestLayout方法。
//View.class
@CallSuper
public void requestLayout() {
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;
}
//設(shè)置PFLAG_FORCE_LAYOUT標(biāo)記位,這樣就會導(dǎo)致重新測量和布局
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
//設(shè)置PFLAG_INVALIDATED就會進(jìn)行重新繪制
mPrivateFlags |= PFLAG_INVALIDATED;
if (mParent != null && !mParent.isLayoutRequested()) {
//不斷調(diào)用上層View的requestLayout方法
mParent.requestLayout();
}
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
mAttachInfo.mViewRequestingLayout = null;
}
}
- 然后看看ViewRootImpl中的requestLayout方法
- 可以看到ViewRootImpl中的requestLayout方法中會調(diào)用scheduleTraversals方法,scheduleTraversals方法最后會調(diào)用performTraversals方法開始執(zhí)行View的三大流程,會分別調(diào)用View的measure、layout、draw方法。
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
- 然后再看看measure測量方法
- 由于requestLayout方法設(shè)置了PFLAG_FORCE_LAYOUT標(biāo)記位,所以measure方法就會調(diào)用onMeasure方法對View進(jìn)行重新測量。在measure方法中最后設(shè)置了PFLAG_LAYOUT_REQUIRED標(biāo)記位,這樣在layout方法中就會執(zhí)行onLayout方法進(jìn)行布局流程。
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
if (forceLayout || needsLayout) {
// first clears the measured dimension flag
mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
if (cacheIndex < 0 || sIgnoreMeasureCache) {
//調(diào)用onMeasure方法
onMeasure(widthMeasureSpec, heightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
} else {
long value = mMeasureCache.valueAt(cacheIndex);
setMeasuredDimensionRaw((int) (value >> 32), (int) value);
mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
//設(shè)置PFLAG_LAYOUT_REQUIRED標(biāo)記位,用于layout方法
mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
}
}
- 再然后看看layout方法
- 由于measure方法中設(shè)置了PFLAG_LAYOUT_REQUIRED標(biāo)記位,所以在layout方法中onLayout方法會被調(diào)用執(zhí)行布局流程。最后清除PFLAG_FORCE_LAYOUT和PFLAG_LAYOUT_REQUIRED標(biāo)記位。
public void layout(int l, int t, int r, int b) {
//由于measure方法中設(shè)置了PFLAG_LAYOUT_REQUIRED標(biāo)記位,所以會進(jìn)入調(diào)用onLayout方法進(jìn)行布局流程
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
if (shouldDrawRoundScrollbar()) {
if(mRoundScrollbarRenderer == null) {
mRoundScrollbarRenderer = new RoundScrollbarRenderer(this);
}
} else {
mRoundScrollbarRenderer = null;
}
//取消PFLAG_LAYOUT_REQUIRED標(biāo)記位
mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnLayoutChangeListeners != null) {
ArrayList<OnLayoutChangeListener> listenersCopy =
(ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
int numListeners = listenersCopy.size();
for (int i = 0; i < numListeners; ++i) {
listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
}
}
}
//取消PFLAG_FORCE_LAYOUT標(biāo)記位
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
}