1萨驶、一切的開端谨娜,Activity的setContentView()
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
public Window getWindow() {
return mWindow;
}
mWindow其實就是一個PhoneWindow
2赠堵、PhoneWindow
//...
private ViewGroup mContentParent;
//...
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
看到這一行mLayoutInflater.inflate(layoutResID, mContentParent)
就知道Activity中setContentView()碾褂,其實主要就是mContentParent.addView(view)脑蠕,mContentParent是ViewGroup
3酌泰、ViewGroup
public void addView(View child, int index, LayoutParams params) {
if (DBG) {
System.out.println(this + " addView");
}
if (child == null) {
throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
}
// addViewInner() will call child.requestLayout() when setting the new LayoutParams
// therefore, we call requestLayout() on ourselves before, so that the child's request
// will be blocked at our level
requestLayout();
invalidate(true);
addViewInner(child, index, params, false);
}
重點requestLayout()
4媒佣、View的requestLayout()
public void requestLayout() {
if (mMeasureCache != null) mMeasureCache.clear();
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;
}
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
mAttachInfo.mViewRequestingLayout = null;
}
}
重點是mParent.requestLayout(),很明顯這個調(diào)用會向上遞歸
回到PhoneWindow里的mContentParent
mContentParent = generateLayout(mDecor);
protected ViewGroup generateLayout(DecorView decor) {
//...
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
//...
return contentParent ;
}
跟蹤findViewById()
public View findViewById(@IdRes int id) {
return getDecorView().findViewById(id);
}
得知mContentParent的parent
就是我們常在Activity里寫的getWindow().getDecorView()
再看decor的parent是什么陵刹,Activity里面
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
public WindowManager getWindowManager() {
return mWindowManager;
}
//....
mWindowManager = mWindow.getWindowManager();
//....
跟蹤wm.addView()默伍,Window類里面
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
public WindowManager getWindowManager() {
return mWindowManager;
}
跟蹤WindowManagerImpl
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
mGlobal就是WindowManagerGlobal
public void addView(View view, ViewGroup.LayoutParams params,
//...
ViewRootImpl root;
//...
root = new ViewRootImpl(view.getContext(), display);
//...
root.setView(view, wparams, panelParentView);
//...
}
跟蹤root.setView(),ViewRootImpl類里面
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
//...
view.assignParent(this);
//...
}
assignParent()衰琐,顧名思義就是設(shè)置View的mParent
跟了這么久也糊,得知DecorView的parent就是ViewRootImpl
5、ViewRootImpl
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
private void performTraversals() {
//...
performMeasure()
//...
performLayout()
//...
performDraw()
}
這個類里requestLayout()方法最終會調(diào)用performTraversals()
總結(jié):
ViewGroup.addView(view)會調(diào)用requestLayout()
View.requestLyaout()每次都會遞歸回到Activity最上層的那個DecorView的parent就是ViewRootImpl
然后ViewRootImpl開始執(zhí)行measure()羡宙、layout()狸剃、draw()
這幾個方法里面會再遞歸遍歷執(zhí)行child View的measure()、layout()狗热、draw()
當(dāng)然并不是一個View的一次requestLyaout()就會觸發(fā)所有View的重繪钞馁,View繪制方法中會判斷child View是否需要重繪虑省。
invalidate()方法整個流程原理也大同小異,也是遞歸向上僧凰,最終還是進(jìn)入ViewRootImpl探颈,調(diào)用scheduleTraversals()