1.activity的setcontent流程
public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar(); }这内里是phonewindow调用setcontentview
@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流程
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();//也就是调用installDecorfinal 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);
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丢两张结构图
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视图代替。 |