那些情况会引起ViewTree的遍历
1)应用程序刚启动时,会在构造出整棵ViewTree后,执行第一次遍历。
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) @ViewRootImpl.java{
requestLayout();
}
public void requestLayout() @ViewRootImpl.java{
if (!mHandlingLayoutInLayoutRequest) {
//检查是否是主线程。
CheckThread();
//表明当前正在发起layout请求。
mLayoutRequested = true;
//安排一个遍历。
scheduleTraversals();
}
}
2)requestLayout()@View.java,view对象可以通过调用requestLayout来主动请求遍历。
//当有些什么变化时调用整个方法来刷新view的布局。这会安排一次viewtree的layout传递,但是如果view层级正在执行layout传递不应该调用requestLayout,如果layout正在执行,那么这次请求可能会被放置到当前layout传递的尾部。子类应该重写这个方法。
public void requestLayout() @View.java{
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
ViewRootImpl viewRoot = getViewRootImpl();
//isInLayout()判断当前是否正在进行layout,如果当前正在执行layout,又强制调用了requestLayout ,那么把这个view放在mLayoutRequesters的尾部。
if (viewRoot != null && viewRoot.isInLayout()) {
if (!viewRoot.requestLayoutDuringLayout(this)) {
return;
}
}
mAttachInfo.mViewRequestingLayout = this;
}
//PFLAG_FORCE_LAYOUT,说明是程序主动请求的layout。
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
//把layout请求提交给它的父类,它的父类可能是ViewGroup,也可能是ViewRoot,最后都要提交到ViewRoot处理,ViewRootImpl中的 requestLayout跟前面提到的应用程序的第一次遍历调用的是同一个函数。
if (mParent != null && !mParent.isLayoutRequested()) {
mParent. requestLayout();
}
}
3)setLayoutParams,设置View对象的各种属性。
public void setLayoutParams(ViewGroup.LayoutParams params) @View.java{
//如果父类是ViewGroup,调用 onSetLayoutParams做参数调整, onSetLayoutParams通常是被ViewGroup的子类重写的。
if (mParent instanceof ViewGroup) {
((ViewGroup) mParent).onSetLayoutParams(this, params);
}
//这个调用跟前一个情况一样。
requestLayout();
}
4)invalidate,当前的UI无效了,所以需要重绘。这个方法有多个重载,最后的实现是invalidateInternal。invalidate只能在UI线程调用,其他线程调用postInvalidate()。
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,boolean fullInvalidate)@View.java {
//如果view不可见,并且也没有动画在执行,就跳过这次刷新。
if (skipInvalidate()) {
return;
}
//满足以下条件,才可能执行invalidate。
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)) {
//fullInvalidate是否全面刷新,如果没有明确标记刷新的范围(调用的是有rect参数、或者有r,t,l,b参数的invalidate()方法)
if (fullInvalidate) {
mLastIsOpaque = isOpaque();
mPrivateFlags &= ~PFLAG_DRAWN;
}
mPrivateFlags |= PFLAG_DIRTY;
//在不全部刷新的情况下,将需要刷新的区域传递到它的父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);
}
}
}
在调用invalidateChild往上传递时,ViewParent可能是ViewGroup,也可能是ViewRootImpl,但是最终还是要由ViewRootImpl处理,ViewGroup对invalidateChild的处理是从当前点开始,沿着ViewTree回溯收集dirty区域,ViewRootImpl对invalidateChild的处理是从ViewTree的根开始,发起ViewTree的遍历。
5)setAppVisibility,当应用程序的可见性发生了变化,会调用WMS的这个函数,然后进一步通过WindowState的变量mClient(ViewRootImpl中W类型的对象)调用到ViewRootImpl的dispatchAppVisibility,ViewRootImpl收到可见性变化的消息,也会通过scheduleTraversals发起一次遍历。
scheduleTraversals是ViewRootImpl中遍历的起点。
void scheduleTraversals() @ViewRootImpl.java{
//当前是不是正在执行遍历。
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//向 Choreographer注册了 CALLBACK_TRAVERSAL类型的会调用,一旦有Vsync信号到来,会回调 mTraversalRunnable中的run方法,Vsync是准备UI数据的触发源。
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
TraversalRunnable的run方法,直接调用doTraversal。
void doTraversal()@ViewRootImpl.java {
if (mTraversalScheduled) {
//把 mTraversalScheduled复位,然后执行遍历 performTraversals。
mTraversalScheduled = false;
performTraversals();
}
}