LayoutInflater

We often use LayoutInflaters to render layout files into View hierarchies. How do we do that? There are currently four ways:

  • context.getSystemService()
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);   
View rootView = inflater.inflate(R.layout.view_layout, null); 
Copy the code
  • LayoutInflater.from(context)
LayoutInflater inflater = LayoutInflater.from(context);  
View rootView = inflater.inflate(R.layout.view_layout, null); 
Copy the code
  • Activity downplays getLayoutInflater()
LayoutInflater inflater = getLayoutInflater();  
View rootView = inflater.inflate(R.layout.view_layout, null); 
Copy the code
  • View’s static method, view.inflate ()
rootView = View.inflate(context, R.layout.view_layout, null);  
Copy the code

As you can see from the source code, the second, third, and fourth methods are simply overwraps of the first, and the final View instance is obtained through the inflate() method of the Inflater instance.

Let’s look again at the setContentView method in the Activity’s onCreate() method:

public void setContentView(int layoutResID) { ... omitif (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else{ mLayoutInflater.inflate(layoutResID, mContentParent); }... Omit}Copy the code

You can see that the inflate() method is also used inflater. So we can conclude that either we actively render the View by calling the Inflater’s inflate() method ourselves or the Activity renders the View by setContentView, we do so through the Inflater’s inflate() method.

We continue tracing the inflate() method and see that the inflate(XmlPullParser,ViewGroup, Boolean attachToRoot) method is eventually called:

public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
        synchronized (mConstructorArgs) {
            ...省略
            try {
                ...省略
                final String name = parser.getName();              
      
                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, inflaterContext, attrs, false);
                } else {
                    // Temp is the root view that was found in the xml
                    final View temp = createViewFromTag(root, name, inflaterContext, 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 paramsfor temp if we are not
                            // attaching. (If we are, we use addView, below)
                            temp.setLayoutParams(params);
                        }
                    }                    
                    // Inflate all children under temp against its context.
                    rInflateChildren(parser, temp, attrs, true); . Omit}} catch (XmlPullParserException e) {.. Ellipsis} catch (Exception e) {... Ellipsis} finally {... Omit}returnresult; }}Copy the code

We omit some of the code and start with the try block, ignoring the merge tag (which is just a special case of normal rendering). We go to the else statement, where the key createViewFromTag() generates the View.

Let’s go ahead and see what createViewFromTag does:

View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
            boolean ignoreThemeAttr) {
        if (name.equals("view")) {
            name = attrs.getAttributeValue(null, "class"); }... Omit try {View View;if(mFactory2 ! = null) { view = mFactory2.onCreateView(parent, name, context, attrs); }else if(mFactory ! = null) { view = mFactory.onCreateView(name, context, attrs); }else {
                view = null;
            }

            if(view == null && mPrivateFactory ! = null) { view = mPrivateFactory.onCreateView(parent, name, context, attrs); }if (view == null) {
                final Object lastContext = mConstructorArgs[0];
                mConstructorArgs[0] = context;
                try {
                    if (-1 == name.indexOf('. ')) {
                        view = onCreateView(parent, name, attrs);
                    } else{ view = createView(name, null, attrs); } } finally { mConstructorArgs[0] = lastContext; }}returnview; } catch (InflateException e) { throw e; } catch (ClassNotFoundException e) { ... Ellipsis} catch (Exception e) {... Omit}}Copy the code

When mFactory2 or mFactory is not null, the View is generated by their onCreateView method. Otherwise, the default process for creating a View is used. The default process for creating a View is to determine if the tag name contains a “.”, add the prefix “Android.view.” to the front of the tag name if it doesn’t, call LayoutInflater’s createView() method, and return the View. Here’s how layoutInflaters render views from layout files: The XmlResourceParser object is created using the resource ID of the layout file. The object is then used to recursively parse the layout file, generate a View based on the parsed tag name, and finally return the hierarchical View. If LayoutInflater sets Factory2 or Factory, then the View is created by calling the Factory2 or Factory onCreateView method. So we can do some logic in onCreateView before the View is created, like skin.

LayoutInflater’s Factory interface

To be continued…