天天看点

自定义View---invalidate() 方法

记录一下前段时间学习的当调用invalidate() 的时候,当前View的onDraw()方法会被调用的原因;通过追踪源码可以发现(我这边看的源码版本是25的):

当我们调用了View的invalidate()时候,invalidate()往下走调用了 invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, boolean fullInvalidate) 方法,其中在这个方法里面会调用了ViewGroup.invalidateChild(View child, Rect r) 的方法

void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
            boolean fullInvalidate) {
        ......
            final ViewParent p = mParent;
            if (p != null && ai != null && l < r && t < b) {
                final Rect damage = ai.mTmpInvalRect;
                damage.set(l, t, r, b);
                // ViewParent是个接口,ViewGroup 中实例化了这个接口 
                // 所以这步调用的是 ViewGroup.invalidateChild(View child, Rect r) 的方法
                p.invalidateChild(this, damage);
            }
		...... 
    }
           

其中这个方法里面会不断的调用了 parent.invalidateChildInParent(location, dirty);

触发ViewParent#invalidateChildInParent方法,返回上层父节点ViewParent,若父节点不空,继续此方法,最终触发顶层节点ViewRootImpl#invalidateChildInParent方法。

@Override
    public final void invalidateChild(View child, final Rect dirty) {
        ViewParent parent = this;
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
           ......

            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;
                    }
                }

                parent = parent.invalidateChildInParent(location, dirty);
                ......
            } while (parent != null);
        }
    }
           

在ViewRootImpl#invalidateChildInParent(final int[] location, final Rect dirty) 第一行会先检查我们的当前线程是否主线程,如果不是则抛异常

@Override
    public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
        checkThread();
        ......
        invalidateRectOnScreen(dirty);

        return null;
    }
           
void checkThread() {
        if (mThread != Thread.currentThread()) {
        // 一般非主线程更新UI就报这个
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }
           

沿着ViewRootImpl#invalidateChildInParent(final int[] location, final Rect dirty)方法往下走,最后调用了ViewRootImpl#invalidateRectOnScreen(dirty);,这个方法里面调用了ViewRootImpl#scheduleTraversals(); 这个方法

private void invalidateRectOnScreen(Rect dirty) {
        ......
        if (!mWillDrawSoon && (intersected || mIsAnimating)) {
            scheduleTraversals();
        }
    }
           
void scheduleTraversals() {
        if (!mTraversalScheduled) {
            ......
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
			......
        }
    }
           

ViewRootImpl#scheduleTraversals()中mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);这个方法传了三个参数,其中第二个是一个Runnable对象,这个Runnable对象中只调用了一个 doTraversal(); 方法,在这个方法里面则调用了ViewRootImpl#performTraversals();

void doTraversal() {
        if (mTraversalScheduled) {
            ......
            performTraversals();
			......
        }
    }
           

ViewRootImpl#performTraversals()这个方法往下走,在2215行里调用了ViewRootImpl#performDraw();的方法

自定义View---invalidate() 方法

ViewRootImpl#performDraw()里面往下走调用了ViewRootImpl#draw(boolean fullRedrawNeeded)方法

private void performDraw() {
        ......
        try {
            draw(fullRedrawNeeded);
        } finally {
            mIsDrawing = false;
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        ......
    }
           

在ViewRootImpl#draw(boolean fullRedrawNeeded)里做的先获取了mDirty值,该值保存了需要重绘的区域的信息,关于视图重绘,后面会有文章专门叙述,这里先熟悉一下。接着根据fullRedrawNeeded来判断是否需要重置dirty区域,最后调用了ViewRootImpl#drawSoftware方法,并把相关参数传递进去,包括dirty区域

private void draw(boolean fullRedrawNeeded) {
    ...
    //获取mDirty,该值表示需要重绘的区域
    final Rect dirty = mDirty;
    if (mSurfaceHolder != null) {
        // The app owns the surface, we won't draw.
        dirty.setEmpty();
        if (animating) {
            if (mScroller != null) {
                mScroller.abortAnimation();
            }
            disposeResizeBuffer();
        }
        return;
    }
    //如果fullRedrawNeeded为真,则把dirty区域置为整个屏幕,表示整个视图都需要绘制
    //第一次绘制流程,需要绘制所有视图
    if (fullRedrawNeeded) {
        mAttachInfo.mIgnoreDirtyState = true;
        dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
    }
    ......
    if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
                return;
        }
}
           

往下走最终调了ViewRootImpl#drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,boolean scalingRequired, Rect dirty)的方法,这个方法里面调了 mView.draw(canvas); 其中这个mView是我们最外层的View也就是DecorView,再由最外层的View往里面走调用了dispatchDraw(canvas);,一层一层的绘制,最终画到当前的调用了invalidate()方法的View的onDraw

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
            boolean scalingRequired, Rect dirty) {
    // Draw with software renderer.
    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区域,由dirty区域决定
        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;
        }

        canvas.setDensity(mDensity);
    } 

    try {
		......
            //正式开始绘制
            mView.draw(canvas);
		......
        }
    } 
    return true;
}