经常使用如下方式获取View
LayoutInflater.from(this).inflate(R.layout.test_layout_inflate, null)
这里面做了什么操作呢?先看 LayoutInflater.from 里面做了什么
public static LayoutInflater from(Context context) {
//通过 getSystemService 获取实例
LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
直接通过 context 来获取对象通过 context 的类图可以知道当前的 context 实现类为 ContextImpl 直接进入来看
// ContextImpl 中
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
// SystemServiceRegistry 中
public static Object getSystemService(ContextImpl ctx, String name) {
//通过一个静态的HashMap 中获取
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
//定义一个静态的 HashMap
private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
new HashMap<String, ServiceFetcher<?>>();
static{
//静态代码快中注册这个服务
registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class,
new CachedServiceFetcher<LayoutInflater>() {
@Override
public LayoutInflater createService(ContextImpl ctx) {
return new PhoneLayoutInflater(ctx.getOuterContext());
}});
}
private static <T> void registerService(String serviceName, Class<T> serviceClass,
ServiceFetcher<T> serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
//将该服务保存到静态 HashMap 中
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}
通过上面可以知道 LayoutInflater.from 就是从一个静态的HashMap 中获取,而数据是在static 中保存所以这是一个单例的设计模式,继续来看 inflate 做了什么
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
//直接调用自身的 inflate 将attachToRoot 参数传过去
return inflate(resource, root, root != null);
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
//通过 Resources 获取 xml
final XmlResourceParser parser = res.getLayout(resource);
try {
//主要是这个,用来解析
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
//将 传过来的 root 赋值给 result
View result = root;
try {
//获取当前标签的名字
final String name = parser.getName();
//判断是否为 merge 标签
if (TAG_MERGE.equals(name)) {
} else {
// 创建 view 对象 关键代码1
final View temp = createViewFromTag(root, name, inflaterContext, attrs);
ViewGroup.LayoutParams params = null;
//判断当前 root 是否为null
if (root != null) {
// 创建params
// Create layout params that match root, if supplied
params = root.generateLayoutParams(attrs);
//根据 attachToRoot 来判断是否 给创建出来的view 设置params
if (!attachToRoot) {
// Set the layout params for temp if we are not
// attaching. (If we are, we use addView, below)
temp.setLayoutParams(params);
}
}
// inflate 子view 关键代码2
rInflateChildren(parser, temp, attrs, true);
//根据 root 和 attachToRoot 判断是否往root 中add创建出来的view
if (root != null && attachToRoot) {
root.addView(temp, params);
}
//根据 root 和 attachToRoot 判断 返回的是创建出来的view 还是 传过来的root
if (root == null || !attachToRoot) {
result = temp;
}
}
return result;
}
}
根据上面知道,最终会调用三个参数的 inflate 方法,然后根据 root 和 attachToRoot 的值来判断返回的是传进来的root 对象还是创建的view 对象和判断创建的view 对象是否有params。view 是否有 params 是用来测量view的宽高。我们来看关键代码 1
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) {
try {
View view;
//判断 Factory 是否为null 不为null 就将view 的创建交给Factory去做
//通过这个我们可以自己拦截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);
}
//如果没有 Factory 则自己创建
if (view == null) {
final Object lastContext = mConstructorArgs[0];
mConstructorArgs[0] = context;
try {
if (-1 == name.indexOf('.')) {
//创建view 就是通过名字使用反射来创建,增加缓存
view = onCreateView(parent, name, attrs);
} else {
view = createView(name, null, attrs);
}
} finally {
mConstructorArgs[0] = lastContext;
}
}
return view;
}
关键代码 1 就是创建除我们自己的view,如果设置有 Factory 则通过 Factory 来创建;来看关键代码2
final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException {
rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
}
void rInflate(XmlPullParser parser, View parent, Context context,
AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
//不断循环 xml 判断是否有下一个节点
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
final String name = parser.getName();
//判断当前标签是否为 requestFocus、tag、include、merge
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 {
//都不是,则创建view对象
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);
}
}
}
所以关键代码2 主要的工作就是不断循环xml 一直到最低部,最后通过 createViewFromTag 来创建view 对象。到这里 LayoutInflater.from(this).inflate(R.layout.test_layout_inflate, null) 已经解析完成,那么下面这几中创建方式有什么区别呢?
View.inflate(this, R.layout.test_layout_inflate, null)
LayoutInflater.from(this).inflate(R.layout.test_layout_inflate, null)
LayoutInflater.from(this).inflate(R.layout.test_layout_inflate, constraint_root, false)
后面两个主要是参数不同,第一个来看源码
public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {
LayoutInflater factory = LayoutInflater.from(context);
//直接调用 inflate
return factory.inflate(resource, root);
}
本质上还是调用 inflate 方法只是传的参数不一样,所以能得到一下结论
//返回 view 本身 没有设置 view 自身的LayoutParams view 宽高是默认的
View.inflate(this, R.layout.test_layout_inflate, null)
LayoutInflater.from(this).inflate(R.layout.test_layout_inflate, null)
LayoutInflater.from(this).inflate(R.layout.test_layout_inflate, null, false)
LayoutInflater.from(this).inflate(R.layout.test_layout_inflate, null, true)
//返回 root 需要加载的布局已经在内部 add
View.inflate(this, R.layout.test_layout_inflate, constraint_root)
LayoutInflater.from(this).inflate(R.layout.test_layout_inflate, constraint_root)
LayoutInflater.from(this).inflate(R.layout.test_layout_inflate, constraint_root, true)
//返回 view 本身 设置了view 自身 的LayoutParams view 的宽高都测量好了
LayoutInflater.from(this).inflate(R.layout.test_layout_inflate, constraint_root, false)
网友评论