示范校建设验收网站,做网站推广有用吗,网站开发公司需要招聘哪些人,企业文化建设方案一、简介
setContentView我们在Activity中经常见到#xff0c;它的作用就是把我们的布局文件放在Activity中显示#xff0c;下面我们根据源码分析setContentView是如何做到的
二、源码分析
1.两种setContentView
注意Activity的setContentView和AppCompatActivity的setCo…一、简介
setContentView我们在Activity中经常见到它的作用就是把我们的布局文件放在Activity中显示下面我们根据源码分析setContentView是如何做到的
二、源码分析
1.两种setContentView
注意Activity的setContentView和AppCompatActivity的setContentView是有一些区别的所以我们要分析两钟setContentView下面先分析Activity的
2.Activity的setContentView
(1).从Activity的setContentView这个方法开始
public void setContentView(LayoutRes int layoutResID) {getWindow().setContentView(layoutResID);initWindowDecorActionBar();
}可以看到第一句getWindow().setContentView(layoutResID)这个getWindow是获取当前Activity的Window在Android中Window的实现类是phoneWindow,所以我们要看phoneWindow的setContentView
顺便提一下Activity的window的创建时机是在Activity的attach方法: (2).继续跟踪到phoneWindow的setContentView 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();}mContentParentExplicitlySet true; //⭐这个flag记一下}上面代码中我标记了三处重点我们下面继续分析这三个重点都干了什么先分析第一个installDecor()
(2.1).分析phoneWindow的setContentView的第一个关键点installDecor() installDecor主要是我用红框标记出来的是重点我们先分析generateDecor(-1)这个方法:
(2.1.1).分析installDecor()方法中的generateDecor(-1)方法 protected DecorView generateDecor(int featureId) {// System process doesnt have application context and in that case we need to directly use// the context we have. Otherwise we want the application context, so we dont cling to the// activity.Context context;if (mUseDecorContext) {Context applicationContext getContext().getApplicationContext();if (applicationContext null) {context getContext();} else {context new DecorContext(applicationContext, this);if (mTheme ! -1) {context.setTheme(mTheme);}}} else {context getContext();}return new DecorView(context, featureId, this, getAttributes());//⭐重点}创建了一个DecorView并且返回之后赋值给了mDecor我们先看一下这个DecorVIew是什么: 很明显是一个FrameLayout这下我们知道了创建了一个FrameLayout类型的DecorView然后赋值给了mDecor变量,下面继续分析installDecor的第二个重点generateLayout(mDecor)
(2.1.2).分析installDecor的第二个重点generateLayout(mDecor) protected ViewGroup generateLayout(DecorView decor) {...
else {// Embedded, so no decoration is needed.layoutResource R.layout.screen_simple; // System.out.println(Simple!);}mDecor.startChanging();mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); //⭐重点下面的方法ViewGroup contentParent (ViewGroup)findViewById(ID_ANDROID_CONTENT);if (contentParent null) {throw new RuntimeException(Window couldnt find content container view);}...return contentParent;}//⭐DecorView的onResourcesLoaded方法void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {if (mBackdropFrameRenderer ! null) {loadBackgroundDrawablesIfNeeded();mBackdropFrameRenderer.onResourcesLoaded(this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),getCurrentColor(mNavigationColorViewState));}mDecorCaptionView createDecorCaptionView(inflater);final View root inflater.inflate(layoutResource, null);//⭐重点主线流程if (mDecorCaptionView ! null) {if (mDecorCaptionView.getParent() null) {addView(mDecorCaptionView,new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));}mDecorCaptionView.addView(root,new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));} else {// Put it below the color views. ⭐重点主线流程addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));}mContentRoot (ViewGroup) root;initializeElevation();}这个方法的作用就是通过我们设置的style或者requestWindowFuture等来选出一个系统自带的布局文件默认的是R.layout.screen_simple选出布局文件后通过调用mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);方法inflate出来后add到DecorView上我们详细看一下R.layout.screen_simple这个布局文件:
LinearLayout xmlns:androidhttp://schemas.android.com/apk/res/androidandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:fitsSystemWindowstrueandroid:orientationverticalViewStub android:idid/action_mode_bar_stubandroid:inflatedIdid/action_mode_barandroid:layoutlayout/action_mode_barandroid:layout_widthmatch_parentandroid:layout_heightwrap_contentandroid:theme?attr/actionBarTheme /FrameLayoutandroid:idandroid:id/contentandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:foregroundInsidePaddingfalseandroid:foregroundGravityfill_horizontal|topandroid:foreground?android:attr/windowContentOverlay /
/LinearLayout就是这个布局文件被inflate到DecorView上然后通过
ViewGroup contentParent (ViewGroup)findViewById(ID_ANDROID_CONTENT);这一句获取到这个是content的FrameLayout然后返回了这个contentParent
到这里installDecor这个方法分析完了总结一下installDecor方法主要就是创建DecorView然后把选出的布局文件add到DecorView上然后再通过findViewbyId找到类型是FrameLayout的content赋值给contentParent 返回其实还有一步是DecorView和phoneWindow结合这里不细说以后有FrameWorker源码解析再说。
(2.2).继续分析phoneWindow的setContentView的第二个关键流程重点
mLayoutInflater.inflate(layoutResID, mContentParent);这句很明显layoutResID是我们的activity_main.layout这种自己写的布局文件把它inflate到mContentParent中通过图片让大家有一个更清晰的感官: (2.3).继续分析phoneWindow的setContentView的第三个关键流程重点
mContentParentExplicitlySet true;这个flag的作用首先我们先看一段代码:
public class MainActivity extends AppCompatActivity {Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);requestWindowFeature(Window.FEATURE_NO_TITLE);}
}这段代码运行会报错:
requestFeature() must be called before adding content为什么会报这个错误呢从代码上来找: public final boolean requestWindowFeature(int featureId) {return getWindow().requestFeature(featureId);}//我们已经知道getWindow其实获取的是PhoneWindow所以调用的是PhoneWindow的requestFeatureOverridepublic boolean requestFeature(int featureId) {if (mContentParentExplicitlySet) { //⭐这就是报错的根源throw new AndroidRuntimeException(requestFeature() must be called before adding content); }final int features getFeatures();final int newFeatures features | (1 featureId);if ((newFeatures (1 FEATURE_CUSTOM_TITLE)) ! 0 (newFeatures ~CUSTOM_TITLE_COMPATIBLE_FEATURES) ! 0) {// Another feature is enabled and the user is trying to enable the custom title feature// or custom title feature is enabled and the user is trying to enable another featurethrow new AndroidRuntimeException(You cannot combine custom titles with other title features);}if ((features (1 FEATURE_NO_TITLE)) ! 0 featureId FEATURE_ACTION_BAR) {return false; // Ignore. No title dominates.}if ((features (1 FEATURE_ACTION_BAR)) ! 0 featureId FEATURE_NO_TITLE) {// Remove the action bar feature if we have no title. No title dominates.removeFeature(FEATURE_ACTION_BAR);}if (featureId FEATURE_INDETERMINATE_PROGRESS getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {throw new AndroidRuntimeException(You cannot use indeterminate progress on a watch.);}return super.requestFeature(featureId);}看到了报错的根源其实就是mContentParentExplicitlySet这个flag在setContentView执行完就设置成了true所以调用requestWindowFeature(Window.FEATURE_NO_TITLE);方法必须在setContentView之前否则就会抛出异常最后从设计的角度分析为什么要设计这么一个flag呢或者说为什么非要在setContentView之前执行requestFeature因为在setContentView中需要通过设置的这些requestWindowFeature的flag去选择一个布局文件然后add到DecorView上如果在setContentView后面设置就起不到作用所以有了这个设计。
3.AppCompatActivity的setContentView
(1).AppCompatActivity的setContentView源码 Overridepublic void setContentView(LayoutRes int layoutResID) {getDelegate().setContentView(layoutResID);}(1.1).getDelegate() NonNullpublic AppCompatDelegate getDelegate() {if (mDelegate null) {mDelegate AppCompatDelegate.create(this, this);}return mDelegate;}实际上实现类是AppCompatDelegategetDelegate().setContentView(layoutResID);的setContentView实际上是AppCompatDelegate的setContentView方法
(2).AppCompatDelegate的setContentView方法 Overridepublic void setContentView(int resId) {ensureSubDecor(); //⭐重点主线ViewGroup contentParent mSubDecor.findViewById(android.R.id.content); //⭐重点主线contentParent.removeAllViews();LayoutInflater.from(mContext).inflate(resId, contentParent);//⭐重点主线mAppCompatWindowCallback.getWrapped().onContentChanged();}(2.1).分析ensureSubDecor()方法 private void ensureSubDecor() {if (!mSubDecorInstalled) {mSubDecor createSubDecor(); // ⭐重点主线流程// If a title was set before we installed the decor, propagate it nowCharSequence title getTitle();if (!TextUtils.isEmpty(title)) {if (mDecorContentParent ! null) {mDecorContentParent.setWindowTitle(title);} else if (peekSupportActionBar() ! null) {peekSupportActionBar().setWindowTitle(title);} else if (mTitleView ! null) {mTitleView.setText(title);}}applyFixedSizeWindow();onSubDecorInstalled(mSubDecor);mSubDecorInstalled true;//⭐这个flag参数// Invalidate if the panel menu hasnt been created before this.// Panel menu invalidation is deferred avoiding application onCreateOptionsMenu// being called in the middle of onCreate or similar.// A pending invalidation will typically be resolved before the posted message// would run normally in order to satisfy instance state restoration.PanelFeatureState st getPanelState(FEATURE_OPTIONS_PANEL, false);if (!mIsDestroyed (st null || st.menu null)) {invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);}}}主要的第一句:
mSubDecor createSubDecor();mSubDecor是一个ViewGroup类型的对象下面我们分析createSubDecor()
(2.1.1).ensureSubDecor()方法中的createSubDecor()方法 private ViewGroup createSubDecor() {TypedArray a mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);//⭐这个错误是不是曾经见到过如果用的Theme不是AppCompatTheme的就会报错if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {a.recycle();throw new IllegalStateException(You need to use a Theme.AppCompat theme (or descendant) with this activity.);}
......// Now lets make sure that the Window has installed its decor by retrieving itensureWindow(); mWindow.getDecorView(); final LayoutInflater inflater LayoutInflater.from(mContext);ViewGroup subDecor null;if (!mWindowNoTitle) {
......} else {
......subDecor (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
......}
......final ContentFrameLayout contentView (ContentFrameLayout) subDecor.findViewById(R.id.action_bar_activity_content);final ViewGroup windowContentView (ViewGroup) mWindow.findViewById(android.R.id.content);if (windowContentView ! null) {// There might be Views already added to the Windows content view so we need to// migrate them to our content viewwhile (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.setId(android.R.id.content);// The decorContent may have a foreground drawable set (windowContentOverlay).// Remove this as we handle it ourselvesif (windowContentView instanceof FrameLayout) {((FrameLayout) windowContentView).setForeground(null);}}// Now set the Windows content view with the decormWindow.setContentView(subDecor);
......return subDecor;}上面显示出来的基本上都很重要我们一句一句分析:
(2.1.1.1).ensureWindow(); private void ensureWindow() {// We lazily fetch the Window for Activities, to allow DayNight to apply in// attachBaseContextif (mWindow null mHost instanceof Activity) {attachToWindow(((Activity) mHost).getWindow());}if (mWindow null) {throw new IllegalStateException(We have not been given a Window);}}首先我们要明确AppCompatActivity是继承自Activity的所以window也是在attach方法中创建的在AppCompatDelegateImpl中也维护了一个Window类型的变量是mWindow就是通过这个ensureWindow方法经过检查后赋值过来的。说白了ensureWindow方法就是把AppCompatActivity中的Window对象赋值到AppCompatDelegateImpl对象中当然对window设置的callBack啥的也换成AppCompatDelegateImpl中的。
(2.1.1.2).mWindow.getDecorView()方法 Window的实现类所以我们要看PhoneWindow的getDecorView()方法: Overridepublic final NonNull View getDecorView() {if (mDecor null || mForceDecorInstall) {installDecor();}return mDecor;}可以看到是调用了installDecor和Activity有了相同的部分我们简单回忆一下installDecor干了啥首先创建了DecorView然后通过解析我们style的设置选出合适的系统自带布局文件把它add到DecorView上并且返回了一个id是com.android.internal.R.id.content的FrameLayout将来我们要把我们的activity_mai的layout文件add到这个content上面。
(2.1.1.3).后面要分析的这一堆代码就是Activity和AppCompatActivity的setContentView的主要区别
....subDecor (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
....
final ContentFrameLayout contentView (ContentFrameLayout) subDecor.findViewById(R.id.action_bar_activity_content);final ViewGroup windowContentView (ViewGroup) mWindow.findViewById(android.R.id.content);if (windowContentView ! null) {// There might be Views already added to the Windows content view so we need to// migrate them to our content viewwhile (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.setId(android.R.id.content);......}// Now set the Windows content view with the decormWindow.setContentView(subDecor);
......return subDecor;}通过我们设置的style选出一个布局文件这一步好像在installDecor中已经做过了这样好像重复了为什么有这样的一个重复?有这样一个重复是为了不影响原来的代码的同时把一部分对style处理的逻辑转移到AppCompatDelegateImpl中例如对windowTitle的隐藏与显示这里可看出来设计师的设计通过下面的学习慢慢体会先看一下这个布局文件
androidx.appcompat.widget.FitWindowsLinearLayoutxmlns:androidhttp://schemas.android.com/apk/res/androidandroid:idid/action_bar_rootandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:orientationverticalandroid:fitsSystemWindowstrueandroidx.appcompat.widget.ViewStubCompatandroid:idid/action_mode_bar_stubandroid:inflatedIdid/action_mode_barandroid:layoutlayout/abc_action_mode_barandroid:layout_widthmatch_parentandroid:layout_heightwrap_content /include layoutlayout/abc_screen_content_include //androidx.appcompat.widget.FitWindowsLinearLayoutabc_screen_content_include的布局文件:
merge xmlns:androidhttp://schemas.android.com/apk/res/androidandroidx.appcompat.widget.ContentFrameLayoutandroid:idid/action_bar_activity_contentandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:foregroundGravityfill_horizontal|topandroid:foreground?android:attr/windowContentOverlay //merge把这个布局文件inflate成view赋值给subDecorsubDecor是一个ViewGroup
下面重点来了:
final ContentFrameLayout contentView (ContentFrameLayout) subDecor.findViewById(R.id.action_bar_activity_content);这一句是获取subDecor中的id是 action_bar_activity_content的ContentFrameLayout类型的View赋值给contentView
final ViewGroup windowContentView (ViewGroup) mWindow.findViewById(android.R.id.content);windowContentView是获取installDecor中选择的那个布局的id是android.R.id.content的那个FrameLayout
while (windowContentView.getChildCount() 0) {final View child windowContentView.getChildAt(0);windowContentView.removeViewAt(0);contentView.addView(child);
}如果windowContentView中有子View那就全部转移到contentView上
windowContentView.setId(View.NO_ID);
contentView.setId(android.R.id.content);然后把windowContentView的id设置成NO_ID把contentView的id设置成android.R.id.content这就是想把AppCompatDelegateImple中选的这个系统自带布局文件的content替换掉之前的installDecor方法中布局文件的content以后我们的activity_main的layout布局文件就加载在替换之后的content上
mWindow.setContentView(subDecor);这句调用的是phoneWindow的setContentView方法 Overridepublic void setContentView(View view) {setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));}Overridepublic void setContentView(View view, ViewGroup.LayoutParams params) {// 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)) {view.setLayoutParams(params);final Scene newScene new Scene(mContentParent, view);transitionTo(newScene);} else {mContentParent.addView(view, params); //⭐把contentView添加到mContentParent上}mContentParent.requestApplyInsets();final Callback cb getCallback();if (cb ! null !isDestroyed()) {cb.onContentChanged();}mContentParentExplicitlySet true;}我们看一下mContentParent是什么
mContentParent generateLayout(mDecor);是我们取消ID的那个之前的content也就是说把AppCompatDelegateImpl的选择的系统自带布局文件(subDecor)添加到之前的content中最后返回subDecor注意这个subDecor是系统自带布局inflate出来的接下来我们通过一张图加深理解 (2.1.2).ensureSubDecor()方法中的一个boolean值mSubDecorInstalled 这个mSubDecorInstalled和Activity中的mContentParentExplicitlySet一样作用也是防止在setContentView之后调用
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);我们发现和Activity的不一样了我们思考一下为什么调用supportRequestWindowFeature而不是调用requestWindowFeature
Activity中处理这些style是在PhoneWIndow的installDecor中而AppCompatActivity在处理一些style时是在AppCompatDelegateImpl中requestWindowFeature是影响的installDecorsupportRequestWindowFeature是影响的AppCompatDelegateImpl拿window的Title来举例看上面放过的两张图Activity的title是要在installDecor方法中决定的显示与隐藏的而AppCompatActivity的title是放在AppCompatDelegateImpl中决定显示与隐藏的我们用AppCompatActivity肯定是要在AppCompatDelegateImpl进行一些操作而不是对installDecor中进行操作。
下面看一下supportRequestWindowFeature调用逻辑: public boolean supportRequestWindowFeature(int featureId) {return getDelegate().requestWindowFeature(featureId);}Overridepublic boolean requestWindowFeature(int featureId) {featureId sanitizeWindowFeatureId(featureId);if (mWindowNoTitle featureId FEATURE_SUPPORT_ACTION_BAR) {return false; // Ignore. No title dominates.}if (mHasActionBar featureId Window.FEATURE_NO_TITLE) {// Remove the action bar feature if we have no title. No title dominates.mHasActionBar false;}switch (featureId) {case FEATURE_SUPPORT_ACTION_BAR:throwFeatureRequestIfSubDecorInstalled();mHasActionBar true;return true;case FEATURE_SUPPORT_ACTION_BAR_OVERLAY:throwFeatureRequestIfSubDecorInstalled();mOverlayActionBar true;return true;case FEATURE_ACTION_MODE_OVERLAY:throwFeatureRequestIfSubDecorInstalled();mOverlayActionMode true;return true;case Window.FEATURE_PROGRESS:throwFeatureRequestIfSubDecorInstalled();mFeatureProgress true;return true;case Window.FEATURE_INDETERMINATE_PROGRESS:throwFeatureRequestIfSubDecorInstalled();mFeatureIndeterminateProgress true;return true;case Window.FEATURE_NO_TITLE:throwFeatureRequestIfSubDecorInstalled();mWindowNoTitle true;return true;}return mWindow.requestFeature(featureId);}//⭐mSubDecorInstalled 这参数眼熟吧private void throwFeatureRequestIfSubDecorInstalled() {if (mSubDecorInstalled) {throw new AndroidRuntimeException(Window feature must be requested before adding content);}}(2.2).AppCompatDelegateImpl中的setContentView(View v)剩下的一起说
ViewGroup contentParent mSubDecor.findViewById(android.R.id.content); //⭐重点主线
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);//⭐重点主线获取contentview注意这个contentView和Activity的contentView的区别看上面那张图把所有的view清除掉然后把我们的activity_main的layout这种我们自己的布局加载上去
3.分析LayoutInflater.from(mContext).inflate(resId, contentParent);方法
(1).很经典的一道面试题inflate三个参数都有什么作用 或者说这三种写法有什么区别 我们先看源码看完之后总结: public View inflate(LayoutRes int resource, Nullable ViewGroup root, boolean attachToRoot) {final Resources res getContext().getResources();if (DEBUG) {Log.d(TAG, INFLATING from resource: \ res.getResourceName(resource) \ ( Integer.toHexString(resource) ));}View view tryInflatePrecompiled(resource, res, root, attachToRoot);if (view ! null) {return view;}XmlResourceParser parser res.getLayout(resource); //⭐把布局文件用xml解析器解析 try {return inflate(parser, root, attachToRoot); //调用inflate重载的方法} finally {parser.close();}}继续往下看inflate的重载方法这个方法中就有这道面试题的答案: 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(**************************);}if (TAG_MERGE.equals(name)) {if (root null || !attachToRoot) {throw new InflateException(merge / can be used only with a valid ViewGroup root and attachToRoottrue);}rInflate(parser, root, inflaterContext, attrs, false);} else { //⭐重点 else单独分析// Temp is the root view that was found in the xmlfinal 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 suppliedparams 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.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 {// Dont retain static reference on context.mConstructorArgs[0] lastContext;mConstructorArgs[1] null;Trace.traceEnd(Trace.TRACE_TAG_VIEW);}return result;}}上面的代码太多我把关于这道题的主要代码逻辑拿出来:
(1.1).advanceToRootNode(这个方法就是确保第一个解析的是布局的根标签 private void advanceToRootNode(XmlPullParser parser)throws InflateException, IOException, XmlPullParserException {// Look for the root node.int type;while ((type parser.next()) ! XmlPullParser.START_TAG type ! XmlPullParser.END_DOCUMENT) {// Empty}if (type ! XmlPullParser.START_TAG) {throw new InflateException(parser.getPositionDescription() : No start tag found!);}}(1.2).else的逻辑单独分析
else {// Temp is the root view that was found in the xmlfinal 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 suppliedparams 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.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;}}下面逐句分析
(1.2.1)
final View temp createViewFromTag(root, name, inflaterContext, attrs);这一句就是通过name来反射创建view下面会详细分析如何创建的view这个temp就是布局文件的根view
(1.2.2)
if (root ! null) {if (DEBUG) {System.out.println(Creating params from root: root);}// Create layout params that match root, if suppliedparams 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);}
}这就是如果inflate方法的第二个参数root不是null那就执行这段代码
params root.generateLayoutParams(attrs);这段代码的意思是我们把新视图的根布局参数传递给root。让root进行转换成适合它自身布局的布局参数因为不同的布局有不同的特性例如LinearLayout和FrameLayout我们要把布局文件的根temp这个View放到root中就要让temp原来的布局参数转换成适合root这个ViewGroup布局的参数如果inflate第三个参数attachToRoot是false就把布局文件的根view设置成转换的params
(1.2.3)
rInflateChildren(parser, temp, attrs, true);之后就继续解析布局文件通过反射创建View具体如何解析和创建之后详细分析
(1.2.4)
if (root ! null attachToRoot) {root.addView(temp, params);
}如果root不是null并且attachToRoot是true把布局文件根生成的temp这个view添加到root上面注意这个params还是上面转换的那个。
(1.2.5)
if (root null || !attachToRoot) {result temp;
}如果root是null或者attachToRoot是false方法最后的返回值就是这个布局文件生成的View
(1.2.6)
到这里我们就知道了
1.当root不为null时attachTORoot是true的时候就直接把我们布局生成的View添加到root(这个root是inflate方法参数的第二个参数)上面并且方法最后的返回结果是root如果attachTORoot是false直接返回我们布局文件生成的View注意这个生成View的layoutParams已经set了所以可以说
LayoutInflater.from(this).inflate(R.layout.activity_main,root,true);等价于
View view LayoutInflater.from(this).inflate(R.layout.activity_main,root,false);
root.addView(view);2.当root为null时直接返回我们布局文件生成的view注意这个生成的View没有layoutParams
,而且一旦root为null后面的attachToRoot这个参数就失效了。
(2).分析inflate方法如何解析和创建View的 (2.1).在inflate布局文件的根标签时要注意merge标签
if (TAG_MERGE.equals(name)) {if (root null || !attachToRoot) {throw new InflateException(merge / can be used only with a valid ViewGroup root and attachToRoottrue);}rInflate(parser, root, inflaterContext, attrs, false);
}当一个布局文件的根标签是merge时如果root是null或者attachToRoot是false就报错说明布局文件跟标签是merge时不能直接生成一个View必须依附于其他View上。
(2.2).解析不是根标签的布局 void rInflate(XmlPullParser parser, View parent, Context context,AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {final int depth parser.getDepth();int type;boolean pendingRequestFocus false;while (((type parser.next()) ! XmlPullParser.END_TAG ||parser.getDepth() depth) type ! XmlPullParser.END_DOCUMENT) {if (type ! XmlPullParser.START_TAG) {continue;}final String name parser.getName();if (TAG_REQUEST_FOCUS.equals(name)) {pendingRequestFocus true;consumeChildElements(parser);} else if (TAG_TAG.equals(name)) {parseViewTag(parser, parent, attrs);} else if (TAG_INCLUDE.equals(name)) {if (parser.getDepth() 0) {throw new InflateException(include / cannot be the root element);}parseInclude(parser, context, parent, attrs);} else if (TAG_MERGE.equals(name)) {throw new InflateException(merge / must be the root element);} else {final View view createViewFromTag(parent, name, context, attrs);final ViewGroup viewGroup (ViewGroup) parent;final ViewGroup.LayoutParams params viewGroup.generateLayoutParams(attrs);rInflateChildren(parser, view, attrs, true);viewGroup.addView(view, params);}}if (pendingRequestFocus) {parent.restoreDefaultFocus();}if (finishInflate) {parent.onFinishInflate();}}(2.2.1).解析不是根布局时注意对merge、include标签的处理
对merge标签时直接报错因为merge标签只能用于根标签对include标签的处理判断如果include是跟标签则报错因为include标签不能用作根标签。
(2.2.2).其他的view标签会走创建view然后通过父布局生成对应的布局参数LayoutParams然后添加在副布局上
else {final View view createViewFromTag(parent, name, context, attrs);final ViewGroup viewGroup (ViewGroup) parent;final ViewGroup.LayoutParams params viewGroup.generateLayoutParams(attrs);rInflateChildren(parser, view, attrs, true);viewGroup.addView(view, params);
}这一段中createViewFromTag这个方法最重要后面都是生成LayoutParams和添加到父布局上的逻辑最后我们分析createViewFromTag这个方法
(2.2.3).createViewFromTag创建view,注意这个Activity和AppCompatActivity有差别
a.先分析Activity的createViewFromTag创建view的流程 private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {return createViewFromTag(parent, name, context, attrs, false);}UnsupportedAppUsageView createViewFromTag(View parent, String name, Context context, AttributeSet attrs,boolean ignoreThemeAttr) {if (name.equals(view)) {name attrs.getAttributeValue(null, class);}// Apply a theme wrapper, if allowed and one is specified.if (!ignoreThemeAttr) {final TypedArray ta context.obtainStyledAttributes(attrs, ATTRS_THEME);final int themeResId ta.getResourceId(0, 0);if (themeResId ! 0) {context new ContextThemeWrapper(context, themeResId);}ta.recycle();}try {View view tryCreateView(parent, name, context, attrs);if (view null) {final Object lastContext mConstructorArgs[0];mConstructorArgs[0] context;try {if (-1 name.indexOf(.)) { //⭐重点view onCreateView(context, parent, name, attrs); //⭐重点} else {view createView(context, name, null, attrs); //⭐重点}} finally {mConstructorArgs[0] lastContext;}}return view;} catch (InflateException e) {throw e;} catch (ClassNotFoundException e) {final InflateException ie new InflateException(getParserStateDescription(context, attrs) : Error inflating class name, e);ie.setStackTrace(EMPTY_STACK_TRACE);throw ie;} catch (Exception e) {final InflateException ie new InflateException(getParserStateDescription(context, attrs) : Error inflating class name, e);ie.setStackTrace(EMPTY_STACK_TRACE);throw ie;}}我们分析重点:
if (view null) {final Object lastContext mConstructorArgs[0];mConstructorArgs[0] context;try {if (-1 name.indexOf(.)) {view onCreateView(context, parent, name, attrs);} else {view createView(context, name, null, attrs);}} finally {mConstructorArgs[0] lastContext;}
}return view;if (-1 name.indexOf(‘.’))这个判断是标签是不是自定义View带.的是自定义View如果不是自定义View通过onCreateView创建View如果是自定义View通过createView创建View
所以我们比较一下为啥不是自定义View要通过onCreateView创建而是自定义VIew的用createView创建
先说一个知识点LayoutInflater的实现类是PhoneLayoutInflateronCreateView方法也被重写了: private static final String[] sClassPrefixList {android.widget.,android.webkit.,android.app.}; Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {for (String prefix : sClassPrefixList) {try {View view createView(name, prefix, attrs);if (view ! null) {return view;}} catch (ClassNotFoundException e) {// In this case we want to let the base class take a crack// at it.}}return super.onCreateView(name, attrs);}最后还调用了super:protected View onCreateView(String name, AttributeSet attrs)throws ClassNotFoundException {return createView(name, android.view., attrs);}可以看到onCreateView最后还是调用了createVIew所以onCreateView和createView主要的差别就是这个prefix的前缀不是自定义View需要有前缀想想LinearLayout这个类的全类名是android.widget.LinearLayout这下知道了这些SDK自带的不是自定义View标签都会在这里补全全类名最后看一下createView
(2.2.4).createView方法 static final Class?[] mConstructorSignature new Class[] {Context.class, AttributeSet.class}; //View的两个参数public final View createView(NonNull Context viewContext, NonNull String name,Nullable String prefix, Nullable AttributeSet attrs)throws ClassNotFoundException, InflateException {Objects.requireNonNull(viewContext);Objects.requireNonNull(name);Constructor? extends View constructor sConstructorMap.get(name);if (constructor ! null !verifyClassLoader(constructor)) {constructor null;sConstructorMap.remove(name);}Class? extends View clazz null;try {Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);if (constructor null) {//⭐主要就是通过反射去创建view// Class not found in the cache, see if its real, and try to add itclazz Class.forName(prefix ! null ? (prefix name) : name, false,mContext.getClassLoader()).asSubclass(View.class);if (mFilter ! null clazz ! null) {boolean allowed mFilter.onLoadClass(clazz);if (!allowed) {failNotAllowed(name, prefix, viewContext, attrs);}}constructor clazz.getConstructor(mConstructorSignature);//通过反射获取两个参数的构造方法两个参数分别是context与AttributeSetXML的参数集合constructor.setAccessible(true);sConstructorMap.put(name, constructor); //用map缓存起来} else { //constructor不为null说明map里面有直接用map里面的// If we have a filter, apply it to cached constructorif (mFilter ! null) {// Have we seen this name before?Boolean allowedState mFilterMap.get(name);if (allowedState null) {// New class -- remember whether it is allowedclazz Class.forName(prefix ! null ? (prefix name) : name, false,mContext.getClassLoader()).asSubclass(View.class);boolean allowed clazz ! null mFilter.onLoadClass(clazz);mFilterMap.put(name, allowed);if (!allowed) {failNotAllowed(name, prefix, viewContext, attrs);}} else if (allowedState.equals(Boolean.FALSE)) {failNotAllowed(name, prefix, viewContext, attrs);}}}Object lastContext mConstructorArgs[0];mConstructorArgs[0] viewContext;Object[] args mConstructorArgs;args[1] attrs;try {final View view constructor.newInstance(args);if (view instanceof ViewStub) {// Use the same context when inflating ViewStub later.final ViewStub viewStub (ViewStub) view;viewStub.setLayoutInflater(cloneInContext((Context) args[0]));}return view;} finally {mConstructorArgs[0] lastContext;}} catch (NoSuchMethodException e) {final InflateException ie new InflateException(getParserStateDescription(viewContext, attrs) : Error inflating class (prefix ! null ? (prefix name) : name), e);ie.setStackTrace(EMPTY_STACK_TRACE);throw ie;} catch (ClassCastException e) {// If loaded class is not a View subclassfinal InflateException ie new InflateException(getParserStateDescription(viewContext, attrs) : Class is not a View (prefix ! null ? (prefix name) : name), e);ie.setStackTrace(EMPTY_STACK_TRACE);throw ie;} catch (ClassNotFoundException e) {// If loadClass fails, we should propagate the exception.throw e;} catch (Exception e) {final InflateException ie new InflateException(getParserStateDescription(viewContext, attrs) : Error inflating class (clazz null ? unknown : clazz.getName()), e);ie.setStackTrace(EMPTY_STACK_TRACE);throw ie;} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}}这么一大堆代码其实就是通过反射去创建View注意里面的注释
b.再分析AppCompatActivity的createViewFromTag创建view的流程 private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {return createViewFromTag(parent, name, context, attrs, false);}UnsupportedAppUsageView createViewFromTag(View parent, String name, Context context, AttributeSet attrs,boolean ignoreThemeAttr) {if (name.equals(view)) {name attrs.getAttributeValue(null, class);}// Apply a theme wrapper, if allowed and one is specified.if (!ignoreThemeAttr) {final TypedArray ta context.obtainStyledAttributes(attrs, ATTRS_THEME);final int themeResId ta.getResourceId(0, 0);if (themeResId ! 0) {context new ContextThemeWrapper(context, themeResId);}ta.recycle();}try {View view tryCreateView(parent, name, context, attrs);//⭐重点if (view null) {final Object lastContext mConstructorArgs[0];mConstructorArgs[0] context;try {if (-1 name.indexOf(.)) { view onCreateView(context, parent, name, attrs);} else {view createView(context, name, null, attrs); }} finally {mConstructorArgs[0] lastContext;}}return view;} catch (InflateException e) {throw e;} catch (ClassNotFoundException e) {final InflateException ie new InflateException(getParserStateDescription(context, attrs) : Error inflating class name, e);ie.setStackTrace(EMPTY_STACK_TRACE);throw ie;} catch (Exception e) {final InflateException ie new InflateException(getParserStateDescription(context, attrs) : Error inflating class name, e);ie.setStackTrace(EMPTY_STACK_TRACE);throw ie;}}这个tryCreatView方法如果尝试创建View失败之后才轮到Activity的创建方式我们看一下tryCreateView方法: public final View tryCreateView(Nullable View parent, NonNull String name,NonNull Context context,NonNull AttributeSet attrs) {if (name.equals(TAG_1995)) {// Lets party like its 1995!return new BlinkLayout(context, attrs);}View view;if (mFactory2 ! null) {view mFactory2.onCreateView(parent, name, context, attrs);} else if (mFactory ! null) {view mFactory.onCreateView(name, context, attrs);} else {view null;}if (view null mPrivateFactory ! null) {view mPrivateFactory.onCreateView(parent, name, context, attrs);}return view;}发现有几个变量不认识mFactory2和mFactory还有mPrivateFactory我们的AppCompatActivity就是使用的mFactory2看AppCompatActivity的onCreate方法: Overrideprotected void onCreate(Nullable Bundle savedInstanceState) {final AppCompatDelegate delegate getDelegate();delegate.installViewFactory(); // ⭐delegate.onCreate(savedInstanceState);super.onCreate(savedInstanceState);}我们继续跟踪这个方法: public void installViewFactory() {LayoutInflater layoutInflater LayoutInflater.from(mContext);if (layoutInflater.getFactory() null) {LayoutInflaterCompat.setFactory2(layoutInflater, this);} else {if (!(layoutInflater.getFactory2() instanceof AppCompatDelegateImpl)) {Log.i(TAG, The Activitys LayoutInflater already has a Factory installed so we can not install AppCompats);}}}//Factory2是一个接口public interface Factory2 extends Factory {NullableView onCreateView(Nullable View parent, NonNull String name,NonNull Context context, NonNull AttributeSet attrs);}//LayoutInflater实现了这个接口
class AppCompatDelegateImpl extends AppCompatDelegateimplements MenuBuilder.Callback, LayoutInflater.Factory2 这下看到了AppCompatActivity的oncreate方法中调用installViewFactory方法获取到layoutInflater对象AppCompatDelegateImpl实现了Factory2的接口
LayoutInflaterCompat.setFactory2(layoutInflater, this); public static void setFactory2(NonNull LayoutInflater inflater, NonNull LayoutInflater.Factory2 factory) {inflater.setFactory2(factory); // ⭐if (Build.VERSION.SDK_INT 21) {final LayoutInflater.Factory f inflater.getFactory();if (f instanceof LayoutInflater.Factory2) {// The merged factory is now set to getFactory(), but not getFactory2() (pre-v21).// We will now try and force set the merged factory to mFactory2forceSetFactory2(inflater, (LayoutInflater.Factory2) f);} else {// Else, we will force set the original wrapped Factory2forceSetFactory2(inflater, factory);}}}//LayoutInflater类中的方法public void setFactory2(Factory2 factory) {if (mFactorySet) {throw new IllegalStateException(A factory has already been set on this LayoutInflater);}if (factory null) {throw new NullPointerException(Given factory can not be null);}mFactorySet true;if (mFactory null) {mFactory mFactory2 factory;} else {mFactory mFactory2 new FactoryMerger(factory, factory, mFactory, mFactory2);}}可以看到吧Factory2传进了infalter中所以Inflater的对象中Factory2不是null了。
Factory2不是null了在执行创建createViewFromTag方法的tryCreateView时: public final View tryCreateView(Nullable View parent, NonNull String name,NonNull Context context,NonNull AttributeSet attrs) {if (name.equals(TAG_1995)) {// Lets party like its 1995!return new BlinkLayout(context, attrs);}View view;if (mFactory2 ! null) {view mFactory2.onCreateView(parent, name, context, attrs);} else if (mFactory ! null) {view mFactory.onCreateView(name, context, attrs);} else {view null;}if (view null mPrivateFactory ! null) {view mPrivateFactory.onCreateView(parent, name, context, attrs);}return view;}mFactory2不是null了就会执行 view mFactory2.onCreateView(parent, name, context, attrs);
我们知道这个mFactory2其实就是AppCompatDelegateImpl的实例对象这个设计挺巧妙和上面ensureDecor有点像添加了一些逻辑使对view的创建逻辑转移到了AppCompatDelegateImpl中所以我们下面看AppCompatDelegateImpl的onCreateView方法: Overridepublic final View onCreateView(View parent, String name, Context context, AttributeSet attrs) {return createView(parent, name, context, attrs);}Overridepublic View createView(View parent, final String name, NonNull Context context,NonNull AttributeSet attrs) {if (mAppCompatViewInflater null) {TypedArray a mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);String viewInflaterClassName a.getString(R.styleable.AppCompatTheme_viewInflaterClass);if ((viewInflaterClassName null)|| AppCompatViewInflater.class.getName().equals(viewInflaterClassName)) {// Either default class name or set explicitly to null. In both cases// create the base inflater (no reflection)mAppCompatViewInflater new AppCompatViewInflater();} else {try {Class? viewInflaterClass Class.forName(viewInflaterClassName);mAppCompatViewInflater (AppCompatViewInflater) viewInflaterClass.getDeclaredConstructor().newInstance();} catch (Throwable t) {Log.i(TAG, Failed to instantiate custom view inflater viewInflaterClassName . Falling back to default., t);mAppCompatViewInflater new AppCompatViewInflater();}}}boolean inheritContext false;if (IS_PRE_LOLLIPOP) {inheritContext (attrs instanceof XmlPullParser)// If we have a XmlPullParser, we can detect where we are in the layout? ((XmlPullParser) attrs).getDepth() 1// Otherwise we have to use the old heuristic: shouldInheritContext((ViewParent) parent);}//⭐return mAppCompatViewInflater.createView(parent, name, context, attrs, inheritContext,IS_PRE_LOLLIPOP, /* Only read android:theme pre-L (L handles this anyway) */true, /* Read read app:theme as a fallback at all times for legacy reasons */VectorEnabledTintResources.shouldBeUsed() /* Only tint wrap the context if enabled */);}可以看到创建了一个mAppCompatViewInflater 最后调用了mAppCompatViewInflater的createView方法: final View createView(View parent, final String name, NonNull Context context,NonNull AttributeSet attrs, boolean inheritContext,boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext) {final Context originalContext context;// We can emulate Lollipops android:theme attribute propagating down the view hierarchy// by using the parents contextif (inheritContext parent ! null) {context parent.getContext();}if (readAndroidTheme || readAppTheme) {// We then apply the theme on the context, if specifiedcontext themifyContext(context, attrs, readAndroidTheme, readAppTheme);}if (wrapContext) {context TintContextWrapper.wrap(context);}View view null;// We need to inject our tint aware Views in place of the standard framework versionsswitch (name) {case TextView:view createTextView(context, attrs);verifyNotNull(view, name);break;case ImageView:view createImageView(context, attrs);verifyNotNull(view, name);break;case Button:view createButton(context, attrs);verifyNotNull(view, name);break;case EditText:view createEditText(context, attrs);verifyNotNull(view, name);break;case Spinner:view createSpinner(context, attrs);verifyNotNull(view, name);break;case ImageButton:view createImageButton(context, attrs);verifyNotNull(view, name);break;case CheckBox:view createCheckBox(context, attrs);verifyNotNull(view, name);break;case RadioButton:view createRadioButton(context, attrs);verifyNotNull(view, name);break;case CheckedTextView:view createCheckedTextView(context, attrs);verifyNotNull(view, name);break;case AutoCompleteTextView:view createAutoCompleteTextView(context, attrs);verifyNotNull(view, name);break;case MultiAutoCompleteTextView:view createMultiAutoCompleteTextView(context, attrs);verifyNotNull(view, name);break;case RatingBar:view createRatingBar(context, attrs);verifyNotNull(view, name);break;case SeekBar:view createSeekBar(context, attrs);verifyNotNull(view, name);break;case ToggleButton:view createToggleButton(context, attrs);verifyNotNull(view, name);break;default:// The fallback that allows extending class to take over view inflation// for other tags. Note that we dont check that the result is not-null.// That allows the custom inflater path to fall back on the default one// later in this method.view createView(context, name, attrs);}if (view null originalContext ! context) {// If the original context does not equal our themed context, then we need to manually// inflate it using the name so that android:theme takes effect.view createViewFromTag(context, name, attrs);}if (view ! null) {// If we have created a view, check its android:onClickcheckOnClickListener(view, attrs);}return view;}可以看到有一个switch如果View是case中的一种那就把布局参数传进去重新创建一个AppCompat的View如果不是case中的一种进default这个createView方法直接返回null
protected View createView(Context context, String name, AttributeSet attrs) { return null; } 然后走下面这句:
if (view null originalContext ! context) {// If the original context does not equal our themed context, then we need to manually// inflate it using the name so that android:theme takes effect.view createViewFromTag(context, name, attrs);
}private View createViewFromTag(Context context, String name, AttributeSet attrs) {if (name.equals(view)) {name attrs.getAttributeValue(null, class);}try {mConstructorArgs[0] context;mConstructorArgs[1] attrs;if (-1 name.indexOf(.)) {for (int i 0; i sClassPrefixList.length; i) {final View view createViewByPrefix(context, name, sClassPrefixList[i]);if (view ! null) {return view;}}return null;} else {return createViewByPrefix(context, name, null);}} catch (Exception e) {// We do not want to catch these, lets return null and let the actual LayoutInflater// tryreturn null;} finally {// Dont retain references on context.mConstructorArgs[0] null;mConstructorArgs[1] null;}}private View createViewByPrefix(Context context, String name, String prefix)throws ClassNotFoundException, InflateException {Constructor? extends View constructor sConstructorMap.get(name);try {if (constructor null) {// Class not found in the cache, see if its real, and try to add itClass? extends View clazz Class.forName(prefix ! null ? (prefix name) : name,false,context.getClassLoader()).asSubclass(View.class);constructor clazz.getConstructor(sConstructorSignature);sConstructorMap.put(name, constructor);}constructor.setAccessible(true);return constructor.newInstance(mConstructorArgs);} catch (Exception e) {// We do not want to catch these, lets return null and let the actual LayoutInflater// tryreturn null;}}这个方法和Activity的逻辑一致了
总结一下AppCompatActivity通过LayoutInfater解析创建View时会通过setFactory2拦截原有Activity的逻辑去执行AppCompatDelegateImpl的逻辑在View解析和创建的时候会先检查如果是AppCompat新设计的View就是case里的那一堆就把这个View转换成AppCompat新设计的View如果不是还是按照之前的逻辑来。
三、总结
1.setContentView的总结
setContentView总的来说就是创建DecorViewDecorView是一个FrameLayout然后根据style选择系统自带的布局文件(例如有没有title这里说一下这个布局文件根布局是linearLayout如果有title则是有一个viewStub和两个FrameLayouttitle的和content的如果没有title则是一个viewstub和一个content的FrameLayout)添加到DecorView上最后再把我们自己的的activity_mian这种layout添加到content这个FrameLyout上。
注意Activity和AppCompatActivity有一些区别但总体上的逻辑是不变的。
2.inflate的总结
总结了inflate三个参数的作用总结了解析到include、merge等标签时的注意点总结了如何解析xml文件和如何创建View的重要流程。