• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.view;
18 
19 import android.content.Context;
20 import android.content.res.TypedArray;
21 import android.content.res.XmlResourceParser;
22 import android.util.AttributeSet;
23 import android.util.Xml;
24 
25 import org.xmlpull.v1.XmlPullParser;
26 import org.xmlpull.v1.XmlPullParserException;
27 
28 import java.io.IOException;
29 import java.lang.reflect.Constructor;
30 import java.util.HashMap;
31 
32 /**
33  * This class is used to instantiate layout XML file into its corresponding View
34  * objects. It is never be used directly -- use
35  * {@link android.app.Activity#getLayoutInflater()} or
36  * {@link Context#getSystemService} to retrieve a standard LayoutInflater instance
37  * that is already hooked up to the current context and correctly configured
38  * for the device you are running on.  For example:
39  *
40  * <pre>LayoutInflater inflater = (LayoutInflater)context.getSystemService
41  *      Context.LAYOUT_INFLATER_SERVICE);</pre>
42  *
43  * <p>
44  * To create a new LayoutInflater with an additional {@link Factory} for your
45  * own views, you can use {@link #cloneInContext} to clone an existing
46  * ViewFactory, and then call {@link #setFactory} on it to include your
47  * Factory.
48  *
49  * <p>
50  * For performance reasons, view inflation relies heavily on pre-processing of
51  * XML files that is done at build time. Therefore, it is not currently possible
52  * to use LayoutInflater with an XmlPullParser over a plain XML file at runtime;
53  * it only works with an XmlPullParser returned from a compiled resource
54  * (R.<em>something</em> file.)
55  *
56  * @see Context#getSystemService
57  */
58 public abstract class LayoutInflater {
59     private final boolean DEBUG = false;
60 
61     /**
62      * This field should be made private, so it is hidden from the SDK.
63      * {@hide}
64      */
65     protected final Context mContext;
66 
67     // these are optional, set by the caller
68     private boolean mFactorySet;
69     private Factory mFactory;
70     private Filter mFilter;
71 
72     private final Object[] mConstructorArgs = new Object[2];
73 
74     private static final Class[] mConstructorSignature = new Class[] {
75             Context.class, AttributeSet.class};
76 
77     private static final HashMap<String, Constructor> sConstructorMap =
78             new HashMap<String, Constructor>();
79 
80     private HashMap<String, Boolean> mFilterMap;
81 
82     private static final String TAG_MERGE = "merge";
83     private static final String TAG_INCLUDE = "include";
84     private static final String TAG_REQUEST_FOCUS = "requestFocus";
85 
86     /**
87      * Hook to allow clients of the LayoutInflater to restrict the set of Views that are allowed
88      * to be inflated.
89      *
90      */
91     public interface Filter {
92         /**
93          * Hook to allow clients of the LayoutInflater to restrict the set of Views
94          * that are allowed to be inflated.
95          *
96          * @param clazz The class object for the View that is about to be inflated
97          *
98          * @return True if this class is allowed to be inflated, or false otherwise
99          */
onLoadClass(Class clazz)100         boolean onLoadClass(Class clazz);
101     }
102 
103     public interface Factory {
104         /**
105          * Hook you can supply that is called when inflating from a LayoutInflater.
106          * You can use this to customize the tag names available in your XML
107          * layout files.
108          *
109          * <p>
110          * Note that it is good practice to prefix these custom names with your
111          * package (i.e., com.coolcompany.apps) to avoid conflicts with system
112          * names.
113          *
114          * @param name Tag name to be inflated.
115          * @param context The context the view is being created in.
116          * @param attrs Inflation attributes as specified in XML file.
117          *
118          * @return View Newly created view. Return null for the default
119          *         behavior.
120          */
onCreateView(String name, Context context, AttributeSet attrs)121         public View onCreateView(String name, Context context, AttributeSet attrs);
122     }
123 
124     private static class FactoryMerger implements Factory {
125         private final Factory mF1, mF2;
126 
FactoryMerger(Factory f1, Factory f2)127         FactoryMerger(Factory f1, Factory f2) {
128             mF1 = f1;
129             mF2 = f2;
130         }
131 
onCreateView(String name, Context context, AttributeSet attrs)132         public View onCreateView(String name, Context context, AttributeSet attrs) {
133             View v = mF1.onCreateView(name, context, attrs);
134             if (v != null) return v;
135             return mF2.onCreateView(name, context, attrs);
136         }
137     }
138 
139     /**
140      * Create a new LayoutInflater instance associated with a particular Context.
141      * Applications will almost always want to use
142      * {@link Context#getSystemService Context.getSystemService()} to retrieve
143      * the standard {@link Context#LAYOUT_INFLATER_SERVICE Context.INFLATER_SERVICE}.
144      *
145      * @param context The Context in which this LayoutInflater will create its
146      * Views; most importantly, this supplies the theme from which the default
147      * values for their attributes are retrieved.
148      */
LayoutInflater(Context context)149     protected LayoutInflater(Context context) {
150         mContext = context;
151     }
152 
153     /**
154      * Create a new LayoutInflater instance that is a copy of an existing
155      * LayoutInflater, optionally with its Context changed.  For use in
156      * implementing {@link #cloneInContext}.
157      *
158      * @param original The original LayoutInflater to copy.
159      * @param newContext The new Context to use.
160      */
LayoutInflater(LayoutInflater original, Context newContext)161     protected LayoutInflater(LayoutInflater original, Context newContext) {
162         mContext = newContext;
163         mFactory = original.mFactory;
164         mFilter = original.mFilter;
165     }
166 
167     /**
168      * Obtains the LayoutInflater from the given context.
169      */
from(Context context)170     public static LayoutInflater from(Context context) {
171         LayoutInflater LayoutInflater =
172                 (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
173         if (LayoutInflater == null) {
174             throw new AssertionError("LayoutInflater not found.");
175         }
176         return LayoutInflater;
177     }
178 
179     /**
180      * Create a copy of the existing LayoutInflater object, with the copy
181      * pointing to a different Context than the original.  This is used by
182      * {@link ContextThemeWrapper} to create a new LayoutInflater to go along
183      * with the new Context theme.
184      *
185      * @param newContext The new Context to associate with the new LayoutInflater.
186      * May be the same as the original Context if desired.
187      *
188      * @return Returns a brand spanking new LayoutInflater object associated with
189      * the given Context.
190      */
cloneInContext(Context newContext)191     public abstract LayoutInflater cloneInContext(Context newContext);
192 
193     /**
194      * Return the context we are running in, for access to resources, class
195      * loader, etc.
196      */
getContext()197     public Context getContext() {
198         return mContext;
199     }
200 
201     /**
202      * Return the current factory (or null). This is called on each element
203      * name. If the factory returns a View, add that to the hierarchy. If it
204      * returns null, proceed to call onCreateView(name).
205      */
getFactory()206     public final Factory getFactory() {
207         return mFactory;
208     }
209 
210     /**
211      * Attach a custom Factory interface for creating views while using
212      * this LayoutInflater.  This must not be null, and can only be set once;
213      * after setting, you can not change the factory.  This is
214      * called on each element name as the xml is parsed. If the factory returns
215      * a View, that is added to the hierarchy. If it returns null, the next
216      * factory default {@link #onCreateView} method is called.
217      *
218      * <p>If you have an existing
219      * LayoutInflater and want to add your own factory to it, use
220      * {@link #cloneInContext} to clone the existing instance and then you
221      * can use this function (once) on the returned new instance.  This will
222      * merge your own factory with whatever factory the original instance is
223      * using.
224      */
setFactory(Factory factory)225     public void setFactory(Factory factory) {
226         if (mFactorySet) {
227             throw new IllegalStateException("A factory has already been set on this LayoutInflater");
228         }
229         if (factory == null) {
230             throw new NullPointerException("Given factory can not be null");
231         }
232         mFactorySet = true;
233         if (mFactory == null) {
234             mFactory = factory;
235         } else {
236             mFactory = new FactoryMerger(factory, mFactory);
237         }
238     }
239 
240     /**
241      * @return The {@link Filter} currently used by this LayoutInflater to restrict the set of Views
242      * that are allowed to be inflated.
243      */
getFilter()244     public Filter getFilter() {
245         return mFilter;
246     }
247 
248     /**
249      * Sets the {@link Filter} to by this LayoutInflater. If a view is attempted to be inflated
250      * which is not allowed by the {@link Filter}, the {@link #inflate(int, ViewGroup)} call will
251      * throw an {@link InflateException}. This filter will replace any previous filter set on this
252      * LayoutInflater.
253      *
254      * @param filter The Filter which restricts the set of Views that are allowed to be inflated.
255      *        This filter will replace any previous filter set on this LayoutInflater.
256      */
setFilter(Filter filter)257     public void setFilter(Filter filter) {
258         mFilter = filter;
259         if (filter != null) {
260             mFilterMap = new HashMap<String, Boolean>();
261         }
262     }
263 
264     /**
265      * Inflate a new view hierarchy from the specified xml resource. Throws
266      * {@link InflateException} if there is an error.
267      *
268      * @param resource ID for an XML layout resource to load (e.g.,
269      *        <code>R.layout.main_page</code>)
270      * @param root Optional view to be the parent of the generated hierarchy.
271      * @return The root View of the inflated hierarchy. If root was supplied,
272      *         this is the root View; otherwise it is the root of the inflated
273      *         XML file.
274      */
inflate(int resource, ViewGroup root)275     public View inflate(int resource, ViewGroup root) {
276         return inflate(resource, root, root != null);
277     }
278 
279     /**
280      * Inflate a new view hierarchy from the specified xml node. Throws
281      * {@link InflateException} if there is an error. *
282      * <p>
283      * <em><strong>Important</strong></em>&nbsp;&nbsp;&nbsp;For performance
284      * reasons, view inflation relies heavily on pre-processing of XML files
285      * that is done at build time. Therefore, it is not currently possible to
286      * use LayoutInflater with an XmlPullParser over a plain XML file at runtime.
287      *
288      * @param parser XML dom node containing the description of the view
289      *        hierarchy.
290      * @param root Optional view to be the parent of the generated hierarchy.
291      * @return The root View of the inflated hierarchy. If root was supplied,
292      *         this is the root View; otherwise it is the root of the inflated
293      *         XML file.
294      */
inflate(XmlPullParser parser, ViewGroup root)295     public View inflate(XmlPullParser parser, ViewGroup root) {
296         return inflate(parser, root, root != null);
297     }
298 
299     /**
300      * Inflate a new view hierarchy from the specified xml resource. Throws
301      * {@link InflateException} if there is an error.
302      *
303      * @param resource ID for an XML layout resource to load (e.g.,
304      *        <code>R.layout.main_page</code>)
305      * @param root Optional view to be the parent of the generated hierarchy (if
306      *        <em>attachToRoot</em> is true), or else simply an object that
307      *        provides a set of LayoutParams values for root of the returned
308      *        hierarchy (if <em>attachToRoot</em> is false.)
309      * @param attachToRoot Whether the inflated hierarchy should be attached to
310      *        the root parameter? If false, root is only used to create the
311      *        correct subclass of LayoutParams for the root view in the XML.
312      * @return The root View of the inflated hierarchy. If root was supplied and
313      *         attachToRoot is true, this is root; otherwise it is the root of
314      *         the inflated XML file.
315      */
inflate(int resource, ViewGroup root, boolean attachToRoot)316     public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
317         if (DEBUG) System.out.println("INFLATING from resource: " + resource);
318         XmlResourceParser parser = getContext().getResources().getLayout(resource);
319         try {
320             return inflate(parser, root, attachToRoot);
321         } finally {
322             parser.close();
323         }
324     }
325 
326     /**
327      * Inflate a new view hierarchy from the specified XML node. Throws
328      * {@link InflateException} if there is an error.
329      * <p>
330      * <em><strong>Important</strong></em>&nbsp;&nbsp;&nbsp;For performance
331      * reasons, view inflation relies heavily on pre-processing of XML files
332      * that is done at build time. Therefore, it is not currently possible to
333      * use LayoutInflater with an XmlPullParser over a plain XML file at runtime.
334      *
335      * @param parser XML dom node containing the description of the view
336      *        hierarchy.
337      * @param root Optional view to be the parent of the generated hierarchy (if
338      *        <em>attachToRoot</em> is true), or else simply an object that
339      *        provides a set of LayoutParams values for root of the returned
340      *        hierarchy (if <em>attachToRoot</em> is false.)
341      * @param attachToRoot Whether the inflated hierarchy should be attached to
342      *        the root parameter? If false, root is only used to create the
343      *        correct subclass of LayoutParams for the root view in the XML.
344      * @return The root View of the inflated hierarchy. If root was supplied and
345      *         attachToRoot is true, this is root; otherwise it is the root of
346      *         the inflated XML file.
347      */
inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)348     public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
349         synchronized (mConstructorArgs) {
350             final AttributeSet attrs = Xml.asAttributeSet(parser);
351             mConstructorArgs[0] = mContext;
352             View result = root;
353 
354             try {
355                 // Look for the root node.
356                 int type;
357                 while ((type = parser.next()) != XmlPullParser.START_TAG &&
358                         type != XmlPullParser.END_DOCUMENT) {
359                     // Empty
360                 }
361 
362                 if (type != XmlPullParser.START_TAG) {
363                     throw new InflateException(parser.getPositionDescription()
364                             + ": No start tag found!");
365                 }
366 
367                 final String name = parser.getName();
368 
369                 if (DEBUG) {
370                     System.out.println("**************************");
371                     System.out.println("Creating root view: "
372                             + name);
373                     System.out.println("**************************");
374                 }
375 
376                 if (TAG_MERGE.equals(name)) {
377                     if (root == null || !attachToRoot) {
378                         throw new InflateException("<merge /> can be used only with a valid "
379                                 + "ViewGroup root and attachToRoot=true");
380                     }
381 
382                     rInflate(parser, root, attrs);
383                 } else {
384                     // Temp is the root view that was found in the xml
385                     View temp = createViewFromTag(name, attrs);
386 
387                     ViewGroup.LayoutParams params = null;
388 
389                     if (root != null) {
390                         if (DEBUG) {
391                             System.out.println("Creating params from root: " +
392                                     root);
393                         }
394                         // Create layout params that match root, if supplied
395                         params = root.generateLayoutParams(attrs);
396                         if (!attachToRoot) {
397                             // Set the layout params for temp if we are not
398                             // attaching. (If we are, we use addView, below)
399                             temp.setLayoutParams(params);
400                         }
401                     }
402 
403                     if (DEBUG) {
404                         System.out.println("-----> start inflating children");
405                     }
406                     // Inflate all children under temp
407                     rInflate(parser, temp, attrs);
408                     if (DEBUG) {
409                         System.out.println("-----> done inflating children");
410                     }
411 
412                     // We are supposed to attach all the views we found (int temp)
413                     // to root. Do that now.
414                     if (root != null && attachToRoot) {
415                         root.addView(temp, params);
416                     }
417 
418                     // Decide whether to return the root that was passed in or the
419                     // top view found in xml.
420                     if (root == null || !attachToRoot) {
421                         result = temp;
422                     }
423                 }
424 
425             } catch (XmlPullParserException e) {
426                 InflateException ex = new InflateException(e.getMessage());
427                 ex.initCause(e);
428                 throw ex;
429             } catch (IOException e) {
430                 InflateException ex = new InflateException(
431                         parser.getPositionDescription()
432                         + ": " + e.getMessage());
433                 ex.initCause(e);
434                 throw ex;
435             }
436 
437             return result;
438         }
439     }
440 
441     /**
442      * Low-level function for instantiating a view by name. This attempts to
443      * instantiate a view class of the given <var>name</var> found in this
444      * LayoutInflater's ClassLoader.
445      *
446      * <p>
447      * There are two things that can happen in an error case: either the
448      * exception describing the error will be thrown, or a null will be
449      * returned. You must deal with both possibilities -- the former will happen
450      * the first time createView() is called for a class of a particular name,
451      * the latter every time there-after for that class name.
452      *
453      * @param name The full name of the class to be instantiated.
454      * @param attrs The XML attributes supplied for this instance.
455      *
456      * @return View The newly instantied view, or null.
457      */
createView(String name, String prefix, AttributeSet attrs)458     public final View createView(String name, String prefix, AttributeSet attrs)
459             throws ClassNotFoundException, InflateException {
460         Constructor constructor = sConstructorMap.get(name);
461         Class clazz = null;
462 
463         try {
464             if (constructor == null) {
465                 // Class not found in the cache, see if it's real, and try to add it
466                 clazz = mContext.getClassLoader().loadClass(
467                         prefix != null ? (prefix + name) : name);
468 
469                 if (mFilter != null && clazz != null) {
470                     boolean allowed = mFilter.onLoadClass(clazz);
471                     if (!allowed) {
472                         failNotAllowed(name, prefix, attrs);
473                     }
474                 }
475                 constructor = clazz.getConstructor(mConstructorSignature);
476                 sConstructorMap.put(name, constructor);
477             } else {
478                 // If we have a filter, apply it to cached constructor
479                 if (mFilter != null) {
480                     // Have we seen this name before?
481                     Boolean allowedState = mFilterMap.get(name);
482                     if (allowedState == null) {
483                         // New class -- remember whether it is allowed
484                         clazz = mContext.getClassLoader().loadClass(
485                                 prefix != null ? (prefix + name) : name);
486 
487                         boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
488                         mFilterMap.put(name, allowed);
489                         if (!allowed) {
490                             failNotAllowed(name, prefix, attrs);
491                         }
492                     } else if (allowedState.equals(Boolean.FALSE)) {
493                         failNotAllowed(name, prefix, attrs);
494                     }
495                 }
496             }
497 
498             Object[] args = mConstructorArgs;
499             args[1] = attrs;
500             return (View) constructor.newInstance(args);
501 
502         } catch (NoSuchMethodException e) {
503             InflateException ie = new InflateException(attrs.getPositionDescription()
504                     + ": Error inflating class "
505                     + (prefix != null ? (prefix + name) : name));
506             ie.initCause(e);
507             throw ie;
508 
509         } catch (ClassNotFoundException e) {
510             // If loadClass fails, we should propagate the exception.
511             throw e;
512         } catch (Exception e) {
513             InflateException ie = new InflateException(attrs.getPositionDescription()
514                     + ": Error inflating class "
515                     + (clazz == null ? "<unknown>" : clazz.getName()));
516             ie.initCause(e);
517             throw ie;
518         }
519     }
520 
521     /**
522      * Throw an excpetion because the specified class is not allowed to be inflated.
523      */
failNotAllowed(String name, String prefix, AttributeSet attrs)524     private void failNotAllowed(String name, String prefix, AttributeSet attrs) {
525         InflateException ie = new InflateException(attrs.getPositionDescription()
526                 + ": Class not allowed to be inflated "
527                 + (prefix != null ? (prefix + name) : name));
528         throw ie;
529     }
530 
531     /**
532      * This routine is responsible for creating the correct subclass of View
533      * given the xml element name. Override it to handle custom view objects. If
534      * you override this in your subclass be sure to call through to
535      * super.onCreateView(name) for names you do not recognize.
536      *
537      * @param name The fully qualified class name of the View to be create.
538      * @param attrs An AttributeSet of attributes to apply to the View.
539      *
540      * @return View The View created.
541      */
onCreateView(String name, AttributeSet attrs)542     protected View onCreateView(String name, AttributeSet attrs)
543             throws ClassNotFoundException {
544         return createView(name, "android.view.", attrs);
545     }
546 
547     /*
548      * default visibility so the BridgeInflater can override it.
549      */
createViewFromTag(String name, AttributeSet attrs)550     View createViewFromTag(String name, AttributeSet attrs) {
551         if (name.equals("view")) {
552             name = attrs.getAttributeValue(null, "class");
553         }
554 
555         if (DEBUG) System.out.println("******** Creating view: " + name);
556 
557         try {
558             View view = (mFactory == null) ? null : mFactory.onCreateView(name,
559                     mContext, attrs);
560 
561             if (view == null) {
562                 if (-1 == name.indexOf('.')) {
563                     view = onCreateView(name, attrs);
564                 } else {
565                     view = createView(name, null, attrs);
566                 }
567             }
568 
569             if (DEBUG) System.out.println("Created view is: " + view);
570             return view;
571 
572         } catch (InflateException e) {
573             throw e;
574 
575         } catch (ClassNotFoundException e) {
576             InflateException ie = new InflateException(attrs.getPositionDescription()
577                     + ": Error inflating class " + name);
578             ie.initCause(e);
579             throw ie;
580 
581         } catch (Exception e) {
582             InflateException ie = new InflateException(attrs.getPositionDescription()
583                     + ": Error inflating class " + name);
584             ie.initCause(e);
585             throw ie;
586         }
587     }
588 
589     /**
590      * Recursive method used to descend down the xml hierarchy and instantiate
591      * views, instantiate their children, and then call onFinishInflate().
592      */
rInflate(XmlPullParser parser, View parent, final AttributeSet attrs)593     private void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs)
594             throws XmlPullParserException, IOException {
595 
596         final int depth = parser.getDepth();
597         int type;
598 
599         while (((type = parser.next()) != XmlPullParser.END_TAG ||
600                 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
601 
602             if (type != XmlPullParser.START_TAG) {
603                 continue;
604             }
605 
606             final String name = parser.getName();
607 
608             if (TAG_REQUEST_FOCUS.equals(name)) {
609                 parseRequestFocus(parser, parent);
610             } else if (TAG_INCLUDE.equals(name)) {
611                 if (parser.getDepth() == 0) {
612                     throw new InflateException("<include /> cannot be the root element");
613                 }
614                 parseInclude(parser, parent, attrs);
615             } else if (TAG_MERGE.equals(name)) {
616                 throw new InflateException("<merge /> must be the root element");
617             } else {
618                 final View view = createViewFromTag(name, attrs);
619                 final ViewGroup viewGroup = (ViewGroup) parent;
620                 final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
621                 rInflate(parser, view, attrs);
622                 viewGroup.addView(view, params);
623             }
624         }
625 
626         parent.onFinishInflate();
627     }
628 
parseRequestFocus(XmlPullParser parser, View parent)629     private void parseRequestFocus(XmlPullParser parser, View parent)
630             throws XmlPullParserException, IOException {
631         int type;
632         parent.requestFocus();
633         final int currentDepth = parser.getDepth();
634         while (((type = parser.next()) != XmlPullParser.END_TAG ||
635                 parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
636             // Empty
637         }
638     }
639 
parseInclude(XmlPullParser parser, View parent, AttributeSet attrs)640     private void parseInclude(XmlPullParser parser, View parent, AttributeSet attrs)
641             throws XmlPullParserException, IOException {
642 
643         int type;
644 
645         if (parent instanceof ViewGroup) {
646             final int layout = attrs.getAttributeResourceValue(null, "layout", 0);
647             if (layout == 0) {
648                 final String value = attrs.getAttributeValue(null, "layout");
649                 if (value == null) {
650                     throw new InflateException("You must specifiy a layout in the"
651                             + " include tag: <include layout=\"@layout/layoutID\" />");
652                 } else {
653                     throw new InflateException("You must specifiy a valid layout "
654                             + "reference. The layout ID " + value + " is not valid.");
655                 }
656             } else {
657                 final XmlResourceParser childParser =
658                         getContext().getResources().getLayout(layout);
659 
660                 try {
661                     final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
662 
663                     while ((type = childParser.next()) != XmlPullParser.START_TAG &&
664                             type != XmlPullParser.END_DOCUMENT) {
665                         // Empty.
666                     }
667 
668                     if (type != XmlPullParser.START_TAG) {
669                         throw new InflateException(childParser.getPositionDescription() +
670                                 ": No start tag found!");
671                     }
672 
673                     final String childName = childParser.getName();
674 
675                     if (TAG_MERGE.equals(childName)) {
676                         // Inflate all children.
677                         rInflate(childParser, parent, childAttrs);
678                     } else {
679                         final View view = createViewFromTag(childName, childAttrs);
680                         final ViewGroup group = (ViewGroup) parent;
681 
682                         // We try to load the layout params set in the <include /> tag. If
683                         // they don't exist, we will rely on the layout params set in the
684                         // included XML file.
685                         // During a layoutparams generation, a runtime exception is thrown
686                         // if either layout_width or layout_height is missing. We catch
687                         // this exception and set localParams accordingly: true means we
688                         // successfully loaded layout params from the <include /> tag,
689                         // false means we need to rely on the included layout params.
690                         ViewGroup.LayoutParams params = null;
691                         try {
692                             params = group.generateLayoutParams(attrs);
693                         } catch (RuntimeException e) {
694                             params = group.generateLayoutParams(childAttrs);
695                         } finally {
696                             if (params != null) {
697                                 view.setLayoutParams(params);
698                             }
699                         }
700 
701                         // Inflate all children.
702                         rInflate(childParser, view, childAttrs);
703 
704                         // Attempt to override the included layout's android:id with the
705                         // one set on the <include /> tag itself.
706                         TypedArray a = mContext.obtainStyledAttributes(attrs,
707                             com.android.internal.R.styleable.View, 0, 0);
708                         int id = a.getResourceId(com.android.internal.R.styleable.View_id, View.NO_ID);
709                         // While we're at it, let's try to override android:visibility.
710                         int visibility = a.getInt(com.android.internal.R.styleable.View_visibility, -1);
711                         a.recycle();
712 
713                         if (id != View.NO_ID) {
714                             view.setId(id);
715                         }
716 
717                         switch (visibility) {
718                             case 0:
719                                 view.setVisibility(View.VISIBLE);
720                                 break;
721                             case 1:
722                                 view.setVisibility(View.INVISIBLE);
723                                 break;
724                             case 2:
725                                 view.setVisibility(View.GONE);
726                                 break;
727                         }
728 
729                         group.addView(view);
730                     }
731                 } finally {
732                     childParser.close();
733                 }
734             }
735         } else {
736             throw new InflateException("<include /> can only be used inside of a ViewGroup");
737         }
738 
739         final int currentDepth = parser.getDepth();
740         while (((type = parser.next()) != XmlPullParser.END_TAG ||
741                 parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
742             // Empty
743         }
744     }
745 }
746