android LayoutInflater源码解析
来源:互联网 发布:苹果代充软件 编辑:程序博客网 时间:2024/05/16 07:39
LayoutInflater
Instantiates a layout XML file into its corresponding {@link android.view.View} objects. It is never used directly. Instead, use {@link android.app.Activity#getLayoutInflater()} or {@link Context#getSystemService} to retrieve a standard LayoutInflater instance
源码版本android 23
LayoutInflater用于实例化一个XML 布局文件成相应的View。
它不能直接获取通过 new 的方式获得,可以通过以下三种获取方式:
- LayoutInflater LayoutInflater =(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- LayoutInflater layoutInflater = LayoutInflater.from(context);
- LayoutInflater layoutInflater = Activity.getLayoutInflater();
第二种方式是第一种的缩写,第三种获取的是Activity私有成员属性mWindow的LayoutInflater(在我们setContentView前,Activity就需要去加载DecorView)。
/** * 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; }
inflate overload
在拿到实例后,为了实现其加载XML文件成View的职能,我们需要调用 public View inflate()
方法,该方法有多个重载。如下:
1. public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)2. public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)3. public View inflate(@LayoutRes int resource, @Nullable ViewGroup root)4. public View inflate(XmlPullParser parser, @Nullable ViewGroup root)
这些重载方法其实最终调用的都是 第一个。
我们来看它是怎么汇流到第一个方法。
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) { final XmlResourceParser parser = getContext().getResources().getLayout(resource); return inflate(parser, root, attachToRoot);}
传入的R.layout.main_activity XML布局文件,会被转换成XmlResourceParser 接口类型,然后调用第一个方法。
那不传入attachToRoot参数呢?
public View inflate(XmlPullParser parser, @Nullable ViewGroup root) { return inflate(parser, root, root != null); }
@Nullable 注解表示,这个参数可以传空。
- 当root为空时,调用第一个方法,并传入inflate(parser, root, false)
- root不为空时,调用第一个方法,并传入inflate(parser, root, true)
inflate @param
终于到达 public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) 方法了。
我们来看它的参数设置
- @param resource Xml布局文件的ID (如:R.layout.main_activity)
- @param root 生成View的父视图,如果不为空,就套LayoutParams到View上。
- @param attachToRoot 是否将生成的View添加到root上(也就是root.addView(view),当attachToRoot为true时,将生成的View添加到root上。并且返回的是root
- @return
- 当attachToRoot为true,返回已经添加View的root。
- 当attachToRoot为false,返回View。
其实还是很清晰的,我们捋一捋,resource参数就是布局xml文件,root决定LayoutParams,attachToRoot决定返回值。
研究完它的使用,我们继续看它到底是怎么样将一个xml文件加载成java里的View对象。
inflate function
final Context inflaterContext = mContext; View result = root; int type; final String name = parser.getName(); //如果开头为<merge>标签则需要传入正确的root 和attachToRoot 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 { // 创建根节点的View(也就是经常我们创建的LinearLayout,RelativeLayout) final View temp = createViewFromTag(root, name, inflaterContext, attrs); ViewGroup.LayoutParams params = null; if (root != null) { // 生成一个默认匹配root类型的LayoutParams(如:LinearLayout.LayoutParams),且宽高为matchParent 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); } } // 加载tempView下的所有子View,并add进temp rInflateChildren(parser, temp, attrs, true); //根据上文提到的,两参数不同,返回不同的result if (root != null && attachToRoot) { root.addView(temp, params); } if (root == null || !attachToRoot) { result = temp; } } return result;}
代码删减了许多,但核心的就是展示了这几个参数对inflate方法的影响。
看了代码,我们发现createViewFromTag方法是根据xml里定义标签生成特定的View,而rInflateChildren是个递归的过程,就是一层层地向下调用createViewFromTag创建子View并且addView到自己的父节点,而createViewFromTag在筛选特定的工程方法(LayoutInflater的子类可以自定义生成View的方式)后,它会调用createView方法,createView才是生成没有子类的View,那它是如何工作的呢。
createView
/*** 通过LayoutInflater的ClassLoader去实例化给出的name相对于的View * * * @param **name** View的name * @param **prefix** View的前缀,自定义的View为包名,系统的为"android.view." * @param **attrs** View的XML attributes * * @return **View** view, 或者null. */public final View createView(String name, String prefix, AttributeSet attrs) throws ClassNotFoundException, InflateException { //作为一个View缓冲Map Constructor<? extends View> constructor = sConstructorMap.get(name); Class<? extends View> clazz = null; try { //日志追踪 Trace.traceBegin(Trace.TRACE_TAG_VIEW, name); if (constructor == null) { // 缓冲为空,加入前缀去加载它 clazz = mContext.getClassLoader().loadClass( prefix != null ? (prefix + name) : name).asSubclass(View.class); if (mFilter != null && clazz != null) { //View是否允许加载 boolean allowed = mFilter.onLoadClass(clazz); if (!allowed) { failNotAllowed(name, prefix, attrs); } } constructor = clazz.getConstructor(mConstructorSignature); constructor.setAccessible(true); sConstructorMap.put(name, constructor); } else { // 缓冲不为空,判断该类能否被加载 if (mFilter != null) { Boolean allowedState = mFilterMap.get(name); if (allowedState == null) { clazz = mContext.getClassLoader().loadClass( prefix != null ? (prefix + name) : name).asSubclass(View.class); boolean allowed = clazz != null && mFilter.onLoadClass(clazz); mFilterMap.put(name, allowed); if (!allowed) { failNotAllowed(name, prefix, attrs); } } else if (allowedState.equals(Boolean.FALSE)) { failNotAllowed(name, prefix, attrs); } } } Object[] args = mConstructorArgs; args[1] = attrs; //constructor.newInstance()去返回一个新的View实例 final View view = constructor.newInstance(args); if (view instanceof ViewStub) { // 如果该View是ViewStub或者其子类,等它需要加载时使用这个相同的上下文 final ViewStub viewStub = (ViewStub) view; viewStub.setLayoutInflater(cloneInContext((Context) args[0])); } return view; } catch (NoSuchMethodException e) { InflateException ie = new InflateException(attrs.getPositionDescription() + ": Error inflating class " + (prefix != null ? (prefix + name) : name)); ie.initCause(e); throw ie; } catch (ClassCastException e) { // If loaded class is not a View subclass InflateException ie = new InflateException(attrs.getPositionDescription() + ": Class is not a View " + (prefix != null ? (prefix + name) : name)); ie.initCause(e); throw ie; } catch (ClassNotFoundException e) { // If loadClass fails, we should propagate the exception. throw e; } catch (Exception e) { InflateException ie = new InflateException(attrs.getPositionDescription() + ": Error inflating class " + (clazz == null ? "<unknown>" : clazz.getName())); ie.initCause(e); throw ie; } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); }}
在createView调用完成后,递归就会往回走,又回到刚才的rInflateChildren去createViewFromTag下一个子View,当所有的View都加载完成了,就执行上文提到的inflate方法里的root.addView(temp, params),最后return result。
OK,整个inflate过程就结束了。
- Android LayoutInflater源码解析
- android LayoutInflater源码解析
- Android LayoutInflater 源码解析
- Android LayoutInflater 源码解析
- Android LayoutInflater 源码解析
- android LayoutInflater用法和源码解析
- Android源码解析之LayoutInflater原理
- LayoutInflater源码解析
- LayoutInflater源码解析
- LayoutInflater源码解析
- Android应用setContentView与LayoutInflater加载解析机制源码分析
- Android应用setContentView与LayoutInflater加载解析机制源码分析
- Android应用setContentView与LayoutInflater加载解析机制源码分析
- Android应用setContentView与LayoutInflater加载解析机制源码分析
- Android应用setContentView与LayoutInflater加载解析机制源码分析
- Android应用setContentView与LayoutInflater加载解析机制源码分析
- Android应用setContentView与LayoutInflater加载解析机制源码分析
- Android应用setContentView与LayoutInflater加载解析机制源码分析
- APP端后台接收消息处理并推送
- 1、Oracle PL/SQL中的字符串及函数介绍
- C# 各类计时器
- 课后练习
- 前端HTML5几种存储方式的总结
- android LayoutInflater源码解析
- Android Studio VCS工具不显示Refresh File status选项
- ORACLE诊断事件
- tomcat学习(2)
- Mysql联合查询UNION和UNION ALL的使用介绍
- 有关c语言编译的小问题
- AndroidStudio与Eclipse快捷键
- Android doc |Getting Started|部分 --Building a Flexible UI(部分翻译)
- Oracle 10g中dbms_system包的使用方法