• 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             Context lastContext = (Context)mConstructorArgs[0];
352             mConstructorArgs[0] = mContext;
353             View result = root;
354 
355             try {
356                 // Look for the root node.
357                 int type;
358                 while ((type = parser.next()) != XmlPullParser.START_TAG &&
359                         type != XmlPullParser.END_DOCUMENT) {
360                     // Empty
361                 }
362 
363                 if (type != XmlPullParser.START_TAG) {
364                     throw new InflateException(parser.getPositionDescription()
365                             + ": No start tag found!");
366                 }
367 
368                 final String name = parser.getName();
369 
370                 if (DEBUG) {
371                     System.out.println("**************************");
372                     System.out.println("Creating root view: "
373                             + name);
374                     System.out.println("**************************");
375                 }
376 
377                 if (TAG_MERGE.equals(name)) {
378                     if (root == null || !attachToRoot) {
379                         throw new InflateException("<merge /> can be used only with a valid "
380                                 + "ViewGroup root and attachToRoot=true");
381                     }
382 
383                     rInflate(parser, root, attrs);
384                 } else {
385                     // Temp is the root view that was found in the xml
386                     View temp = createViewFromTag(name, attrs);
387 
388                     ViewGroup.LayoutParams params = null;
389 
390                     if (root != null) {
391                         if (DEBUG) {
392                             System.out.println("Creating params from root: " +
393                                     root);
394                         }
395                         // Create layout params that match root, if supplied
396                         params = root.generateLayoutParams(attrs);
397                         if (!attachToRoot) {
398                             // Set the layout params for temp if we are not
399                             // attaching. (If we are, we use addView, below)
400                             temp.setLayoutParams(params);
401                         }
402                     }
403 
404                     if (DEBUG) {
405                         System.out.println("-----> start inflating children");
406                     }
407                     // Inflate all children under temp
408                     rInflate(parser, temp, attrs);
409                     if (DEBUG) {
410                         System.out.println("-----> done inflating children");
411                     }
412 
413                     // We are supposed to attach all the views we found (int temp)
414                     // to root. Do that now.
415                     if (root != null && attachToRoot) {
416                         root.addView(temp, params);
417                     }
418 
419                     // Decide whether to return the root that was passed in or the
420                     // top view found in xml.
421                     if (root == null || !attachToRoot) {
422                         result = temp;
423                     }
424                 }
425 
426             } catch (XmlPullParserException e) {
427                 InflateException ex = new InflateException(e.getMessage());
428                 ex.initCause(e);
429                 throw ex;
430             } catch (IOException e) {
431                 InflateException ex = new InflateException(
432                         parser.getPositionDescription()
433                         + ": " + e.getMessage());
434                 ex.initCause(e);
435                 throw ex;
436             } finally {
437                 // Don't retain static reference on context.
438                 mConstructorArgs[0] = lastContext;
439                 mConstructorArgs[1] = null;
440             }
441 
442             return result;
443         }
444     }
445 
446     /**
447      * Low-level function for instantiating a view by name. This attempts to
448      * instantiate a view class of the given <var>name</var> found in this
449      * LayoutInflater's ClassLoader.
450      *
451      * <p>
452      * There are two things that can happen in an error case: either the
453      * exception describing the error will be thrown, or a null will be
454      * returned. You must deal with both possibilities -- the former will happen
455      * the first time createView() is called for a class of a particular name,
456      * the latter every time there-after for that class name.
457      *
458      * @param name The full name of the class to be instantiated.
459      * @param attrs The XML attributes supplied for this instance.
460      *
461      * @return View The newly instantied view, or null.
462      */
createView(String name, String prefix, AttributeSet attrs)463     public final View createView(String name, String prefix, AttributeSet attrs)
464             throws ClassNotFoundException, InflateException {
465         Constructor constructor = sConstructorMap.get(name);
466         Class clazz = null;
467 
468         try {
469             if (constructor == null) {
470                 // Class not found in the cache, see if it's real, and try to add it
471                 clazz = mContext.getClassLoader().loadClass(
472                         prefix != null ? (prefix + name) : name);
473 
474                 if (mFilter != null && clazz != null) {
475                     boolean allowed = mFilter.onLoadClass(clazz);
476                     if (!allowed) {
477                         failNotAllowed(name, prefix, attrs);
478                     }
479                 }
480                 constructor = clazz.getConstructor(mConstructorSignature);
481                 sConstructorMap.put(name, constructor);
482             } else {
483                 // If we have a filter, apply it to cached constructor
484                 if (mFilter != null) {
485                     // Have we seen this name before?
486                     Boolean allowedState = mFilterMap.get(name);
487                     if (allowedState == null) {
488                         // New class -- remember whether it is allowed
489                         clazz = mContext.getClassLoader().loadClass(
490                                 prefix != null ? (prefix + name) : name);
491 
492                         boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
493                         mFilterMap.put(name, allowed);
494                         if (!allowed) {
495                             failNotAllowed(name, prefix, attrs);
496                         }
497                     } else if (allowedState.equals(Boolean.FALSE)) {
498                         failNotAllowed(name, prefix, attrs);
499                     }
500                 }
501             }
502 
503             Object[] args = mConstructorArgs;
504             args[1] = attrs;
505             return (View) constructor.newInstance(args);
506 
507         } catch (NoSuchMethodException e) {
508             InflateException ie = new InflateException(attrs.getPositionDescription()
509                     + ": Error inflating class "
510                     + (prefix != null ? (prefix + name) : name));
511             ie.initCause(e);
512             throw ie;
513 
514         } catch (ClassNotFoundException e) {
515             // If loadClass fails, we should propagate the exception.
516             throw e;
517         } catch (Exception e) {
518             InflateException ie = new InflateException(attrs.getPositionDescription()
519                     + ": Error inflating class "
520                     + (clazz == null ? "<unknown>" : clazz.getName()));
521             ie.initCause(e);
522             throw ie;
523         }
524     }
525 
526     /**
527      * Throw an excpetion because the specified class is not allowed to be inflated.
528      */
failNotAllowed(String name, String prefix, AttributeSet attrs)529     private void failNotAllowed(String name, String prefix, AttributeSet attrs) {
530         InflateException ie = new InflateException(attrs.getPositionDescription()
531                 + ": Class not allowed to be inflated "
532                 + (prefix != null ? (prefix + name) : name));
533         throw ie;
534     }
535 
536     /**
537      * This routine is responsible for creating the correct subclass of View
538      * given the xml element name. Override it to handle custom view objects. If
539      * you override this in your subclass be sure to call through to
540      * super.onCreateView(name) for names you do not recognize.
541      *
542      * @param name The fully qualified class name of the View to be create.
543      * @param attrs An AttributeSet of attributes to apply to the View.
544      *
545      * @return View The View created.
546      */
onCreateView(String name, AttributeSet attrs)547     protected View onCreateView(String name, AttributeSet attrs)
548             throws ClassNotFoundException {
549         return createView(name, "android.view.", attrs);
550     }
551 
552     /*
553      * default visibility so the BridgeInflater can override it.
554      */
createViewFromTag(String name, AttributeSet attrs)555     View createViewFromTag(String name, AttributeSet attrs) {
556         if (name.equals("view")) {
557             name = attrs.getAttributeValue(null, "class");
558         }
559 
560         if (DEBUG) System.out.println("******** Creating view: " + name);
561 
562         try {
563             View view = (mFactory == null) ? null : mFactory.onCreateView(name,
564                     mContext, attrs);
565 
566             if (view == null) {
567                 if (-1 == name.indexOf('.')) {
568                     view = onCreateView(name, attrs);
569                 } else {
570                     view = createView(name, null, attrs);
571                 }
572             }
573 
574             if (DEBUG) System.out.println("Created view is: " + view);
575             return view;
576 
577         } catch (InflateException e) {
578             throw e;
579 
580         } catch (ClassNotFoundException e) {
581             InflateException ie = new InflateException(attrs.getPositionDescription()
582                     + ": Error inflating class " + name);
583             ie.initCause(e);
584             throw ie;
585 
586         } catch (Exception e) {
587             InflateException ie = new InflateException(attrs.getPositionDescription()
588                     + ": Error inflating class " + name);
589             ie.initCause(e);
590             throw ie;
591         }
592     }
593 
594     /**
595      * Recursive method used to descend down the xml hierarchy and instantiate
596      * views, instantiate their children, and then call onFinishInflate().
597      */
rInflate(XmlPullParser parser, View parent, final AttributeSet attrs)598     private void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs)
599             throws XmlPullParserException, IOException {
600 
601         final int depth = parser.getDepth();
602         int type;
603 
604         while (((type = parser.next()) != XmlPullParser.END_TAG ||
605                 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
606 
607             if (type != XmlPullParser.START_TAG) {
608                 continue;
609             }
610 
611             final String name = parser.getName();
612 
613             if (TAG_REQUEST_FOCUS.equals(name)) {
614                 parseRequestFocus(parser, parent);
615             } else if (TAG_INCLUDE.equals(name)) {
616                 if (parser.getDepth() == 0) {
617                     throw new InflateException("<include /> cannot be the root element");
618                 }
619                 parseInclude(parser, parent, attrs);
620             } else if (TAG_MERGE.equals(name)) {
621                 throw new InflateException("<merge /> must be the root element");
622             } else {
623                 final View view = createViewFromTag(name, attrs);
624                 final ViewGroup viewGroup = (ViewGroup) parent;
625                 final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
626                 rInflate(parser, view, attrs);
627                 viewGroup.addView(view, params);
628             }
629         }
630 
631         parent.onFinishInflate();
632     }
633 
parseRequestFocus(XmlPullParser parser, View parent)634     private void parseRequestFocus(XmlPullParser parser, View parent)
635             throws XmlPullParserException, IOException {
636         int type;
637         parent.requestFocus();
638         final int currentDepth = parser.getDepth();
639         while (((type = parser.next()) != XmlPullParser.END_TAG ||
640                 parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
641             // Empty
642         }
643     }
644 
parseInclude(XmlPullParser parser, View parent, AttributeSet attrs)645     private void parseInclude(XmlPullParser parser, View parent, AttributeSet attrs)
646             throws XmlPullParserException, IOException {
647 
648         int type;
649 
650         if (parent instanceof ViewGroup) {
651             final int layout = attrs.getAttributeResourceValue(null, "layout", 0);
652             if (layout == 0) {
653                 final String value = attrs.getAttributeValue(null, "layout");
654                 if (value == null) {
655                     throw new InflateException("You must specifiy a layout in the"
656                             + " include tag: <include layout=\"@layout/layoutID\" />");
657                 } else {
658                     throw new InflateException("You must specifiy a valid layout "
659                             + "reference. The layout ID " + value + " is not valid.");
660                 }
661             } else {
662                 final XmlResourceParser childParser =
663                         getContext().getResources().getLayout(layout);
664 
665                 try {
666                     final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
667 
668                     while ((type = childParser.next()) != XmlPullParser.START_TAG &&
669                             type != XmlPullParser.END_DOCUMENT) {
670                         // Empty.
671                     }
672 
673                     if (type != XmlPullParser.START_TAG) {
674                         throw new InflateException(childParser.getPositionDescription() +
675                                 ": No start tag found!");
676                     }
677 
678                     final String childName = childParser.getName();
679 
680                     if (TAG_MERGE.equals(childName)) {
681                         // Inflate all children.
682                         rInflate(childParser, parent, childAttrs);
683                     } else {
684                         final View view = createViewFromTag(childName, childAttrs);
685                         final ViewGroup group = (ViewGroup) parent;
686 
687                         // We try to load the layout params set in the <include /> tag. If
688                         // they don't exist, we will rely on the layout params set in the
689                         // included XML file.
690                         // During a layoutparams generation, a runtime exception is thrown
691                         // if either layout_width or layout_height is missing. We catch
692                         // this exception and set localParams accordingly: true means we
693                         // successfully loaded layout params from the <include /> tag,
694                         // false means we need to rely on the included layout params.
695                         ViewGroup.LayoutParams params = null;
696                         try {
697                             params = group.generateLayoutParams(attrs);
698                         } catch (RuntimeException e) {
699                             params = group.generateLayoutParams(childAttrs);
700                         } finally {
701                             if (params != null) {
702                                 view.setLayoutParams(params);
703                             }
704                         }
705 
706                         // Inflate all children.
707                         rInflate(childParser, view, childAttrs);
708 
709                         // Attempt to override the included layout's android:id with the
710                         // one set on the <include /> tag itself.
711                         TypedArray a = mContext.obtainStyledAttributes(attrs,
712                             com.android.internal.R.styleable.View, 0, 0);
713                         int id = a.getResourceId(com.android.internal.R.styleable.View_id, View.NO_ID);
714                         // While we're at it, let's try to override android:visibility.
715                         int visibility = a.getInt(com.android.internal.R.styleable.View_visibility, -1);
716                         a.recycle();
717 
718                         if (id != View.NO_ID) {
719                             view.setId(id);
720                         }
721 
722                         switch (visibility) {
723                             case 0:
724                                 view.setVisibility(View.VISIBLE);
725                                 break;
726                             case 1:
727                                 view.setVisibility(View.INVISIBLE);
728                                 break;
729                             case 2:
730                                 view.setVisibility(View.GONE);
731                                 break;
732                         }
733 
734                         group.addView(view);
735                     }
736                 } finally {
737                     childParser.close();
738                 }
739             }
740         } else {
741             throw new InflateException("<include /> can only be used inside of a ViewGroup");
742         }
743 
744         final int currentDepth = parser.getDepth();
745         while (((type = parser.next()) != XmlPullParser.END_TAG ||
746                 parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
747             // Empty
748         }
749     }
750 }
751