Android 中LayoutInflater(布局加载器)之源码篇
来源:互联网 发布:大庆三打一网络直播 编辑:程序博客网 时间:2024/05/17 07:47
本文出自博客Vander丶CSDN博客,如需转载请标明出处,尊重原创谢谢
博客地址:http://blog.csdn.net/l540675759/article/details/78099609
前言
如果读者没有阅读过该系列博客,建议先阅读下博文说明,这样会对后续的阅读博客思路上会有一个清晰的认识。
Android 中LayoutInflater(布局加载器)系列博文说明
导航
Android 中LayoutInflater(布局加载器)系列博文说明
Android 中LayoutInflater(布局加载器)系列之介绍篇
Android 中LayoutInflater(布局加载器)系列之源码篇
Android 中LayoutInflater(布局加载器)源码篇之createViewFromTag方法
Android 中LayoutInflater(布局加载器)源码篇之rInflate方法
Android 中LayoutInflater(布局加载器)源码篇之parseInclude方法
Android 中LayoutInflater(布局加载器)之实战篇
概述
(1)Activity 的 getSystemService的实现过程
(2)LayoutInflater 如果将布局资源转换为 View 的过程
(3)LayoutInflater的 Factory,Factory2是什么,在解析过程中的作用是什么?
(4)LayoutInflater 的 inflater 方法的各个参数的含义,不同的情况的含义
LayoutInflater的构造方法
protected LayoutInflater(Context context) { mContext = context; }
这种是LayoutInflater常规的构造方法,将Context传入,最后生成的LayoutInflater与对应的Context相绑定。
protected LayoutInflater(LayoutInflater original, Context newContext) { mContext = newContext; mFactory = original.mFactory; mFactory2 = original.mFactory2; mPrivateFactory = original.mPrivateFactory; setFilter(original.mFilter); }
而这种构造方法来说,只是复制原LayoutInflater的内容,然后将Context对象替换,一般来说只会在cloneInContext()方法中使用。
LayoutInflater#form()方法分析
根据介绍篇的内容,LayoutInflater在Android开发中一般是通过
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);LayoutInflater.from(context);
因为第一种方式,已经是LayoutInflater介绍中声明获取的方式之一,那么这里我们看一下LayoutInflater#form的方法。
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; }
从源码上看,LayoutInflater#form()方法内部也是通过getSystemService()方法获得,那么接下来我们看一下context#getSystemService()这个方法:
public abstract Object getSystemService(@ServiceName @NonNull String name);
发现这个只是一个抽象方法,而我们知道Activity也是Context的一个实现。
Activity#getSystemService()这个方法:
@Override public Object getSystemService(@ServiceName @NonNull String name) { if (getBaseContext() == null) { throw new IllegalStateException( "System services not available to Activities before onCreate()"); } //获取WindowManager if (WINDOW_SERVICE.equals(name)) { return mWindowManager; //系统的搜索框SearchManager } else if (SEARCH_SERVICE.equals(name)) { ensureSearchManager(); return mSearchManager; } return super.getSystemService(name); }
从上面看到,在Activity中只处理了两种类型的服务,分别是获取WindowManager、获取SearchManager,那我们接着看其父类的SystemService()方法:
@Override public Object getSystemService(String name) { //找到我们要的东西,注意这是个单例 if (LAYOUT_INFLATER_SERVICE.equals(name)) { if (mInflater == null) { mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this); } return mInflater; } return getBaseContext().getSystemService(name); }
在Activity的父类即ContextThemeWrapper的getSystemService()方法中,我们发现了LayoutInflater的创建过程,从上面的代码我们可以看出:
每个Activity内包含的LayoutInflater是一个单例。
Activity创建LayoutInflater时,是先使用最原始的BaseContext创建,然后在将Activity的父类ContextThemeWrapper的信息通过cloneInContext()方法与其绑定。
然后我们在看下LayoutInflater的cloneInContext的实现:
public abstract LayoutInflater cloneInContext(Context newContext);
先看下,这个方法的介绍:
这个方法通过现有的LayoutInflater创建一个新的LayoutInflater副本,唯一变化的地方是指向不同的上下文对象。
在ContextThemeWrapper通过这个方法创建的新的LayoutInflater还包含了主题的信息。
在ContextThemeWrapper中使用cloneInContext是想将更多的信息,赋予LayoutInflater中,与其相互绑定。
Activity中LayoutInflater创建
对于Activity的LayoutInflater,其实在Activity创建之时就已经创建完成,但是这一块内容属于FrameWork层的内容,博主道行太浅了,只想带大家看下from这个方法的实现过程。
这里如果大家想了解可以参考下这篇文章
LayoutInflater源码解析
而Activity#getLayoutInflater方法:
@NonNull public LayoutInflater getLayoutInflater() { return getWindow().getLayoutInflater(); }
这个Window对象即PhoneWindow,此时创建出来的LayoutInflater即PhoneLayoutInflater。
这里给大家看下PhoneLayoutInflater的cloneInContext()方法:
public LayoutInflater cloneInContext(Context newContext) { return new PhoneLayoutInflater(this, newContext); }
protected PhoneLayoutInflater(LayoutInflater original, Context newContext) { super(original, newContext); }
可以发现PhoneLayoutInflater中cloneInContext()的实现,调用了第二个构造方法。
这里在Android Studio是无法查阅的,有条件的可以下载源码,如果下载源码麻烦,可以在这里查阅。
Android源码查看网址
将R.layout.xxx转换为View的过程分析
其实这个过程即LayoutInflater.inflater()这个过程:
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) { final Resources res = getContext().getResources(); if (DEBUG) { Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" (" + Integer.toHexString(resource) + ")"); } final XmlResourceParser parser = res.getLayout(resource); try { return inflate(parser, root, attachToRoot); } finally { parser.close(); } }
在这个方法中,只是先拿到XmlResourceParser,用于后续节点的解析,我们接着往下看:
这里只看一些关键的信息,具体代码大家自行查看
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) { //》》》》》》》》》》》》》》》》》第一部分》》》》》》》》》》》》》》》》》》》 try { 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) { throw new InflateException("<merge /> can be used only with a valid " + "ViewGroup root and attachToRoot=true"); } rInflate(parser, root, inflaterContext, attrs, false); } else { //》》》》》》》》》》》》》》》》》第三部分》》》》》》》》》》》》》》》》》》》 final View temp = createViewFromTag(root, name, inflaterContext, attrs); ViewGroup.LayoutParams params = null; if (root != null) { params = root.generateLayoutParams(attrs); if (!attachToRoot) { temp.setLayoutParams(params); } } rInflateChildren(parser, temp, attrs, true); if (root != null && attachToRoot) { root.addView(temp, params); } if (root == null || !attachToRoot) { result = temp; } } return result; } }
第一部分:
这里第一部分的内容,主要是一个XML文件的读取过程,这里有两个判断:
(1)遍历XML内容寻找XML标签的开始的标志或者文档结尾的标志才可以跳出循环。
(2)如果该XML没有开始的标识,则抛出异常。
下面给大家介绍下,几种常见的解析标识:
XmlPullParser.START_DOCUMENT 文档开始XmlPullParser.END_DOCUMENT 文档结束XmlPullParser.START_TAG XML标签的开始XmlPullParser.END_TAG XML标签的结束XmlPullParser.TEXT XML标签的内容
第二部分
这部分的一开始先进行了Merge标签的检验,如果发现该节点是Merge,必须满足父View存在,并且与父View绑定的状态。
转换为代码:
root != null && attachToRoot ==true
这里Merge是减少布局层级存在的标签,通常和include标签一起使用,所以其必须存在父View,而且merge标签的内容必须与父View绑定。
这里调用rInflate()方法去解析Merge的标签,而rInflate()方法,在另一篇文章已经单独分析。
Android 中LayoutInflater(布局加载器)源码篇之rInflate方法
第三部分
我们再看一下第三部分的代码,代码中会有一些简要的说明:
//》》》》》》》》》》》》》》》》》第三部分》》》》》》》》》》》》》》》》》》》 //createViewFromTag是一个根据name来创建View的方法 final View temp = createViewFromTag(root, name, inflaterContext, attrs); ViewGroup.LayoutParams params = null; if (root != null) { params = root.generateLayoutParams(attrs); if (!attachToRoot) { temp.setLayoutParams(params); } } //解析子标签 rInflateChildren(parser, temp, attrs, true); if (root != null && attachToRoot) { root.addView(temp, params); } if (root == null || !attachToRoot) { result = temp; } } return result; }
将第三部分内容分拆一下主要分为以下几块内容:
排除标签为include,或者merge之后,就会通过createViewFromTag()方法来创建View
root是inflater()方法的第二个参数,而attachToRoot是第三个参数,最后会根据这两个参数来决定返回的View
在这部分中,createViewFromTag()是根据name(名称),来创建View的一个方法。
由于createViewFromTag()方法的通用性,这块内容博主给单独拿出来,链接如下:
Android 中LayoutInflater(布局加载器)源码篇之createViewFromTag方法
接下来,我们要介绍的是inflater()方法中的参数,到底有什么作用?
ViewGroup.LayoutParams params = null; //当Root存在 if (root != null) { params = root.generateLayoutParams(attrs); if (!attachToRoot) { //设置View在父布局下Params temp.setLayoutParams(params); } } //遍历子节点 rInflateChildren(parser, temp, attrs, true); //如果Root存在并且attachToRoot为true,即与父View绑定 //这里在解析的同时,就会将其添加至父View上 if (root != null && attachToRoot) { root.addView(temp, params); } //如果父Viewwe为null或者没有绑定父View都会将当前解析的View返回,否则返回父View if (root == null || !attachToRoot) { result = temp; } }
仔细分析上述代码,可以得出如下结论:
从这段代码中,得出以下几个结论:
当root为null时,attachToRoot参数无效,而解析出的View作为一个独立的View存在(不存在LayoutParams)。
当root不为null时,attactToRoot为false,那么会给该View设置一个父View的约束(LayoutParams),然后将其返回。
当root不为null时,attactToRoot为true,那么该View会被直接addView进父View,然后会将父View返回。
当root不为null的话,attactToRoot的默认值是true。
public View inflate(XmlPullParser parser, @Nullable ViewGroup root) { return inflate(parser, root, root != null); }
上面的代码中,我们还少分析了一处代码rInflateChildren(),即解析子类:
final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException { rInflate(parser, parent, parent.getContext(), attrs, finishInflate); }
可以看到这里面解析子类调用了rInflate方法, 在来一次rInflate()的分析连接。
Android 中LayoutInflater(布局加载器)源码篇之rInflate方法
如果你之前没看过这段代码,其实你会像博主之前一样,一直在试,而不知道这段代码正确的含义,但是有时候源码会是一个很好的老师,通过它能够得到你想要的。
流程图
- Android 中LayoutInflater(布局加载器)之源码篇
- Android 中LayoutInflater(布局加载器)源码篇之rInflate方法
- Android 中LayoutInflater(布局加载器)源码篇之createViewFromTag方法
- Android 中LayoutInflater(布局加载器)源码篇之parseInclude方法
- Android 中LayoutInflater(布局加载器)之介绍篇
- Android 中LayoutInflater(布局加载器)之实战篇
- Android布局加载之LayoutInflater
- Android LayoutInflater(布局加载器)详解
- android中加载第二个布局之LayoutInflater
- Android 中LayoutInflater(布局加载器)系列博文说明
- Android 中LayoutInflater(布局加载器)系列博文说明
- Android之LayoutInflater加载布局及原理分析
- 布局加载器LayoutInflater讲解
- android一个BaseAdapter的使用(LayoutInflater加载自定义布局)
- Android布局学习之LayoutInflater
- Android布局加载之setContentView源码分析
- Android 布局填充器之LayoutInflater必知细节
- android中布局填充器LayoutInflater的使用
- web——仿WIT
- nodejs中的this指针详解
- 内存对齐 位域
- getResources().getDrawable()过时问题 报错解决
- Java下利用Jackson进行JSON解析和序列化
- Android 中LayoutInflater(布局加载器)之源码篇
- 第二章 开始学习C++
- SQL SERVER2008 调用Webservice
- 简单的菜单下拉效果
- 用 RunTime 为 UITextView 设置占位文本并实时改变文本框占位文本的颜色
- 基于S3C2440的Linux-3.6.6移植 PWM蜂鸣器驱动
- 推荐的、数据检查的方式
- yum 无法使用的解决
- c++primer 第五、六章