在平时开发中,继承一个activity,在起onCreate方法中调用setContentView传入对应的布局资源id就可以得到想要的页面效果,几行代码就实现了,但是细看源码并不是那么回事,那么简单的,不信就去看看吧。
activity中setContentView流程图:
1、activity中setContentView布局资源的加载
进入到activity的源码,找到setContentView方法,这里看的是传入资源id的那个方法
/**
* Set the activity content from a layout resource. The resource will be
* inflated, adding all top-level views to the activity.
*
* @param layoutResID Resource ID to be inflated.
*
* @see #setContentView(android.view.View)
* @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
*/
public void setContentView(@LayoutRes int layoutResID) {
//getWindow()获取到的是一个Window实例,而它的子类是PhoneWindow,也就是调用的是PhoneWindow中的setContentView方法
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
getWindow()获取到的是一个Window实例,而它的子类是PhoneWindow,也就是调用的是PhoneWindow中的setContentView方法
@Override
public void setContentView(int layoutResID) {
//第一次加载的时候mContentParent肯定是null
if (mContentParent == null) {
//在这个方法中会去调用generateDecor方法实例化一个DecorView
//DecorView是继承自FrameLayout的 实际上就是一个布局容器
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
//第一次加载后 重复加载时 会移除掉所有的view
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
//调用LayoutInflater中的inflate去加载布局资源
//layoutResID 就是自定义传入的布局资源id
//mContentParent 就是创建好的系统布局容器 其实就是R.id.content 的容器
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
第一次加载的时候mContentParent肯定是null的,就会去调用installDecor方法,在installDecor中就会通过调用generateDecor方法创建一个DecorView布局容器
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
//在generateDecor方法中会去创建一个DecorView布局容器
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
//将创建好的DecorView布局容器传入到generateLayout方法中去创建系统布局容器
mContentParent = generateLayout(mDecor);
......
} else {
......
}
......
}
protected DecorView generateDecor(int featureId) {
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();
}
//直接通过new 创建一个DecorView布局容器
return new DecorView(context, featureId, this, getAttributes());
}
DecorView布局容器创建好后会传入到generateLayout方法中,去创建系统布局容器
protected ViewGroup generateLayout(DecorView decor) {
......
//通过调用requestFeature去设置一些配置比如无标题 actionBar等,这是在调用setContentView的时候去设置的
//所以应该在setContentView之前去设置无标题 actionBar等配置,才会有效果
if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {
requestFeature(FEATURE_NO_TITLE);
} else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {
// Don't allow an action bar if there is no title.
requestFeature(FEATURE_ACTION_BAR);
}
if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) {
requestFeature(FEATURE_ACTION_BAR_OVERLAY);
}
......
// Inflate the window decor.
//layoutResource 系统布局资源id 下面会根据不同的配置得到不同的布局资源id
int layoutResource;
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleIconsDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_title_icons;
}
// XXX Remove this once action bar supports these features.
removeFeature(FEATURE_ACTION_BAR);
// System.out.println("Title Icons!");
} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {
// Special case for a window with only a progress bar (and title).
// XXX Need to have a no-title version of embedded windows.
layoutResource = R.layout.screen_progress;
// System.out.println("Progress!");
} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
// Special case for a window with a custom title.
// If the window is floating, we need a dialog layout
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogCustomTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_custom_title;
}
// XXX Remove this once action bar supports these features.
removeFeature(FEATURE_ACTION_BAR);
} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
// If no other features and not embedded, only need a title.
// If the window is floating, we need a dialog layout
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
layoutResource = a.getResourceId(
R.styleable.Window_windowActionBarFullscreenDecorLayout,
R.layout.screen_action_bar);
} else {
layoutResource = R.layout.screen_title;
}
// System.out.println("Title!");
} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
layoutResource = R.layout.screen_simple_overlay_action_mode;
} else {
// Embedded, so no decoration is needed.
layoutResource = R.layout.screen_simple;
// System.out.println("Simple!");
}
mDecor.startChanging();
//在onResourcesLoaded中通过LayoutInflater去加载实例化系统布局资源layoutResource
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
//获取创建好的系统布局资源中的id为R.id.content的布局容器
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
......
return contentParent;
}
一开始会根据配置调用requestFeature方法进行一些title、actionbar等设置,所以如果要去掉系统标题 或者actionbar等配置要在setContentView之前进行设置才会有效果,这也是在setContentView之后再去设置没有效果的原因,接着会去根据对应的配置获取系统布局资源,然后在onResourcesLoaded方法中通过LayoutInflater加载系统布局资源
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 {
//将系统资源布局添加到之前创建好的DecorView中
// Put it below the color views.
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mContentRoot = (ViewGroup) root;
initializeElevation();
}
至于LayoutInflater的inflate中是如何加载资源的这里暂时不讲,后面会进行源码查看,这是会将创建好的系统布局资源root添加到之前创建好的DecorView布局容器中,在前面
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
下面会看到这样一行代码
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
这里其实获取到的就是刚才创建好并添加到DecorView容器中的id为R.id.content布局容器,这个时候mContentParent就得到初始化了,就会通过setContentView中的代码去加载自定义传入的布局资源
mLayoutInflater.inflate(layoutResID, mContentParent);
2、LayoutInflater加载资源布局
以上是Window(PhoneWindow)、DecorView、系统布局资源、mContentParent的创建和实例化,接下来才是自定义布局文件的加载,还是通过LayoutInflater中的inflate去加载资源布局的,那么这里说说LayoutInflater的布局资源加载
View.inflate(this,R.layout.activity_main,null);
LayoutInflater.from(this).inflate(R.layout.activity_main,null);
LayoutInflater.from(this).inflate(R.layout.activity_main,null,false);
加载布局资源有上面这三种方式,要注意root(ViewGroup)null和非null的传入是有区别的,第一和第二最终调用的还是第三,所以这里就只看第三种方式,
/**
* Obtains the LayoutInflater from the given context.
*/
public static LayoutInflater from(Context context) {
LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
from是LayoutInflater的一个静态方法,里面通过系统服务获取LayoutInflater实例,Context是一个抽象类,需要找到ContextImpl子类中的getSystemService方法,看看是如何获取LayoutInflater实例的
@Override
public Object getSystemService(String name) {
if (vmIncorrectContextUseEnabled()) {
// We may override this API from outer context.
final boolean isUiContext = isUiContext() || isOuterUiContext();
// Check incorrect Context usage.
if (isUiComponent(name) && !isUiContext) {
final String errorMessage = "Tried to access visual service "
+ SystemServiceRegistry.getSystemServiceClassName(name)
+ " from a non-visual Context:" + getOuterContext();
final String message = "Visual services, such as WindowManager, WallpaperService "
+ "or LayoutInflater should be accessed from Activity or other visual "
+ "Context. Use an Activity or a Context created with "
+ "Context#createWindowContext(int, Bundle), which are adjusted to "
+ "the configuration and visual bounds of an area on screen.";
final Exception exception = new IllegalAccessException(errorMessage);
StrictMode.onIncorrectContextUsed(message, exception);
Log.e(TAG, errorMessage + " " + message, exception);
}
}
//根据对应的服务名称获取注册好的系统服务实例
return SystemServiceRegistry.getSystemService(this, name);
}
通过SystemServiceRegistry中getSystemService中的SYSTEM_SERVICE_FETCHERS获取,SYSTEM_SERVICE_FETCHERS就是一个定义好的静态的ArrayMap,其实就是一个单例,在系统加载创建的时候,会在SystemServiceRegistry的static代码块中注册对应的系统服务,并以key value的方式存储在一个静态的ArrayMap中,后面使用时直接可以通过系统服务的name直接获取到对应的系统服务实例
static{
......
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher<LayoutInflater>() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
......
}
在static代码块中,可以找的LAYOUT_INFLATER_SERVICE系统服务的注册
在PhoneWindow调用的是
mLayoutInflater.inflate(layoutResID, mContentParent);
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
//获取Resources实例
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;
}
//通过Resources得到xml资源解析器
XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
resource--->传入的自定义的布局资源
root--->就是mContentParent,就是系统布局中R.id.content id的布局容器
attachToRoot--->这里mContentParent肯定不为null,所以是true
得到xml布局资源解析器后,利用xml布局资源解析器进行布局资源的解析加载
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;
//将R.id.content 根view赋值给result
View result = root;
try {
advanceToRootNode(parser);
//解析获取到name
final String name = parser.getName();
if (DEBUG) {
System.out.println("**************************");
System.out.println("Creating root view: "
+ name);
System.out.println("**************************");
}
//如果name等于merge 会继续调用rInflate进行解析加载
//这里的name肯定不会是merge 所以看else中的逻辑
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
//根据解析到的name 上下文 属性 创建对应的view并添加到根布局中
//这里的temp是root中的根试图
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.
//获取到根视图后 接着获取所有的子视图
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.
//root不为null attachToRoot传入的时候为true
if (root != null && attachToRoot) {
//将根视图添加到root中,这时已经将所有的子视图添加到temp根视图中了
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;
}
}
接下来根据解析到的name、属性、上下文等创建对应的view,得到的temp 是根view
@UnsupportedAppUsage
View 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 view = tryCreateView(parent, name, context, attrs);
if (view == null) {
final Object lastContext = mConstructorArgs[0];
//赋值构造函数
mConstructorArgs[0] = context;
try {
//name.indexOf('.') 类似于自定义的控件 com.aaa.aaaa.ATextView
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;
}
}
在tryCreateView方法中其实把view的创建交给了Factory中的onCreateView方法;
@UnsupportedAppUsage(trackingBug = 122360734)
@Nullable
public final View tryCreateView(@Nullable View parent, @NonNull String name,
@NonNull Context context,
@NonNull AttributeSet attrs) {
if (name.equals(TAG_1995)) {
// Let's party like it's 1995!
return new BlinkLayout(context, attrs);
}
View view;
//如果mFactory2不为null 会调用mFactory2的onCreateView去创建view
if (mFactory2 != null) {
view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) {
//如果mFactory不为null 会调用mFactory的onCreateView去创建view
view = mFactory.onCreateView(name, context, attrs);
} else {
view = null;
}
if (view == null && mPrivateFactory != null) {
//调用系统的factory的onCreateView去创建view
view = mPrivateFactory.onCreateView(parent, name, context, attrs);
}
return view;
}
在Factory的onCreateView创建view的时候,其实可以通过外部调用LayoutInflateCompat中的setFactory方法设置自己的factory进行view创建的拦截,当然了肯定要在setContentView之前调用,这里是在super.onCreate之前调用
@Override
protected void onCreate(Bundle savedInstanceState) {
LayoutInflater inflater = LayoutInflater.from(this);
LayoutInflaterCompat.setFactory(inflater, new LayoutInflaterFactory() {
@Override
public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
//拦截view的创建
return null;
}
});
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
如果任何factory都没有设置的话,view就会为null,就会调用下面的createView进行view的创建
@Nullable
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);
//通过静态的HashMap获取之前缓存的构造函数
Constructor<? extends View> constructor = sConstructorMap.get(name);
if (constructor != null && !verifyClassLoader(constructor)) {
constructor = null;
//移除name对应的构造函数
sConstructorMap.remove(name);
}
Class<? extends View> clazz = null;
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);
if (constructor == null) {
// Class not found in the cache, see if it's real, and try to add it
//通过类加载器获取view的class
clazz = 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);
constructor.setAccessible(true);
//缓存对应的构造函数
sConstructorMap.put(name, constructor);
} else {
......
}
Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = viewContext;
Object[] args = mConstructorArgs;
args[1] = attrs;
try {
//通过构造函数进行创建View实例并将创建的view对象返回
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 subclass
final 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);
}
}
这样temp根视图就创建好了,后面通过循环遍历创建好所有的子视图,并添加到temp根视图中,最终将temp根视图添加到mContentParent(R.id.content)容器中并返回,这样整个xml布局就加载完成了。
3、AppCompactActivity中setContentView布局资源的加载
AppCompactActivity是material design效果 suppor包中的类,虽然继承自activity,但是会发现同样的控件在继承自activity和继承自AppCompactActivity页面上面效果会有所区别,这是因为在AppCompactActivity页面中在setContentView加载view时做了处理,加载的是support中的material design风格的控件,那就去看看是如何做的处理的吧
@Override
public void setContentView(@LayoutRes int layoutResID) {
getDelegate().setContentView(layoutResID);
}
getDelegate点击去看到的是一个抽象类AppCompatDelegate,应该去找它的子类AppCompatDelegateImpl中的setContentView方法
@Override
public void setContentView(int resId) {
//进行DecorView等的创建和系统布局 mSubDecor的加载
ensureSubDecor();
//mSubDecor就是系统容器
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
//进行布局资源的加载
LayoutInflater.from(mContext).inflate(resId, contentParent);
mAppCompatWindowCallback.getWrapped().onContentChanged();
}
这里看上去和activity中的差不多,DecorView的创建,系统布局容器的创建,R.id.content id容器的获取,然后自定义布局的加载;上面有说到View的创建的时候会交给Factory中的onCreateView去创建,并且可以外部调用LayoutInflateCompat中的setFactory方法设置自定义的Factory,这样能拦截view的创建,这样话,AppCompactActivity中肯定有设置自己的Factory,找到installViewFactory方法,里面就设置了自己的Factory
@Override
public void installViewFactory() {
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
if (layoutInflater.getFactory() == null) {
//AppCompatDelegateImpl实现了LayoutInflater.Factory2接口
LayoutInflaterCompat.setFactory2(layoutInflater, this);
} else {
if (!(layoutInflater.getFactory2() instanceof AppCompatDelegateImpl)) {
Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed"
+ " so we can not install AppCompat's");
}
}
}
既然设置了自己的Factory了,也就会重写onCreateView方法,在createView方法中就会去创建一个AppCompatViewInflater实例,同时调用该实例的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 Lollipop's android:theme attribute propagating down the view hierarchy
// by using the parent's context
if (inheritContext && parent != null) {
context = parent.getContext();
}
if (readAndroidTheme || readAppTheme) {
// We then apply the theme on the context, if specified
context = 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 versions
switch (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 don't 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:onClick
checkOnClickListener(view, attrs);
}
return view;
}
这里就会看到如果name是TextView,就会去创建一个AppCompatTextView,其他如ImageView、Button等控件是一样的,等于就是拦截了view,同时创建一个support包中的控件,这样就会效果不一样了。
4、总结
1、在通过setContentView设置自定义xml布局时,系统会进行Window的创建,DecorView的实例化,R.id.content系统根容器的获取,最终将自定义的xml布局创建并添加进去。
2、AppCompactActivity中的setContentView会设置一个自己的Factory,从而对view进行拦截,并创建对应的support中的view实例