废话不多说,直接上代码:
常用的:
View inflate = View.inflate(context, resource, null);
是不是经常用这种方式来读取xml,生成view.
如果你点开源码去看就会知道,调用的其实是LayoutInflater
public static View inflate(Context context, @LayoutRes int resource, ViewGroup root) {
LayoutInflater factory = LayoutInflater.from(context); //这里需要一个上下文
return factory.inflate(resource, root);//调用的是LayoutInflater的inflate方法.
}
为什么要传入一个上下文呢?
public static layoufrom(Context context) {
//context.getSystemService,这不是activity里获取系统服务类的方法么.
//看看介绍LAYOUT_INFLATER_SERVICE LayoutInflater 取得xml里定义的view
LayoutInflater LayoutInflater =(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) { //如果拿不到这个服务类就会抛出异常.
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
可以看出,LayoutInflater,其实是通过context 的getSystemService获取到的系统服务对象.
接着往下看,查源码,一路找到contextImpl类,这是抽象类context的实现类
系统关键代码:
//这里可以理解为一种单例模式,通过hashmap来保存多个单例,并通过key,来获取相应的单例
//但是这里并不是直接保存单例,而保存了生成单例的对应工厂.
private static final HashMap<String, ServiceFetcher> SYSTEM_SERVICE_MAP = new HashMap<String, ServiceFetcher>();
private static int sNextPerContextServiceCacheIndex = 0;
private static void registerService(String serviceName, ServiceFetcher fetcher) {
if (!(fetcher instanceof StaticServiceFetcher)) {
//将初始化时的顺序赋值给ServiceFetcher中的mContextCacheIndex
fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;
}
//将生成的ServiceFetcher工厂保存起来.
SYSTEM_SERVICE_MAP.put(serviceName, fetcher);
}
//通过静态代码块,创建覆盖了createService()方法的ServiceFetcher工厂来绑定对应的系统服务.
static {
registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());
}});
//很多registerService(String serviceName, ServiceFetcher fetcher);
.......
}
@Override
public Object getSystemService(String name) {
//通过名字拿到对应的工厂,来获取对应的服务对象
ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
return fetcher == null ? null : fetcher.getService(this);
}
static class ServiceFetcher {
int mContextCacheIndex = -1;
/** * Main entrypoint; only override if you don't need caching. */
//上面注释的大概意思是,如果不需要缓存,就复写这个方法.
public Object getService(ContextImpl ctx) {
//ContextImpl中的service缓存集合
ArrayList<Object> cache = ctx.mServiceCache;
Object service;
synchronized (cache) {
if (cache.size() == 0) { //如果没有缓存,则添加sNextPerContextServiceCacheIndex数量的长度.
for (int i = 0; i < sNextPerContextServiceCacheIndex; i++) {
cache.add(null);
} else {
//如果有缓存,则通过该工厂保存的mContextCacheIndex角标从contextImpl的cache中拿到service
service = cache.get(mContextCacheIndex);
if (service != null) { //不为null就直接返回
return service;
}
}
//如果为null,则调用这个工厂的生成方法,来生成对应的系统服务
//就通过静态代码块里面,重写了createService方法的匿名类来获取
service = createService(ctx);
//这里通过代码块里面的生成顺序,来缓存service
cache.set(mContextCacheIndex, service);
//返回我们需要的service
return service;
}
}
}
扯远了..
通过这块内容
registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());
}});
可以看到是通过PolicyManager来获取LayoutInflater实例的
接着看下去
private static final IPolicy sPolicy;
..........
public static LayoutInflater makeNewLayoutInflater(Context context) {
return sPolicy.makeNewLayoutInflater(context);
}
这里IPolicy 是个接口,而sPolicy是通过反射生成的
PolicyManager主要用于创建Window类、LayoutInflater类和WindowManagerPolicy类,它扮演着简单工厂模式中的工厂类角色,而抽象产品角色由IPolicy接口实现,具体产品角色由Policy类实现。
源码就写这些...
------------------------
看看LayoutInflater用法:
//1.传入xml的解析,父容器,用的较少
public View inflate(XmlPullParser parser, ViewGroup root) {
return inflate(parser, root, root != null);
}
//2.传入资源Id.与父容器,走到方法3
public View inflate(int resource, ViewGroup root) {
return inflate(resource, root, root != null);
}
//3.传入资源Id.与父容器,是否加载到父容器,用的较多
public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
if (DEBUG) System.out.println("INFLATING from resource: " + resource);
XmlResourceParser parser = getContext().getResources().getLayout(resource);
try {
//传入xml的解析,父容器,是否直接添加到父t容器
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}}
//4.发现1,2,3,最终都是走到这里
/**
* parser xml资源
* root 父容器
* attachToRoot 是否直接加载到父容器中
**/
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
synchronized (mConstructorArgs) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
final AttributeSet attrs = Xml.asAttributeSet(parser);
Context lastContext = (Context)mConstructorArgs[0];
mConstructorArgs[0] = mContext;
View result = root;
try {
//解析xml,代码较多,部分代码省略
//1.如果xml的根节点为merge,则root不能为null,否则抛出异常
//2.如果根节点为blink,则生成BlinkLayout作为根布局
//3.如果1,2不成立,则根据根节点名称,生成对应的根布局
if (TAG_1995.equals(name)) { //TAG_1995 ="blink";
temp = new BlinkLayout(mContext, attrs);
} else {
temp = createViewFromTag(root, name, attrs);
}
//4.如果root不为null,则拿到root的layoutparams,来设置根布局的layoutparams.
if (root != null) {
params = root.generateLayoutParams(attrs);
if (!attachToRoot) {
temp.setLayoutParams(params);
}
}
//5.通过解析出的根布局,然后解析其包含的所有子控件.
rInflate(parser, temp, attrs, true);
//6.如果传入的root不为null并且attachToRoot为true,则将解析出来的view添加到root容器中
if (root != null && attachToRoot) {
root.addView(temp, params);
}
//7.如果传入的root为null或者attachToRoot为false,则不添加
if (root == null || !attachToRoot) {
result = temp;
}
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
return result; //返回解析出来的
}}
总结:
举例:
//例1.生成view,但不指定父容器,如果父容器为null,那么设置ture还是false结果都一样
LayoutInflater.from(context).inflate(id,null);
//例2.结果和1一样,
LayoutInflater.from(context).inflate(id,null,false);
//例3.结果和1一样
LayoutInflater.from(context).inflate(id,null,true);
//例4.生成view,指定父容器,并添加到其中,获取parent的Layoutparmas设置view的Layoutparmas.
LayoutInflater.from(context).inflate(id,parent,true);
//例5.生成view,指定父容器,不添加到其中,获取parent的Layoutparmas设置view的Layoutparmas.
LayoutInflater.from(context).inflate(id,parent,false);
用例1,2,3生成的view是没有Layoutparmas的.所以必须手动设置,不然xml里面设置的属性会失效
用例4,5设生成的view,不管设置的true还是false,生成的view都会从parent中得到Layoutparmas
,区别在于,是否添加到parent中
如果使用例4,则不要再去parent.addview(),否则会抛出异常
if (child.getParent() != null) {
throw new IllegalStateException(
"The specified child already has a parent."
"You must call removeView() on the child's parent first.");
}
使用例5,则可以使用parent.addview().