关键词:View ViewGroup ViewRootImpl
invalidate是如何刷新view的?
View
/**
* 必须可见才能刷新,运行于UI线程
* Invalidate the whole view. If the view is visible,
* This must be called from a UI thread. To call from a non-UI thread, call
* {@link #postInvalidate()}.
*/
public void invalidate() {
invalidate(true);
}
/**
* invalidateCache设置为true,会刷新所有的view,包括大小没有改变的view
* 设置为false,只会刷新需要被刷新的view,例如大小发生改变
*/
public void invalidate(boolean invalidateCache) {
//mRight - mLeft 该view占据的宽度范围,mBottom - mTop高度范围
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;
}
//跳过刷新的条件
//View不是可见的 && 存在动画对象 && 父视图不是ViewGroup或者不是过渡态
if (skipInvalidate()) {
return;
}
//如下条件才可以重绘:正在动画或者View大小不是0 || 需要完整绘制绘制并且绘制缓存可用 || 未重绘过 || 透明度和上次比较有了变化
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)) {
......
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);
}
......
}
}
其中的这个ViewParent p,ViewParent 是一个接口,它的子类,ViewGroup,ViewRootImpl,在两个字类中都有invalidateChild方法,如下
ViewGroup中
@Deprecated
@Override
public final void invalidateChild(View child, final Rect dirty) {
......
do {
......
parent = parent.invalidateChildInParent(location, dirty);
......
} while (parent != null);
}
}
/**
* 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.
*
* @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) {
// either DRAWN, or 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);
}
final int left = mLeft;
final int top = mTop;
if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
if (!dirty.intersect(0, 0, mRight - left, mBottom - top)) {
dirty.setEmpty();
}
}
location[CHILD_LEFT_INDEX] = left;
location[CHILD_TOP_INDEX] = top;
} else {
if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
dirty.set(0, 0, mRight - mLeft, mBottom - mTop);
} else {
// in case the dirty rect extends outside the bounds of this container
dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
}
location[CHILD_LEFT_INDEX] = mLeft;
location[CHILD_TOP_INDEX] = mTop;
mPrivateFlags &= ~PFLAG_DRAWN;
}
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
if (mLayerType != LAYER_TYPE_NONE) {
mPrivateFlags |= PFLAG_INVALIDATED;
}
return mParent;
}
return null;
}
可以看出,当调用了子view的invalidate方法后,刷新是逐层往上进行的,通过一个do while循环不断的获取上层的父布局,该循环的作用主要是不断向上回溯父容器,求得父容器和子View需要重绘的区域的并集(dirty)。调用父布局的invalidateChildInParent方法,直到最上层没有parent为止,最后一次调用会进入到ViewRootImpl的invalidateChildInParent方法。总的来说事件层层向上传递,直到DecorView,而DecorView又会传递给ViewRootImpl,也即是说子View的invalidate事件,最终会被ViewRootImpl接收并得到处理。纵观这个向上传递的流程,其实是采用了责任链模式,即不断向上传递该事件,直到找到能处理该事件的上级,在这里,只有ViewRootImpl能够处理invalidate事件,在ViewGroup中我们是看不到有效的处理方法的
ViewRootImpl中
@Override
public void invalidateChild(View child, Rect dirty) {
invalidateChildInParent(null, dirty);
}
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
//线程检查,判断如果当前线程不在主线程,则抛出异常,
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);
}
}
invalidateRectOnScreen(dirty);
return null;
}
可以看到不论是当dirty==null时走的invalidate()方法还是invalidateRectOnScreen方法,最终都会调用scheduleTraversals()方法
scheduleTraversals
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
开启了一个Runnable线程
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
doTraversal
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
performTraversals
private void performTraversals() {
......
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
......
performLayout(lp, mWidth, mHeight);
.....
performDraw();
......
}
到了这里会回调onMeasure onLayout onDraw方法,现在我们先关注performDraw这个方法
private void performDraw() {
......
try {
draw(fullRedrawNeeded);
} finally {
mIsDrawing = false;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
......
}
private void draw(boolean fullRedrawNeeded) {
....
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
....
}
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
// Draw with software renderer.
//最终还是canvas来绘制
final Canvas canvas;
......
//这个View是之前循环遍历出来的最外层的那个view,调用draw方法,会不断的向下绘制,在View的源码中可以看到在绘制的同时,还在向下分发draw事件:dispatchDraw(canvas);
mView.draw(canvas);
......
}
postInvalidate是如何刷新view的?
public void postInvalidate() {
postInvalidateDelayed(0);
}
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);
}
}
ViewRootImpl,可以看到还是在主线程刷新的,只不过是从子线程发送到主线程处理的
public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
mHandler.sendMessageDelayed(msg, delayMilliseconds);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_INVALIDATE:
//调用了View的invalidate
((View) msg.obj).invalidate();
break;
}
}
......
}
requestLayout是如何刷新view的?
@CallSuper
public void requestLayout() {
......
//判断如果当前View树是否正在布局流程,返回
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;
}
//这两个标记很关键,是区分measure layout draw方法是否执行的标记
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
......
}
mParent.requestLayout()方法:调用mParent.requestLayout方法,这个十分重要,因为这里是向父容器请求布局,即调用父容器的requestLayout方法,而父容器又会调用它的父容器的requestLayout方法,即requestLayout事件层层向上传递,直到DecorView,即根View,而根View又会传递给ViewRootImpl,也即是说子View的requestLayout事件,最终会被ViewRootImpl接收并得到处理。纵观这个向上传递的流程,其实是采用了责任链模式,即不断向上传递该事件,直到找到能处理该事件的上级,在这里,只有ViewRootImpl能够处理requestLayout事件
在ViewRootImpl中
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
到这里,基本就和invalidate方法源码走入了相同的处理方法中,不同的是,根据标记的不同而进行不同的处理
问题:invalidate和requestLaout区别何在?
通过上边的源码分析,我们已经知道,invalidate和requestLayout最终殊途同归,汇集到了一个方法中,就是scheduleTraversals,那么它们逻辑有何不同呢,其实区别在这里
private void performTraversals() {
......
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
......
performLayout(lp, mWidth, mHeight);
.....
performDraw();
......
}
当源码走到这个方法中后,会分别去调用View的measure layout 和draw方法,但是其实这并不能说明,onMeasure onLayout onDraw方法都会被回调,这时,之前设置的标记就起作用了
我们来看measure
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
......
//可以看到PFLAG_FORCE_LAYOUT是requestLayout时设置的标记,所以requestLayout会最终执行onMeasure,invalidate则相反
final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
......
if (forceLayout || needsLayout) {
......
onMeasure(widthMeasureSpec, heightMeasureSpec);
.....
//又设置了一个标记,留心,这个很有用
mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
}
}
layout
public void layout(int l, int t, int r, int b) {
......
//PFLAG_LAYOUT_REQUIRED是measure中回调了onMeasure之后设置的标记,这里用到了,也就是requestLayout代码走到onMeasure之后还会走到这里的onLayout
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
......
}
}
draw,两者都会回调
@CallSuper
public void draw(Canvas canvas) {
......
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
//回调onDraw方法
if (!dirtyOpaque) onDraw(canvas);
}
}
所以二者区别在于requestLayout 会回调三个,invalidate只会回调onDraw