LayoutInflater相关-布局XML文件转化为View
【LayoutInflater相关-布局XML文件转化为View】概述:平时开发,我们只需要在Activity的onCreate()方法中调用setContentView()方法就能实现页面的展示,同时也能调用findViewById()获取到对应的控件实例,那么layout的XML文件到底是怎么转化成View呢?1、探索入口:setContentView()
public class MainActivity extends Activity{@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
}
}-->调用Activity的setContentView
public class Activity{public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
...
}
}-->调用了Window类的setContentView
public class Window{
public abstract void setContentView(@LayoutRes int layoutResID);
}-->Window类的唯一实现类是PhoneWindow,最终是调用到了PhoneWindow的setContentView方法
public class PhoneWindow{
private LayoutInflater mLayoutInflater;
public PhoneWindow(Context context) {
super(context);
mLayoutInflater = LayoutInflater.from(context);
}@Override
public void setContentView(int layoutResID) {
...mLayoutInflater.inflate(layoutResID, mContentParent);
...
}}
分析:
- Activity的setContentView()方法最终是调用了PhoneWindow中的setContentView();
- PhoneWindow中的setContentView()是通过LayoutInflater的inflate(int resource, ViewGroup root)方法;
- layoutResID的xml构建成View之后,会添加为mContentParent的子View(我们设置的布局并非页面的根布局,需要了解mContentParent请看mWindow.getDecorView);
@SystemService(Context.LAYOUT_INFLATER_SERVICE)
public abstract class LayoutInflater {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;
}}-->通过调用ContextImpl类的getSystemService()方法
class ContextImpl extends Context {@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}}-->通过系统服务管理类来获取系统服务
/**
* Manages all of the system services that can be returned by {@link Context#getSystemService}.
* Used by {@link ContextImpl}.
*/
final class SystemServiceRegistry {private static final HashMap> SYSTEM_SERVICE_FETCHERS =
new HashMap>();
--> 所有的系统服务都是在静态代码块中注册的,
static {
...
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
...
}//把系统服务的名称和系统服务保存到常量Hashmap中
private static void registerService(String serviceName, Class serviceClass,
ServiceFetcher serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}//通过服务名称获取对应的系统服务
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}}
分析:
- LayoutInflater实例的获取只能通过其内部的静态方法from()获取;
- context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)实际上是通过SystemServiceRegistry的getSystemService()方法获取,调用一个常量的HashMap获取对应的服务;
- 所有的系统服务都是在SystemServiceRegistry的静态代码块中注册保存的,这保证了服务不会被重复注册保存;
- 所以LayoutInflater的实例获取其实是一个单例设计模式。
public abstract class LayoutInflater {
/**
* Inflate a new view hierarchy from the specified xml resource. Throws
* {@link InflateException} if there is an error.
*/
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
//获取Resources资源类
... final XmlResourceParser parser = res.getLayout(resource);
//获取布局文件对应xml解析器
try {
return inflate(parser, root, attachToRoot);
//继续调用下一个方法
} finally {
parser.close();
}
}
}
这里只是获取Resources资源类,然后获取xml文件对应的xml解析器,再调用下一个inflate方法 。
3.1、开始解析布局xml文件
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
...final AttributeSet attrs = Xml.asAttributeSet(parser);
//获取xml的属性集合
...
View result = root;
try {
//判断是否有开始结束标签
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!");
}final String name = parser.getName();
//获取标签名...if (TAG_MERGE.equals(name)) {
if (root == null || !attachToRoot) {
//如果父布局为空或者不需要添加到父布局之中,抛出异常,不直接解析merge为根标签的布局
throw new InflateException(" can be used only with a valid "
+ "ViewGroup root and attachToRoot=true");
}//填充merge的xml布局,往下查看3.2
rInflate(parser, root, inflaterContext, attrs, false);
} else {
//转换xml的根标签为temp,查看3.3
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
//配置根布局temp的布局参数
ViewGroup.LayoutParams params = null;
if (root != null) {
// 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);
}
}//填充跟标签的子view,往下查看3.2
rInflateChildren(parser, temp, attrs, true);
//判断一下,把我们的布局根元素对应的view,添加为root的子view
if (root != null && attachToRoot) {
root.addView(temp, params);
}//把我们的布局根元素对应的view作为结果返回回去
if (root == null || !attachToRoot) {
result = temp;
}
}}
...finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
}return result;
}
}
分析:
- 先从XmlPullParser获取到所有的属性集合,标签转化为view时,需要用到;
- 判断是否有开始结束标签,然后获取第一个标签,判断第一个标签是否为merge标签;
- 如果是merge标签,root不为空且要添加为root的子view时,调用rInflate方法解析其余标签;
- 如果第一个标签不是merge标签,调用createViewFromTag方法把此标签转化成view,然后调用rInflateChildren方法解析其余标签;然后判断是否需要把第一个标签添加为root的子view。
-->填充parent的所有子view,
final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException {
rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
}-->对parser进行循环获取,解析所有的标签
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);
//给parent设置tag
} else if (TAG_INCLUDE.equals(name)) {// 标签
if (parser.getDepth() == 0) {
throw new InflateException(" cannot be the root element");
}
parseInclude(parser, context, parent, attrs);
} else if (TAG_MERGE.equals(name)) {
throw new InflateException(" must be the root element");
} else {
final View view = createViewFromTag(parent, name, context, attrs);
//把标签转换成View,往下看3.3
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
rInflateChildren(parser, view, attrs, true);
//对view包裹的子view进行填充
viewGroup.addView(view, params);
//添加为parent的子view
}
}...
if (finishInflate) {
parent.onFinishInflate();
//调用父View的onFinishInflate方法
}
}/**
* 循环parser当前深度下,所有嵌套的标签,但不进行任何操作;
* 即跳过某个标签和这个标签包裹的所有子标签
*/
final static void consumeChildElements(XmlPullParser parser)
throws XmlPullParserException, IOException {
int type;
final int currentDepth = parser.getDepth();
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
// Empty
}
}
分析:
- rInflateChildren和rInflate是个递归方法,会互相调用;
- rInflate方法中循环所有所有的标签,对标签进行判断;
- 如果为
标签,那么跳过这个标签以及requestFocus嵌套下的所有子标签; - 如果是
标签,那么获取ViewTag主题,给parent设置tag,然后跳过; - 如果为merge标签, 那么merge包裹的标签,会填充merge的子标签,最后也会重新调用rInflate的方法;
- 然后对普通的View标签调用createViewFromTag方法,转换成view;把这个view添加到父view中,然后调用rInflateChildren,递归填充此标签嵌套的子标签(如果存在子标签)。
public abstract class LayoutInflater {
.
private Factory mFactory;
private Factory2 mFactory2;
private Factory2 mPrivateFactory;
//我们可以实现LayoutInflater的Factory接口,再调用setFactory(),可以hook我们的布局View创建
public interface Factory {
public View onCreateView(String name, Context context, AttributeSet attrs);
}-->中转方法,往下调用
private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {
return createViewFromTag(parent, name, context, attrs, false);
}View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
boolean ignoreThemeAttr) {
...// 给context加上主题属性
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;
//如果是继承Activity,而且没有实现Factory接口,mFactory和mFactory2都为空
//如果是继承了AppCompatActivity,mFactory2不为空,onCreateView会在AppCompatDelegateImplV9中被调用
if (mFactory2 != null) {
view = mFactory2.onCreateView(parent, name, context, attrs);
} else if (mFactory != null) {
view = mFactory.onCreateView(name, context, attrs);
} else {
view = null;
}//Activity赋值了mPrivateFactory,但是Activity类中的onCreateView实现为空,所以View依然为空
if (view == null && mPrivateFactory != null) {
view = mPrivateFactory.onCreateView(parent, name, context, attrs);
}if (view == null) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
//判断是否为自定义的View,有.的是自定义view,没有.的是系统的View
if (-1 == name.indexOf('.')) {
view = onCreateView(parent, name, attrs);
//往下看3.4
} else {
view = createView(name, null, attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
}return view;
}
...
}}
分析:
- 首先把context转换成ContextThemeWrapper,可以带上主题属性,传递给View的创建;
- 调用view的创建之前,会先判断mFactory,mFactory2和mPrivateFactory是否为空;
- 通常如果我们继承的是Activity类,mFactory和mFactory2是null,没有被赋值,而mPrivateFactory虽然在Activity中设置了,但是Factory得onCreateView方法没有被复写,所以最后View的创建会调用onCreateView()方法(3.4);
- 如果我们继承的是AppCompatActivity,那么会调用mFactory2接口的onCreateView方法,往下看4;
public abstract class LayoutInflater {//用于传递给view的构造方法,保存和context和attr
final Object[] mConstructorArgs = new Object[2];
private Filter mFilter;
//View实例化过滤器
private HashMap mFilterMap;
//过滤器记录//让我们定义是否允许这个Class对象的创建
public interface Filter {
boolean onLoadClass(Class clazz);
}//用于给我们设置过滤器,可以过滤某些view的创建
public void setFilter(Filter filter) {
mFilter = filter;
if (filter != null) {
mFilterMap = new HashMap();
}
}//用来保存所有View的构造方法
private static final HashMap> sConstructorMap =
new HashMap>();
-->中转方法,往下调用
protected View onCreateView(View parent, String name, AttributeSet attrs)
throws ClassNotFoundException {
return onCreateView(name, attrs);
}-->中转方法,拼接系统View的全类名,再往下调用
protected View onCreateView(String name, AttributeSet attrs)
throws ClassNotFoundException {
return createView(name, "android.view.", attrs);
//系统的View要拼接全类名
}-->传入name,通过反射实例化对应的View
public final View createView(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
Constructor extends View> constructor = sConstructorMap.get(name);
//获取构造方法
//判断constructor的类加载器和LayoutInflater或者Context的类加载器是否为同一个
if (constructor != null && !verifyClassLoader(constructor)) {
constructor = null;
sConstructorMap.remove(name);
}
Class extends View> clazz = null;
try {if (constructor == null) {
//获取对应View的Class对象
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class);
if (mFilter != null && clazz != null) {//Filter接口不设置的话,mFilter为空
boolean allowed = mFilter.onLoadClass(clazz);
if (!allowed) {
failNotAllowed(name, prefix, attrs);
}
}
constructor = clazz.getConstructor(mConstructorSignature);
//获取View的构造方法
constructor.setAccessible(true);
sConstructorMap.put(name, constructor);
//缓存构造方法
} else {
// If we have a filter, apply it to cached constructor
if (mFilter != null) {
// Have we seen this name before?
Boolean allowedState = mFilterMap.get(name);
//获取过滤记录
if (allowedState == null) {
// 获取Class对象
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class);
//如果Class对象不为空,且onLoadClass返回true,过滤这个view的创建
boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
mFilterMap.put(name, allowed);
//记录是否过滤这个view的实例化
if (!allowed) {
failNotAllowed(name, prefix, attrs);
//抛出InflateException异常
}
} else if (allowedState.equals(Boolean.FALSE)) {
failNotAllowed(name, prefix, attrs);
}
}
}//配置view构造方法的参数,
Object lastContext = mConstructorArgs[0];
if (mConstructorArgs[0] == null) {
// Fill in the context if not already within inflation.
mConstructorArgs[0] = mContext;
}
Object[] args = mConstructorArgs;
args[1] = attrs;
final View view = constructor.newInstance(args);
//调用反射,实例化view
if (view instanceof ViewStub) {
// Use the same context when inflating ViewStub later.
final ViewStub viewStub = (ViewStub) view;
viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
//设置viewStub的LayoutInflater,用于后期填充viewStub对应的view
}
mConstructorArgs[0] = lastContext;
return view;
}
...
}
}
分析:
- 实例化View是在createView方法里面;
- 首先从构造方法的缓存里面获取View的构造方法,判断一下类加载器和LayoutInflater.class或者context的类加载器是否相同;
- 如果View的构造方法为空,判断一下是否设置了这个View的实例化过滤,反射获取这个View的构造方法,并记录起来;
- 如果View的构造方法不为空,判断是否设置了View过滤器,记录是否过滤这个view的实例化;
- 最后调用constructor.newInstance()方法实例化View;
-->如果继承的是AppCompatActivity,那么setContentView会调用到AppCompatActivity里面来
public class AppCompatActivity extends FragmentActivity implements AppCompatCallback,
TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider {@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
final AppCompatDelegate delegate = getDelegate();
//delegate是AppCompatDelegateImplV9的子类,最终会调用到AppCompatDelegateImplV9里面的installViewFactory方法
delegate.installViewFactory();
//这里会设置LayoutInflater的mFractory2,下面会讲到...
}-->调用到这里的setContentView
@Override
public void setContentView(@LayoutRes int layoutResID) {
//先获取AppCompatDelegate
getDelegate().setContentView(layoutResID);
}-->往下走getDelegate()
@NonNull
public AppCompatDelegate getDelegate() {
if (mDelegate == null) {
mDelegate = AppCompatDelegate.create(this, this);
}
return mDelegate;
}}-->调用到AppCompatDelegate里面的create
public abstract class AppCompatDelegate {public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
return create(activity, activity.getWindow(), callback);
}//下面的几个类都都是AppCompatDelegateImplV9的子类
private static AppCompatDelegate create(Context context, Window window,
AppCompatCallback callback) {
if (Build.VERSION.SDK_INT >= 24) {
return new AppCompatDelegateImplN(context, window, callback);
} else if (Build.VERSION.SDK_INT >= 23) {
return new AppCompatDelegateImplV23(context, window, callback);
} else if (Build.VERSION.SDK_INT >= 14) {
return new AppCompatDelegateImplV14(context, window, callback);
} else if (Build.VERSION.SDK_INT >= 11) {
return new AppCompatDelegateImplV11(context, window, callback);
} else {
return new AppCompatDelegateImplV9(context, window, callback);
//最后会调用AppCompatDelegateImplV9的setConetentView()
}
}}
当我们设置了继承AppCompatActivity,内部会通过getDelegate()方法,最终能获取到AppCompatDelegateImplV9获取其子类;
@RequiresApi(14)
class AppCompatDelegateImplV9 extends AppCompatDelegateImplBase
implements MenuBuilder.Callback, LayoutInflater.Factory2 {-->这个方法在AppCompatActivity的onCreate()中被调用,设置LayoutInflater的mFactory2
@Override
public void installViewFactory() {
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
if (layoutInflater.getFactory() == null) {
LayoutInflaterCompat.setFactory2(layoutInflater, this);
} else {
if (!(layoutInflater.getFactory2() instanceof AppCompatDelegateImplV9)) {
Log.i(TAG, "The Activity's LayoutInflater already has a Factory installed"
+ " so we can not install AppCompat's");
}
}
}-->LayoutInflater.Factory2的接口方法,LayoutInflater中的mFactory2.onCreateView会回调进来这个方法
@Override
public final View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
//这里会回调我们的Activity中的onCreateView方法,如果没有实现,那么返回null
final View view = callActivityOnCreateView(parent, name, context, attrs);
if (view != null) {
return view;
}//往下调用createView方法实例化View
return createView(parent, name, context, attrs);
}//回调Activity的onCreateView,如果我们有复写,返回onCreateView方法的结果
View callActivityOnCreateView(View parent, String name, Context context, AttributeSet attrs) {
// Let the Activity's LayoutInflater.Factory try and handle it
if (mOriginalWindowCallback instanceof LayoutInflater.Factory) {
final View result = ((LayoutInflater.Factory) mOriginalWindowCallback)
.onCreateView(name, context, attrs);
if (result != null) {
return result;
}
}
return null;
}//中转方法,用来加入一些判断
@Override
public View createView(View parent, final String name, @NonNull Context context,
@NonNull AttributeSet attrs) {
if (mAppCompatViewInflater == null) {
mAppCompatViewInflater = new AppCompatViewInflater();
}boolean inheritContext = false;
if (IS_PRE_LOLLIPOP) {//如果sdk版本低于21,就是5.0以下的手机
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);
}//通过AppCompatViewInflater的createView方法实例化View
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 */
);
}
}
分析:
- installViewFactory()方法在AppCompatActivity的onCreate()方法中被调用,也就是我们自己的Activity的onCreate()方法;
- installViewFactory()方法中调用了LayoutInflaterCompat.setFactory2(layoutInflater, this),这里给LayoutInflater设置了mFractory2;
- 也就是我们在调用setContentView()方法之前,就先设置好了LayoutInflater的mFractory2,那么LayoutInflater的createViewFromTag()方法会回调进来AppCompatDelegateImplV9的onCreateView(LayoutInflater.Fractory2接口方法)方法;
- 经过一连串的调用,如果我们没有实现onCreateView方法,那么最终会调用AppCompatViewInflater的createView,来实例化View;
class AppCompatViewInflater {
public 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;
//配置一下主题,上下文,略过
...View view = null;
//根据标签名,返回android.support.v7.widget中的控件
//如果是继承Activity,返回的是android.view中的控件
switch (name) {
case "TextView":
view = new AppCompatTextView(context, attrs);
break;
case "ImageView":
view = new AppCompatImageView(context, attrs);
break;
case "Button":
view = new AppCompatButton(context, attrs);
break;
case "EditText":
view = new AppCompatEditText(context, attrs);
break;
case "Spinner":
view = new AppCompatSpinner(context, attrs);
break;
case "ImageButton":
view = new AppCompatImageButton(context, attrs);
break;
case "CheckBox":
view = new AppCompatCheckBox(context, attrs);
break;
case "RadioButton":
view = new AppCompatRadioButton(context, attrs);
break;
case "CheckedTextView":
view = new AppCompatCheckedTextView(context, attrs);
break;
case "AutoCompleteTextView":
view = new AppCompatAutoCompleteTextView(context, attrs);
break;
case "MultiAutoCompleteTextView":
view = new AppCompatMultiAutoCompleteTextView(context, attrs);
break;
case "RatingBar":
view = new AppCompatRatingBar(context, attrs);
break;
case "SeekBar":
view = new AppCompatSeekBar(context, attrs);
break;
}//如果view还是空,那么就走反射实例化View
if (view == null && originalContext != context) {
view = createViewFromTag(context, name, attrs);
}if (view != null) {
// If we have created a view, check its android:onClick
checkOnClickListener(view, attrs);
}return view;
}
}
这里其实也很easy,判断一下标签名,直接新建一个android.support.v7.widge包中的控件;如果还是为空,就调用createViewFromTag利用反射实例化;
总结:
1、如果我们的MainActivity设置继承Activity,那么setContentView是会调用PhoneWindow的setContentView(),然后调用LayoutInflater.inflater()方法进行布局填充;
2、LayoutInflater中首先会用XmlPullParser对布局文件进行解析,然后循环遍历xml中的所有标签;
3、对标签的名字进行判断,处理一些特殊标签,然后普通的View标签调用createViewFromTag方法进行转换;
4、createViewFromTag方法会判断一下是否有设置Factory接口并且实现onCreateView方法来实例化View,最后如果view还是为空,则调用createView方法,通过标签的全类名反射进行View的实例化;
5、如果我们的MainActivity设置继承AppCompatActivity,AppCompatActivity的onCreate中会设置LayoutInflater的mFactory2,对View的实例化进行代理;
6、AppCompatDelegateImplV9类中继承了LayoutInflater.Factory2方法,并实现了onCreateView方法,那么View的实例化由AppCompatDelegateImplV9这里进行主导;
7、AppCompatDelegateImplV9会调用AppCompatViewInflater的createView方法实例化View,对标签名进行判断,如果是系统的控件名,返回android.support.v7.widge包中对应的控件,其他控件则用反射进行实例化。
推荐阅读
- 思维导图作业3—工作相关导图
- 织网布局,社群营销走进山东玖零落地企业
- AnyProxy抓取http/https请求
- day16-Linux|day16-Linux 软件管理
- 适合写进作文与疫情相关的句子|适合写进作文与疫情相关的句子|你看 爱和希望蔓延的比病毒更快 每一种爱 都刻进武汉的心脏
- 瀑布流布局
- 2018-12-03-新手教程重构思路
- Linux|Linux 服务器nginx相关命令
- linux环境变量相关操作
- iOS|iOS 系统相关复习