Preface
Activity needs to load the contents of the XML layout file in the interface creation. Just as we need to load the layout of the Item in ListView or RecyclerView, we use LayoutInflater to operate.
There are several ways to get LayoutInflater instances, but ultimately through(LayoutInflater)(Context.LAYOUT_INFLATER_SERVICE)
What you get is that the LayoutInflater that loads the layout comes from the system service.
Since the content part of the Android system source code adopts the decorative mode, the specific functions of Context are implemented by ContextImpl. By finding the getSystemService code in ContextImpl and following up all the way, we learned that the last returned instance is PhoneLayoutInflater.
registerService(Context.LAYOUT_INFLATER_SERVICE, , new CachedServiceFetcher<LayoutInflater>() { @Override public LayoutInflater createService(ContextImpl ctx) { return new PhoneLayoutInflater(()); }});
LayoutInflater is just an abstract class, while PhoneLayoutInflater is the concrete implementation class.
inflate method loads View
The common method when using LayoutInflater is the inflate method, which passes in a layout file ID and finally parses it into a View.
The inflate method of LayoutInflater loading layout also has many overload forms:
View inflate(@LayoutRes int resource, @Nullable ViewGroup root) View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)
The difference between the two is whether the resource layout file needs to be loaded into the root layout.
However, there is something to note. If root is null, the properties set for resource in the xml layout will be invalid, just a simple loading layout.
// temp is the top view in the xml layout final View temp = createViewFromTag(root, name, inflaterContext, attrs); params = null; if (root != null) { // root // LayoutParams will be generated if root is not null params = (attrs); if (!attachToRoot) { // If not added to root, set the layout parameters to temp (params); } } // Loading child View rInflateChildren(parser, temp, attrs, true); if (root != null && attachToRoot) { (temp, params);//Add to the layout, the layout parameters will be used in the addView } if (root == null || !attachToRoot) { result = temp; }
Follow up on the createViewFromTag method to see how the View is created.
View view; // The View to be returned at the end if (mFactory2 != null) { view = (parent, name, context, attrs); // Whether Factory2 is set } else if (mFactory != null) { view = (name, context, attrs); // Whether Factory is set } else { view = null; } if (view == null && mPrivateFactory != null) { // Whether PrivateFactory is set view = (parent, name, context, attrs); } if (view == null) { // If the Factory has not been set, the View is finally generated final Object lastContext = mConstructorArgs[0]; mConstructorArgs[0] = context; try { if (-1 == ('.')) { // System controls view = onCreateView(parent, name, attrs); } else { // Non-system control, custom View view = createView(name, null, attrs); } } finally { mConstructorArgs[0] = lastContext; } }
If the Factory interface is set, the View will be generated by the onCreateView method in the Factory.
aboutThe function is to create a View by yourself when loading the layout, and before the system creates the View.
aboutThe most common usage scenarios are the skin replacing of the application now.
If the Factory interface has not been set, it is to determine whether it is a custom control or a system control. Whether it is the onCreateView method or the createView method, the createView method is finally called internally to generate a View through it.
// The parameters that generate View through reflection are the Context and AttributeSet classes respectivelystatic final Class<?>[] mConstructorSignature = new Class[] { , }; public final View createView(String name, String prefix, AttributeSet attrs) throws ClassNotFoundException, InflateException { Constructor<? extends View> constructor = (name); Class<? extends View> clazz = null; if (constructor == null) { // Get the constructor of View from the cache, and call getConstructor if it does not. clazz = ().loadClass( prefix != null ? (prefix + name) : name).asSubclass(); if (mFilter != null && clazz != null) { boolean allowed = (clazz); if (!allowed) { failNotAllowed(name, prefix, attrs); } } constructor = (mConstructorSignature); (true); (name, constructor); } else { // If we have a filter, apply it to cached constructor if (mFilter != null) { // Filter, whether to allow the generation of the View // Have we seen this name before? Boolean allowedState = (name); if (allowedState == null) { // New class -- remember whether it is allowed clazz = ().loadClass( prefix != null ? (prefix + name) : name).asSubclass(); boolean allowed = clazz != null && (clazz); (name, allowed); if (!allowed) { failNotAllowed(name, prefix, attrs); } } else if (()) { failNotAllowed(name, prefix, attrs); // This View is not allowed to be generated } } } Object[] args = mConstructorArgs; args[1] = attrs; final View view = (args); // Generate View through reflection return view;
In the createView method, first look for whether there is a corresponding cache from the view constructor cache. If not, a constructor will be generated and placed in the cache. If there is a constructor, it will see if it can pass the filtering and whether the View is allowed to be generated.
In the end, the View is generated through the constructor reflection of the View.
Used when generating ViewThe constructor is called, and the variables required for the parameters are defined by the mConstructorSignature variable, namely Context and AttributeSet. As you can see, the corresponding parameters are also passed in when the View is generated at the end.
useThe form of reflection generates a View, which is for decoupling, and you only need to have a class name to load it.
It can be seen that the LayoutInflater loading layout still needs to pass a Context, not only to get the LayoutInflater, but also used when reflecting and generating View.
Deep traversal loading layout
If there is only one control for the layout that needs to be loaded, then the work of returning to that View is over when LayoutInflater returns.
If there are multiple Views that need to be loaded in the layout file, the View under the top-level View will continue to be loaded through the rInflateChildren method, and finally loaded through the rInflate method.
void rInflate(XmlPullParser parser, View parent, Context context, AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException { final int depth = (); int type; // If the while condition does not hold, the loading is finished while (((type = ()) != XmlPullParser.END_TAG || () > depth) && type != XmlPullParser.END_DOCUMENT) { if (type != XmlPullParser.START_TAG) { continue; } final String name = (); // Get name from XmlPullParser to parse if (TAG_REQUEST_FOCUS.equals(name)) { // name Analysis in various situations parseRequestFocus(parser, parent); } else if (TAG_TAG.equals(name)) { parseViewTag(parser, parent, attrs); } else if (TAG_INCLUDE.equals(name)) { if (() == 0) { throw new InflateException("<include /> cannot be the root element"); } parseInclude(parser, context, parent, attrs); } else if (TAG_MERGE.equals(name)) { throw new InflateException("<merge /> must be the root element"); } else { final View view = createViewFromTag(parent, name, context, attrs); final ViewGroup viewGroup = (ViewGroup) parent; final params = (attrs); rInflateChildren(parser, view, attrs, true); // Continue to traverse (view, params); // Top View Add child View } } if (finishInflate) { // traversal analysis (); } }
The rInflate method first determines whether the parsing has ended. If not, the next View is loaded from the XmlPullParser for processing. Different types will also be processed in the middle, such as TAG_REQUEST_FOCUS, TAG_TAG, TAG_INCLUDE, TAG_MERGE, etc.
In the end, we still generate View through createViewFromTag, and use the generated View as the parent node, start deep traversal, continue to call the rInflateChildren method to load the layout, and add this View to its parent View.
As for why the method name of the createViewFromTag literally comes from the Tag tag, it must be related to the content generated by the XmlPullParser parsing layout.
Summarize
The above is the entire content of this article. I hope the content of this article will be of some help to all Android developers. If you have any questions, you can leave a message to communicate. Thank you for your support.