当前位置: 首页 > 工具软件 > lin-view-ui > 使用案例 >

Android GUI系统-ViewTree的遍历(四)

锺离刚洁
2023-12-01

那些情况会引起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();
	}
}

2requestLayout()@View.javaview对象可以通过调用requestLayout来主动请求遍历。

//当有些什么变化时调用整个方法来刷新view的布局。这会安排一次viewtreelayout传递,但是如果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();
	}
}


3setLayoutParams,设置View对象的各种属性。


public void setLayoutParams(ViewGroup.LayoutParams params) @View.java{
//如果父类是ViewGroup,调用 onSetLayoutParams做参数调整, onSetLayoutParams通常是被ViewGroup的子类重写的。
	if (mParent instanceof ViewGroup) {
		((ViewGroup) mParent).onSetLayoutParams(this, params);
	}
//这个调用跟前一个情况一样。
	requestLayout();
}


4invalidate,当前的UI无效了,所以需要重绘。这个方法有多个重载,最后的实现是invalidateInternalinvalidate只能在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处理,ViewGroupinvalidateChild的处理是从当前点开始,沿着ViewTree回溯收集dirty区域,ViewRootImplinvalidateChild的处理是从ViewTree的根开始,发起ViewTree的遍历。


5setAppVisibility,当应用程序的可见性发生了变化,会调用WMS的这个函数,然后进一步通过WindowState的变量mClientViewRootImplW类型的对象)调用到ViewRootImpldispatchAppVisibilityViewRootImpl收到可见性变化的消息,也会通过scheduleTraversals发起一次遍历。


scheduleTraversalsViewRootImpl中遍历的起点。


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


 类似资料: