Android从xml加载到View对象过程解析

我们从Activity的setContentView()入手,开始源码解析,

//Activity.setContentView public void setContentView(int layoutResID) { getWindow().setContentView(layoutResID); initActionBar(); } //PhoneWindow.setContentView public void setContentView(int layoutResID) { if (mContentParent == null) { installDecor(); } else { mContentParent.removeAllViews(); } mLayoutInflater.inflate(layoutResID, mContentParent); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } }

发现是使用mLayoutInflater创建View的,所以我们去LayoutInflater.inflate()里面看下,

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 { return inflate(parser, root, attachToRoot); } finally { parser.close(); } }

先根据resource id 获取到XmlResourceParseer,意如其名,就是xml的解析器,继续往下,进入到inflate的核心方法,有些长,我们只分析关键部分:

public View inflate(XmlPullParser parser, ViewGroup root, boolean 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, attrs, false); } else { // Temp is the root view that was found in the xml View temp; if (TAG_1995.equals(name)) { temp = new BlinkLayout(mContext, attrs); } else { temp = createViewFromTag(root, name, attrs); } ...... } catch (XmlPullParserException e) { InflateException ex = new InflateException(e.getMessage()); ex.initCause(e); throw ex; } catch (IOException e) { InflateException ex = new InflateException( parser.getPositionDescription() + ": " + e.getMessage()); ex.initCause(e); throw ex; } finally { // Don't retain static reference on context. mConstructorArgs[0] = lastContext; mConstructorArgs[1] = null; } return result; } }

如果tag的名字不是TAG_1995(名字是个梗),就调用函数createViewFromTag()创建View,进去看看,

View createViewFromTag(View parent, String name, AttributeSet attrs) { if (name.equals("view")) { name = attrs.getAttributeValue(null, "class"); } ...... View view; if (mFactory2 != null) view = mFactory2.onCreateView(parent, name, mContext, attrs); else if (mFactory != null) view = mFactory.onCreateView(name, mContext, attrs); else view = null; if (view == null && mPrivateFactory != null) { view = mPrivateFactory.onCreateView(parent, name, mContext, attrs); } if (view == null) { if (-1 == name.indexOf('.')) { view = onCreateView(parent, name, attrs); } else { view = createView(name, null, attrs); } } if (DEBUG) System.out.println("Created view is: " + view); return view; ...... }

首先尝试用3个Fractory创建View,如果成功就直接返回了。注意,我们可以利用这个机制,创建自己的Factory来控制View的创建过程。

如果没有Factory或创建失败,那么走默认逻辑。

先判断name中是否有'.'字符,如果没有,则认为使用android自己的View,此时会在name的前面加上包名"android.view.";如果有这个'.',则认为使用的自定义View,这时无需添加任何前缀,认为name已经包含全包名了。

最终,使用这个全包名的name来创建实例,

private static final HashMap<String, Constructor<? extends View>> sConstructorMap = new HashMap<String, Constructor<? extends View>>(); protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException { return createView(name, "android.view.", attrs); } public final View createView(String name, String prefix, AttributeSet attrs) throws ClassNotFoundException, InflateException { Constructor<? extends View> constructor = sConstructorMap.get(name); Class<? extends View> clazz = null;    ...... if (constructor == null) { // Class not found in the cache, see if it's real, and try to add it clazz = mContext.getClassLoader().loadClass( prefix != null ? (prefix + name) : name).asSubclass(View.class); if (mFilter != null && clazz != null) { boolean allowed = mFilter.onLoadClass(clazz); if (!allowed) { failNotAllowed(name, prefix, attrs); } } constructor = clazz.getConstructor(mConstructorSignature); sConstructorMap.put(name, constructor); } else { // If we have a filter, apply it to cached constructor if (mFilter != null) { // Have we seen this name before? Boolean allowedState = mFilterMap.get(name); if (allowedState == null) { // New class -- remember whether it is allowed 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; return constructor.newInstance(args); ...... }

从源码中看到,在创建实例前,会先从一个静态Map中获取缓存,

Constructor<? extends View> constructor = sConstructorMap.get(name);

缓存的是Constructor对象,目的是用于创建实例,这里要注意sConstructorMap是静态的,并且通过Constructor创建的实例,是使用和Constructor对象同一个ClassLoader来创建的,换句话说,在同一个进程中,同一个自定义View对象,是无法用不同ClassLoader加载的,如果想解决这个问题,就不要让系统使用createView()接口创建View,做法就是自定义Factory或Factory2来自行创建View。

继续往下看,如果缓存里没有,则创建View的Class对象clazz,并缓存到sConstructorMap中,

if (constructor == null) { // Class not found in the cache, see if it's real, and try to add it clazz = mContext.getClassLoader().loadClass( prefix != null ? (prefix + name) : name).asSubclass(View.class); if (mFilter != null && clazz != null) { boolean allowed = mFilter.onLoadClass(clazz); if (!allowed) { failNotAllowed(name, prefix, attrs); } } constructor = clazz.getConstructor(mConstructorSignature); sConstructorMap.put(name, constructor); }

然后就是newInstance了,至此这个View便从xml中变成了java对象,我们继续返回到inflate函数中,看看这个View返回之后做了什么,

...... // Temp is the root view that was found in the xml View temp; if (TAG_1995.equals(name)) { temp = new BlinkLayout(mContext, attrs); } else { temp = createViewFromTag(root, name, attrs); } ViewGroup.LayoutParams params = null; if (root != null) { // Create layout params that match root, if supplied 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); } } // Inflate all children under temp rInflate(parser, temp, attrs, true); // We are supposed to attach all the views we found (int temp) // to root. Do that now. 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; } ...... return result;

从createViewFromTag返回后,会调用个rInflate(),其中parent参数就是刚才创建出的View,应该能猜到里面做了什么,

void rInflate(XmlPullParser parser, View parent, final 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_INCLUDE.equals(name)) { if (parser.getDepth() == 0) { throw new InflateException("<include /> cannot be the root element"); } parseInclude(parser, parent, attrs); } else if (TAG_MERGE.equals(name)) { throw new InflateException("<merge /> must be the root element"); } else if (TAG_1995.equals(name)) { final View view = new BlinkLayout(mContext, attrs); final ViewGroup viewGroup = (ViewGroup) parent; final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs); rInflate(parser, view, attrs, true); viewGroup.addView(view, params); } else { final View view = createViewFromTag(parent, name, attrs); final ViewGroup viewGroup = (ViewGroup) parent; final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs); rInflate(parser, view, attrs, true); viewGroup.addView(view, params); } } if (finishInflate) parent.onFinishInflate(); }

没错,就是递归的使用createViewFromTag()创建子View,并通过ViewGroup.addView添加到parent view中。

之后,这个View树上的所有View都创建完毕。然后会根据inflate()的参数(root和attachToRoot)判断是否将新创建的View添加到root view中,

if (root != null && attachToRoot) { root.addView(temp, params); }

然后,inflate()就将View返回了。

以上内容是小编给大家介绍的android从xml加载到view对象过程解析,希望对大家有所帮助!

时间: 2024-08-02 09:51:11

Android从xml加载到View对象过程解析的相关文章

Android从xml加载到View对象过程解析_Android

我们从Activity的setContentView()入手,开始源码解析, //Activity.setContentView public void setContentView(int layoutResID) { getWindow().setContentView(layoutResID); initActionBar(); } //PhoneWindow.setContentView public void setContentView(int layoutResID) { if (

Android自定义加载loading view动画组件_Android

在github上找的一个有点酷炫的loading动画https://github.com/Fichardu/CircleProgress  我写写使用步骤  自定义view(CircleProgress )的代码  package com.hysmarthotel.view; import com.hysmarthotel.roomcontrol.R; import com.hysmarthotel.util.EaseInOutCubicInterpolator; import android.

Android仿美团加载数据、小人奔跑进度动画对话框(附顺丰快递员奔跑效果)

我们都知道在Android中,常见的动画模式有两种:一种是帧动画(Frame Animation),一种是补间动画(Tween Animation).帧动画是提供了一种逐帧播放图片的动画方式,播放事先做好的图像,与gif图片原理类似,就像是在放电影一样.补间动画可以实现View组件的移动.放大.缩小以及渐变等效果.      今天我们主要来模仿一下美团中加载数据时小人奔跑动画的对话框效果,取个有趣的名字就是Running Man,奔跑吧,兄弟!话不多少,先上效果图,让各位大侠看看是不是你想要实现

Android Loader 异步加载详解二:探寻Loader内部机制

Android Loader 异步加载详解二:探寻Loader内部机制 转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/70259914 本文出自[赵彦军的博客] Android Loader 异步加载详解一:基础概念 Android Loader 异步加载详解二:探寻Loader内部机制 在上一篇文章中,讲解了 Loader 的基本概念.这一篇将会用实战的方式来探寻 Android Loader的内部机制.我们准备做一个 读取手

Android编程动态加载布局实例详解【附demo源码】_Android

本文实例讲述了Android编程动态加载布局的方法.分享给大家供大家参考,具体如下: 由于前段时间项目需要,需要在一个页面上加载根据不同的按钮加载不同的布局页面,当时想到用 tabhot .不过美工提供的界面图完全用不上tabhot ,所以想到了动态加载的方法来解决这一需求.在这里我整理了一下,写了一个 DEMO 希望大家以后少走点弯路. 首先,我们先把界面的框架图画出来,示意图如下: 中间白色部门是一个线性布局文件,我喜欢在画图的时候用不同的颜色将一块布局标示出来,方便查看.布局文件代码如下:

android实现模拟加载中的效果_Android

最近闲着没事做了一个Android小程序,具体如下: 效果图: 原始界面 点击按钮运行    运行后界面 实现代码: public class MainActivity extends AppCompatActivity { private Button bt; private TextView tv; public ProgressDialog myDialog; @Override protected void onCreate(Bundle savedInstanceState) { su

Android Bitmap的加载优化与Cache相关介绍

一 . 高效加载 Bitmap BitMapFactory 提供了四类方法: decodeFile,decodeResource,decodeStream 和 decodeByteArray 分别用于从文件系统,资源,输入流以及字节数组中加载出一个 Bitmap 对象. 高效加载 Bitmap 很简单,即采用 BitMapFactory.options 来加载所需要尺寸图片.BitMapFactory.options 就可以按照一定的采样率来加载缩小后的图片,将缩小后的图片置于 ImageVie

Android实现网络加载时的对话框功能

效果预览 简要说明 现在android程序网络请求操作是必不可少的,然而拥有好的交互体验的程序对网络耗时操作的处理尤为重要. 代码说明: dialog_loading.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="

布局-Android imageloade异步加载图片的,怎么知道图片是否加载完成?

问题描述 Android imageloade异步加载图片的,怎么知道图片是否加载完成? -image-loade 用了这个异步加载图片包,怎么知道图片加载完成? 想当加载完成,再显示layout,而不是显示一张图片.. 解决方案 imageloader有回调函数的. ImageLoader.getInstance().displayImage(uri, imageView, new ImageLoadingListener() { @Override public void onLoading