LinearLayout详解3:LayoutInflater创建View过程分析
LinearLayout详解三:LayoutInflater创建View过程分析
纵观上面的一段代码,可能最有用的无非这么一句
这样的代码实现的。
以上的代码中,注意这一行:
这一行就是通过解析tag来创建view的,这个方法的实现如下:
再关注里面的
这句话,正式开始创建了!
创建成功了!
上次讲到下面这么一段代码,这段代码的作用就是解析xml文件成为view并显示到屏幕上的。
@Override public void setContentView(int layoutResID) { if (mContentParent == null) { installDecor(); } else { mContentParent.removeAllViews(); } mLayoutInflater.inflate(layoutResID, mContentParent); final Callback cb = getCallback(); if (cb != null) { cb.onContentChanged(); } }
纵观上面的一段代码,可能最有用的无非这么一句
mLayoutInflater.inflate(layoutResID, mContentParent);他解析了id为layoutResId的资源。
mLayoutInflater这个成员变量,说起这个东西。又是一堆废话要讲。大家千万不要嫌我啰嗦,因为最终我会回到一个非常有用的主题,在阐述这个主题之前,这些“小知识”都是必备的,就比如说爱爱之前总要来点前戏对吧。
如果你听说过Fragment,那么应该知道fragment中加载视图是通过类似
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); View layout = inflater.inflate(R.layout.main_activity_menu, null); initLayouView(layout); return layout; }
这样的代码实现的。
这就是inflate的作用,他就是用来解析xml并显示到屏幕的,至于解析的代码我这边就不多做阐述了,毕竟已经超越了我们的主题太远。我们还是看inflate是如何给解析的xml分配内存,并添加到view上的吧!
这里是LayoutInflater.java的源代码
其中inflate函数的实现在这里:
/** * Inflate a new view hierarchy from the specified XML node. Throws * {@link InflateException} if there is an error. * <p> * <em><strong>Important</strong></em> For performance * reasons, view inflation relies heavily on pre-processing of XML files * that is done at build time. Therefore, it is not currently possible to * use LayoutInflater with an XmlPullParser over a plain XML file at runtime. * * @param parser XML dom node containing the description of the view * hierarchy. * @param root Optional view to be the parent of the generated hierarchy (if * <em>attachToRoot</em> is true), or else simply an object that * provides a set of LayoutParams values for root of the returned * hierarchy (if <em>attachToRoot</em> is false.) * @param attachToRoot Whether the inflated hierarchy should be attached to * the root parameter? If false, root is only used to create the * correct subclass of LayoutParams for the root view in the XML. * @return The root View of the inflated hierarchy. If root was supplied and * attachToRoot is true, this is root; otherwise it is the root of * the inflated XML file. */ public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) { synchronized (mConstructorArgs) { final AttributeSet attrs = Xml.asAttributeSet(parser); mConstructorArgs[0] = mContext; 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("**************************"); } 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); } else { // Temp is the root view that was found in the xml View temp = createViewFromTag(name, 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) { // Set the layout params for temp if we are not // attaching. (If we are, we use addView, below) temp.setLayoutParams(params); } } if (DEBUG) { System.out.println("-----> start inflating children"); } // Inflate all children under temp rInflate(parser, temp, attrs); if (DEBUG) { System.out.println("-----> done inflating children"); } // 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; } } } 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; } return result; } }
以上的代码中,注意这一行:
View temp = createViewFromTag(name, attrs);
这一行就是通过解析tag来创建view的,这个方法的实现如下:
/* * default visibility so the BridgeInflater can override it. */ View createViewFromTag(String name, AttributeSet attrs) { if (name.equals("view")) { name = attrs.getAttributeValue(null, "class"); } if (DEBUG) System.out.println("******** Creating view: " + name); try { View view = (mFactory == null) ? null : mFactory.onCreateView(name, mContext, attrs); if (view == null) { if (-1 == name.indexOf('.')) { view = onCreateView(name, attrs); } else { view = createView(name, null, attrs); } } if (DEBUG) System.out.println("Created view is: " + view); return view; } catch (InflateException e) { throw e; } catch (ClassNotFoundException e) { InflateException ie = new InflateException(attrs.getPositionDescription() + ": Error inflating class " + name); ie.initCause(e); throw ie; } catch (Exception e) { InflateException ie = new InflateException(attrs.getPositionDescription() + ": Error inflating class " + name); ie.initCause(e); throw ie; } }
再关注里面的
view = createView(name, null, attrs);
这句话,正式开始创建了!
/** * Low-level function for instantiating a view by name. This attempts to * instantiate a view class of the given <var>name</var> found in this * LayoutInflater's ClassLoader. * * <p> * There are two things that can happen in an error case: either the * exception describing the error will be thrown, or a null will be * returned. You must deal with both possibilities -- the former will happen * the first time createView() is called for a class of a particular name, * the latter every time there-after for that class name. * * @param name The full name of the class to be instantiated. * @param attrs The XML attributes supplied for this instance. * * @return View The newly instantied view, or null. */ public final View createView(String name, String prefix, AttributeSet attrs) throws ClassNotFoundException, InflateException { Constructor constructor = sConstructorMap.get(name); Class clazz = null; try { 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); 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); 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 (View) constructor.newInstance(args); } catch (NoSuchMethodException e) { InflateException ie = new InflateException(attrs.getPositionDescription() + ": Error inflating class " + (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; } }
创建成功了!
- 1楼ningshunyao9小时前
- 分析的很不错,
- Re: zjh1719小时前
- 回复ningshunyaon谢谢关注,让我们继续努力吧,android的世界无限精彩!