LayoutInflate源码分析之如何解析视图树
来源:互联网 发布:知乎 国开博裕和博裕 编辑:程序博客网 时间:2024/06/05 20:18
前言:
在前面介绍完LayooutInflater的inflate方法后,这次我们着重来分析下inflate是如何去加载布局的。
我们之前讲过系统获取LayoutInflater的时候,会通过ServiceFetcher的createService方法进行注入到容器
ReceiverRestrictedContext类地址:
https://android.googlesource.com/platform/frameworks/base/+/0e2d281/core/java/android/app/ContextImpl.java
继续跟踪PolicyManager类:
跟踪Policy代码:
继续查看PhoneLayoutInflater源码:
具体如何我们以setContentView为例:
activity中的setContentView中实际调用的是Window中的setContentView,而window是个抽象类,它的实现类是PhoneWindow
在PhoneWindow中有一个对应的setContentView方法,方法中调用了LayoutInflater的inflate方法,我们都知道所有的inflate
方法最终都会调用含有xml解析器的那个inflate方法:
上面代码1处是根据tag的名字有没有包含“.”,没有的话就返回-1,直接调用onCreateView进行内置view的解析,否则调用
createView解析自定义控件,那这两个方法又有什么区别呢?还记得之前讲过PhoneLayoutInflate中的OncreateView方法,
它是将“android.widget”前缀传递给createView方法,然后再通过View的完整路径去解析。
createView方法中很简单,就是通过完整的类名反射去构造view对象,最终将view对象返回。
现在单个view解释通了,那整个视图树又是怎么解析的呢?
总结:基本关于LayoutInflater的解析大概就到这里了,以上代码如果有什么不清楚的地方,欢迎指出。
在前面介绍完LayooutInflater的inflate方法后,这次我们着重来分析下inflate是如何去加载布局的。
我们之前讲过系统获取LayoutInflater的时候,会通过ServiceFetcher的createService方法进行注入到容器
ReceiverRestrictedContext类地址:
https://android.googlesource.com/platform/frameworks/base/+/0e2d281/core/java/android/app/ContextImpl.java
注入代码如下:
registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext()); }});这里调用了PolicyManager的makeNewLayoutInflater方法:
继续跟踪PolicyManager类:
public final class PolicyManager { private static final String POLICY_IMPL_CLASS_NAME = "com.android.internal.policy.impl.Policy"; private static final IPolicy sPolicy; static { //通过反射去生成Policy对象 try { Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME); sPolicy = (IPolicy)policyClass.newInstance(); }.... } // Cannot instantiate this class private PolicyManager() {} // The static methods to spawn new policy-specific objects public static Window makeNewWindow(Context context) { return sPolicy.makeNewWindow(context); }//创建LayoutInflater public static LayoutInflater makeNewLayoutInflater(Context context) { return sPolicy.makeNewLayoutInflater(context); } public static WindowManagerPolicy makeNewWindowManager() { return sPolicy.makeNewWindowManager(); } public static FallbackEventHandler makeNewFallbackEventHandler(Context context) { return sPolicy.makeNewFallbackEventHandler(context); }}在这里它去通过反射构造了里面的policy对象,policy是Ipolicy的具体实现类
跟踪Policy代码:
public class Policy implements IPolicy{public LayoutInflater makeNewLayoutInflater(Context context){return new PhoneLayoutInflater(context);}}原来LayoutInflater的实现类是PhoneLayoutInflater。
继续查看PhoneLayoutInflater源码:
public class PhoneLayoutInflater extends LayoutInflater { private static final String[] sClassPrefixList = {//内置view类型的前缀,如textview的完整路径为android.widget.textview "android.widget.", "android.webkit." }; public PhoneLayoutInflater(Context context) { super(context); } protected PhoneLayoutInflater(LayoutInflater original, Context newContext) { super(original, newContext); } //在View名字的前面添加前缀来构造View的完整路径,以便于根据类的完整路径来构造View对象 @Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException { for (String prefix : sClassPrefixList) { try { View view = createView(name, prefix, attrs); if (view != null) { return view; } } catch (ClassNotFoundException e) { // In this case we want to let the base class take a crack // at it. } } return super.onCreateView(name, attrs); } public LayoutInflater cloneInContext(Context newContext) { return new PhoneLayoutInflater(this, newContext); }}
具体如何我们以setContentView为例:
activity中的setContentView中实际调用的是Window中的setContentView,而window是个抽象类,它的实现类是PhoneWindow
在PhoneWindow中有一个对应的setContentView方法,方法中调用了LayoutInflater的inflate方法,我们都知道所有的inflate
方法最终都会调用含有xml解析器的那个inflate方法:
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) { synchronized (mConstructorArgs) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate"); final Context inflaterContext = mContext; final AttributeSet attrs = Xml.asAttributeSet(parser); Context lastContext = (Context) mConstructorArgs[0]; mConstructorArgs[0] = inflaterContext; View result = root;//存储根视图 try { // Look for the root node. 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 (DEBUG) { System.out.println("**************************"); System.out.println("Creating root view: " + name); System.out.println("**************************"); }//如果根视图采用的是merge标签 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"); }//解析xml布局的merge根标签(第一个标签),并调用这个方法将其标签下的所有子view直接添加到根标签中 rInflate(parser, root, inflaterContext, attrs, false); } else { // Temp is the root view that was found in the xml//如果是普通布局,调用createViewTag直接解析出根视图,比如LinearLayout,这个方法是对单个元素进行解析 final View temp = createViewFromTag(root, name, inflaterContext, attrs); ViewGroup.LayoutParams params = null;//这些参数就不解释了,以前博文中解释过 if (root != null) { if (DEBUG) { System.out.println("Creating params from root: " + root); } // Create layout params that match root, if supplied params = root.generateLayoutParams(attrs); if (!attachToRoot) { temp.setLayoutParams(params); } } if (DEBUG) { System.out.println("-----> start inflating children"); } // Inflate all children under temp against its context.//解析这个根布局下面的所有子view,默认还是递归调用Inflate方法,将根布局下的所有的子view添加到temp中,返回temp rInflateChildren(parser, temp, attrs, true); if (DEBUG) { System.out.println("-----> done inflating children"); }//如果root不为空,attachToRoot为true,将temp添加到父视图中(前面博文已经讲过) if (root != null && attachToRoot) { root.addView(temp, params); } // Decide whether to return the root that was passed in or the // top view found in xml. if (root == null || !attachToRoot) { result = temp; } } }...catch代码 Trace.traceEnd(Trace.TRACE_TAG_VIEW); return result; } }接下来我们继续从解析单个view的方法去看:
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, boolean ignoreThemeAttr) { if (name.equals("view")) { name = attrs.getAttributeValue(null, "class"); }...............省略部分代码 try { View view; if (view == null) { final Object lastContext = mConstructorArgs[0]; mConstructorArgs[0] = context; try {//1.内置View控件解析 if (-1 == name.indexOf('.')) { view = onCreateView(parent, name, attrs); } else {//2.自定义控件的解析 view = createView(name, null, attrs); } } finally { mConstructorArgs[0] = lastContext; } } return view; }.....catch代码 }
上面代码1处是根据tag的名字有没有包含“.”,没有的话就返回-1,直接调用onCreateView进行内置view的解析,否则调用
createView解析自定义控件,那这两个方法又有什么区别呢?还记得之前讲过PhoneLayoutInflate中的OncreateView方法,
它是将“android.widget”前缀传递给createView方法,然后再通过View的完整路径去解析。
createView方法中很简单,就是通过完整的类名反射去构造view对象,最终将view对象返回。
现在单个view解释通了,那整个视图树又是怎么解析的呢?
void rInflate(XmlPullParser parser, View parent, Context context, AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {//得到视图树的深度 final int depth = parser.getDepth(); int type; 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)) { parseRequestFocus(parser, parent); } 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 {//根据元素名解析 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);//每解析到一个view就会递归调用上面的方法,直到这条路径下的最后一个元素,然后再回溯过来将//每个view元素添加到parent中,解析完成后整个视图树也就构造完成。 viewGroup.addView(view, params); } } if (finishInflate) { parent.onFinishInflate(); } }
总结:基本关于LayoutInflater的解析大概就到这里了,以上代码如果有什么不清楚的地方,欢迎指出。
参考书籍:
《Android源码设计模式解析与实战》
0 0
- LayoutInflate源码分析之如何解析视图树
- View绘制之LayoutInflate源码分析
- LayoutInflate渲染view源码分析
- springmvc 之视图解析器源码分析
- layoutInflate 解析
- android LayoutInflate.inflate源码分析及使用区分
- MapReduce源码分析之MapReduce如何读取、解析输入文件
- opencv源码解析之:hog源码分析
- opencv源码解析之hog源码分析
- opencv源码解析之----hog源码分析
- 【源码分析】Guava源码解析之EventBus
- List源码解析之ArrayList源码分析
- List源码解析之Vector 源码分析
- List源码解析之LinkedList 源码分析
- List源码解析之LinkedList 源码分析
- Spring mvc 视图解析器 ContentNegotiatingViewResolver 源码分析
- Spring mvc 视图解析器 ContentNegotiatingViewResolver 源码分析
- spring mvc json及各类视图解析 源码分析
- memcpy和memmove的区别
- networkD3包
- Spring源码分析之ioc容器第二天之oC容器在Web容器中的启动
- 在ec2上部署spark-部署成功后无法启动
- hibernate初探之单向一对多映射
- LayoutInflate源码分析之如何解析视图树
- android实现应用程序前台和后台之间转换的监听的两种方式
- 特征工程--笔记
- 博客第一天
- 简单计算器-界面实现
- jp.ne.so_net.ga2.no_ji.jcom.JComException: createInstance() failed HRESULT=0x800401F3L
- redis集合set操作
- secureCRT连接虚拟机与使用设置
- ListView或GridView上添加能一起滚动的Button效果的解决思路