做自定义View 都知道这三个方法,主要用来通知重绘的,但区别是什么呢,我们来从源码的角度来分析
invalidate()
这个是最常用的方法了,在自定义View 修改内容后通知重绘,显示修改后的内容,来看做了什么
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) {
//根据传过来的值,直到这两个判断都为 true
if (fullInvalidate) {
mLastIsOpaque = isOpaque();
//设置标志位
mPrivateFlags &= ~PFLAG_DRAWN;
}
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);
//直接调用 mParent 的invalidateChild
p.invalidateChild(this, damage);
}
}
这里的 mParent 就是父布局,父布局一般都为ViewGroup,来看ViewGroup中的invalidateChild
public final void invalidateChild(View child, final Rect dirty) {
//不断循环,直到 parent 为null
do {
//调用 mParent 的 invalidateChildInParent
parent = parent.invalidateChildInParent(location, dirty);
} while (parent != null);
}
}
public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
//根据mPrivateFlages 标志为来判断,
//前面调用invalidateInternal 方法时已经给这个标志为赋值,所以会进来
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0) {
//直接返回 mParent
return mParent;
}
return null;
}
到这里,大概能清楚了,当调用invalidate 方法时,会一直向上请求,调用父类的 invalidateChildInParent 直到 mParent 的值为null,那么什么时候 mParent 为null 呢?根据View的绘制流程可以知道当为ViewRootImp 时,mParent 为null,那么最后走的就是ViewRootImp 中的invalidateChildInParent 方法
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
//检查线程,所以在非主线程中调用会抛异常
checkThread();
//调用该方法
invalidateRectOnScreen(dirty);
return null;
}
private void invalidateRectOnScreen(Rect dirty) {
//调用该方法
scheduleTraversals();
}
//到这里又回到了View 的绘制流程中,所以我们知道最后会走到 performTraversals 中
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
到这里又回到了View 的绘制流程中,所以我们知道最后会走到 performTraversals 中,来看
private void performTraversals() {
//.......
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
//.......
performLayout(lp, mWidth, mHeight);
//......
performDraw();
}
最终又会走一遍View 的绘制流程,但当调用invalidate 时候并不会走 onMeasure 和 onLayout 方法,这又是为什么呢?继续看performMeasure 和 performLayout 方法
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
//此时的mView 就是DecorView
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
//根据 mPrivateFlags 的标志为判断是否需要 measured
final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
if (forceLayout || needsLayout) {
// first clears the measured dimension flag
mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
// onMeasure
onMeasure(widthMeasureSpec, heightMeasureSpec);
mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
}
}
其实就是根据 mPrivateFlags 的值来判断是否需要 onMeasure 这个值什么时候会清空呢?不要急,继续看 performLayout
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
// mView 为DecorView
final View host = mView;
if (host == null) {
return;
}
//调用 layout
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
}
public void layout(int l, int t, int r, int b) {
//也是根据 mPrivateFlags 来判断 是否需要调用onLayout
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
//onLayout 完之后清除 mPrivateFlags 中的 PFLAG_LAYOUT_REQUIRED 标志
mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
//清除 mPrivateFlags 中的 PFLAG_FORCE_LAYOUT
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
}
}
所以当我们调用 invalidate 方法是,会判断mPrivateFlags中是否包含 PFLAG_LAYOUT_REQUIRED 和 PFLAG_FORCE_LAYOUT 这两个标志位,从而判断是否调用onMeasure 和 onLayout 方法,而当我们打开页面时,View 的绘制流程会走一遍,从而清除这两个标志为,所以再调用 invalidate 方法时是不会在调用 onMeasure 和 onLayout 方法的。如果我们需要调用这两个方法该怎么办呢?可以使用 requestLayout 方法
requestLayout()
见名知意,请求Layout,来看源码
public void requestLayout() {
//注意 这里将这两个标志位赋值给 mPrivateFlags
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
//调用 mParent 的 requestLayout
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
}
根据View 的绘制流程 得知最终会调用ViewRootImp 中的 requestLayout 方法
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
//检查线程
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
最后又回到了这里,根据前面的分析,又会走一遍View 的绘制,但此时 mPrivateFlags 已经被赋值,所以也会走 onMeasure 和 onLayout 方法。所以 invalidate 方法和 requestLayout 方法的区别就在于是否调用 onMeasure 和 onLayout 方法,共同点在于都以请求一遍 View 绘制流程。最后还有 postInvalidate 方法
postInvalidate()
public void postInvalidate() {
postInvalidateDelayed(0);
}
public void postInvalidateDelayed(long delayMilliseconds) {
//根据前面文章分析的 mAttachInfo 不为null
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds);
}
}
//使用 mHandler 进行通信
//根据Handler 的原理可以知道最后会走到 mHandler 中的handleMessage方法中
public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
mHandler.sendMessageDelayed(msg, delayMilliseconds);
}
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_INVALIDATE:
//最后还是调用了自身View.invalidate 方法
((View) msg.obj).invalidate();
break;
}
}
postInvalidate 方法最后还是调用了 invalidate 方法,和 invalidate 的区别就在于中间使用了一次 Handler 调度,所以 postInvalidate 可以在非主线程中调用,内部会使用 Handler 来切换回主线程