参考:
https://blog.csdn.net/samlss/article/details/81332395
https://blog.csdn.net/csdn_aiyang/article/details/86626748
文章目录
- 1 AppCompatActivity.java
- 2 AppCompatDelegateImpl.java
- 3 LayoutInflater.java
在了解动态换肤之前,有必要对setContentView源码进行分析。
这里分析AppCompatActivity的setContentView方法,Activity的setContentView方法会不同。
1 AppCompatActivity.java
/android.support.v7.app.AppCompatActivity.java
public void setContentView(@LayoutRes int layoutResID) {
this.getDelegate().setContentView(layoutResID);
}
2 AppCompatDelegateImpl.java
/android.support.v7.app.AppCompatDelegateImpl.java
public void setContentView(int resId) {
this.ensureSubDecor();
ViewGroup contentParent = (ViewGroup)this.mSubDecor.findViewById(16908290);
contentParent.removeAllViews();
LayoutInflater.from(this.mContext).inflate(resId, contentParent);//1
this.mOriginalWindowCallback.onContentChanged();
}
注释1 使用LayoutInflater布局加载器将布局文件加载到contentParent当中。
3 LayoutInflater.java
/android.view.LayoutInflater.java
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();//1
final XmlResourceParser parser = res.getLayout(resource);//2
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
注释1获取到一个Resources对象。
注释2获取到xml的解析器XmlResourceParser,解析布局文件。
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
...
final String name = parser.getName();//1
if (TAG_MERGE.equals(name)) {
...
} else {
final View temp = createViewFromTag(root, name, inflaterContext, attrs);//2
...
}
}
注释1通过解析器获取一个节点名,比如我们布局文件的的一个Image节点。
注释2创建这个节点对应的View。
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
if (name.equals("view")) {
name = attrs.getAttributeValue(null, "class");
}
...
try {
View view;
if (mFactory2 != null) {
view = mFactory2.onCreateView(parent, name, context, attrs);//1
} 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);
}
if (view == null) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf('.')) {//2
view = onCreateView(parent, name, attrs);
} else {
view = createView(name, null, attrs);//3
}
} finally {
mConstructorArgs[0] = lastContext;
}
}
return view;
}
...
}
注释1 通过工厂Factory2 创建View。
注释2 表示自定义View的创建(自定义View包含包名,包名中包含".")。
注释3 创建系统自带的View
createView方法:
public final View createView(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
Constructor<? extends View> constructor = sConstructorMap.get(name);
...
Class<? extends View> clazz = null;
try {
if (constructor == null) {
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class);//1
...
constructor = clazz.getConstructor(mConstructorSignature);//2
constructor.setAccessible(true);
sConstructorMap.put(name, constructor);//缓存,保存到一个map中
}else{
...
}
...
final View view = constructor.newInstance(args);//3
...
return view;
}
...
}
注释1 获取到View的Class。
注释2 通过反射获取到View的构造方法。
注释3 通过构造函数创建View。
还有一点需要注意的是setFactory2只能设置一次,否则会抛出异常"A factory has already been set on this LayoutInflater"。所以在自定义Factory2的时候,需要重新设置mFactorySet的值。因为mFactorySet是私有的,可以通过反射,然后修改该参数值。
private boolean mFactorySet;
...
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);
}
}