1.activity的setcontent流程
less
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
这里面是phonewindow调用setcontentview
scss
@Override
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();//1.创建 DecorView 拿到 mContentParent
} 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);2.将自己的xml布局加载进mContentParent里面
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);//生成Decorview
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);返回contentParent,一些activity的系统主题在里面设置比如screen_custom_title ,R.layout.screen_simple
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
2.AppCompatActivity的setcontentview流程
scss
getDelegate().setContentView(layoutResID);
@Override
public void setContentView(View v) {
ensureSubDecor();
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
contentParent.addView(v);
mAppCompatWindowCallback.getWrapped().onContentChanged();
}
mSubDecor = createSubDecor();
createSubDecor()方法里面主要看
// Now let's make sure that the Window has installed its decor by retrieving it
ensureWindow();//拿到phonewindow
mWindow.getDecorView();//也就是调用installDecor
final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
if (windowContentView != null) {
// There might be Views already added to the Window's content view so we need to
// migrate them to our content view
while (windowContentView.getChildCount() > 0) {
final View child = windowContentView.getChildAt(0);
windowContentView.removeViewAt(0);
contentView.addView(child);
}
// Change our content FrameLayout to use the android.R.id.content id.
// Useful for fragments.
windowContentView.setId(View.NO_ID);
//修改contentview的id变成android.R.id.content contentView.setId(android.R.id.content);
// The decorContent may have a foreground drawable set (windowContentOverlay).
// Remove this as we handle it ourselves
if (windowContentView instanceof FrameLayout) {
((FrameLayout) windowContentView).setForeground(null);
}
}
// Now set the Window's content view with the decor
mWindow.setContentView(subDecor);
总结
1.activity中setcontentview流程
1.installDecor()->mDecor = generateDecor(-1)生成DecorView->mContentParent =generateLayout(mDecor);加载系统布局到Decorview,mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
--> ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);里面的@android:id/content赋值给mContentParent
2.mLayoutInflater.inflate(layoutResID, mContentParent);
2.AppCompatActivity的setcontentview流程
1.AppCompatDelegate.setContentView ->ensureSubDecor()->createSubDecor里面调用ensureWindow(); // 从Activity 那PhoneWindow,mWindow.getDecorView()里面调用installDecor()
-> final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
-> windowContentView.setId(View.NO_ID); // 将原始的 content id 置为 NO_ID
-> contentView.setId(android.R.id.content); // subDecerView R.id.action_bar_activity_content -> 置为 content
-> mWindow.setContentView(subDecor); //
-> ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
接下来分析mLayoutInflater.inflate(layoutResID, mContentParent);
java
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
final Context inflaterContext = mContext;
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context) mConstructorArgs[0];
mConstructorArgs[0] = inflaterContext;
View result = root;
try {
advanceToRootNode(parser);
final String name = parser.getName();
if (DEBUG) {
System.out.println("**************************");
System.out.println("Creating root view: "
+ name);
System.out.println("**************************");
}
//如果标签是MERGE到这段代码里
if (TAG_MERGE.equals(name)) {
if (root == null || !attachToRoot) {
throw new InflateException("<merge /> can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}
rInflate(parser, root, inflaterContext, attrs, false);
} else {
// Temp is the root view that was found in the xml
//主要看这句话
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
if (root != null) {
if (DEBUG) {
System.out.println("Creating params from root: " +
root);
}
// Create layout params that match root, if supplied
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
temp.setLayoutParams(params);
}
}
if (DEBUG) {
System.out.println("-----> start inflating children");
}
// Inflate all children under temp against its context.创建子布局,然后再调用createViewFromTag
rInflateChildren(parser, temp, attrs, true);
if (DEBUG) {
System.out.println("-----> done inflating children");
}
// We are supposed to attach all the views we found (int temp)
// to root. Do that now.
if (root != null && attachToRoot) {
root.addView(temp, params);
}
// Decide whether to return the root that was passed in or the
// top view found in xml.
if (root == null || !attachToRoot) {
result = temp;
}
}
} catch (XmlPullParserException e) {
final InflateException ie = new InflateException(e.getMessage(), e);
ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} catch (Exception e) {
final InflateException ie = new InflateException(
getParserStateDescription(inflaterContext, attrs)
+ ": " + e.getMessage(), e);
ie.setStackTrace(EMPTY_STACK_TRACE);
throw ie;
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
return result;
}
}
查看主要看
if (view == null) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf('.')) {
//如果是线性相对等没有.的布局走这个
view = onCreateView(context, parent, name, attrs);
} else {
//比如androidx.constraintlayout.widget.ConstraintLayout
view = createView(context, name, null, attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
}
点击进去最后都会调用到LayoutInflater的createView方法
clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
mContext.getClassLoader()).asSubclass(View.class);
constructor = clazz.getConstructor(mConstructorSignature);
final View view = constructor.newInstance(args);
通过反射生成view
丢两张布局图
image.png
image.png
inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) 解释
第一个是加载的资源文件
第二个父控件view
第三个是否attachToRoot里面带有addview功能,就是是否想让其处于某一个容器中
如果父控件设置为null就会使加载的子控件的父控件设置的长宽失效,子控件的大小由子控件里面的子view决定
ViewStub跟include差不多,懒加载隐藏布局,他就是一个宽高都为0的view,这里需要注意的一点是,当ViewStub被inflate到parent时,ViewStub就被remove掉了,即当前view hierarchy中不再存在ViewStub,而是使用对应的layout视图代替。