We know that Phonewindow#setcontentview () is called in Activity#setcontentview (). and in Phonewindow#setcontentview () there is a sentence mLayoutInflater.inflate(layoutResID, mContentParent)
. The purpose of this line of code is to fill our activity_main.xml into the mcontentparent. See: Setcontentview source parsing. When writing adapter, also often write mInflater.inflate(layoutResID, null)
. So how does this line of code convert an XML file into a view or viewgroup?
There are two ways to get the Layoutinflater object:
LayoutInflater.from(Context context);
LayoutInflater LayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Actually these two are the same way, first look at the Layoutinflater#from ()
SOURCE Location: Frameworks/base/core/java/android/view/layoutinflater.java
Layoutinflater#from ()
publicstaticfrom(Context context) { LayoutInflater LayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); ifnull) { thrownew AssertionError("LayoutInflater not found."); } return LayoutInflater; }
The first way to get a Layoutinflater object is a simple encapsulation of the second way. It's actually the same thing. The implementation class of the context is Contextimpl, follow-up.
SOURCE Location: Frameworks/base/core/java/android/app/contextimpl.java
Contextimpl#getsystemservice ()
@Override publicgetSystemService(String name) { return SystemServiceRegistry.getSystemService(this, name); }
Follow.
SOURCE Location: Frameworks/base/core/java/android/app/systemserviceregistry.java
Systemserviceregistry#getsystemservice ()
publicstaticgetSystemService(ContextImpl ctx, String name) { ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name); returnnullnull; }
Get to fetcher directly from the global variable system_service_fetchers by name, followed by Fetcher Direct get to the Layoutinflater object. Capitalize on the b~ of the original Ah, in the systemserviceregistry there is a static code block, first look at this part.
Static{... registerservice (Context.layout_inflater_service, Layoutinflater.class,NewCachedservicefetcher<layoutinflater> () {@Override PublicLayoutinflaterCreateService(Contextimpl CTX) {return NewPhonelayoutinflater (Ctx.getoutercontext ()); }}); ... }Private Static<T>void Registerservice(String serviceName, class<t> serviceclass, servicefetcher<t> servicefetcher) {System_service_names.put (ServiceClass, serviceName); System_service_fetchers.put (ServiceName, Servicefetcher); }Static AbstractClass Cachedservicefetcher<t> implements Servicefetcher<t> {Private Final intMcacheindex; Public Cachedservicefetcher() {mcacheindex = sservicecachesize++; }@Override @SuppressWarnings("Unchecked") Public FinalTGetService(Contextimpl CTX) {Finalobject[] cache = Ctx.mservicecache;synchronized(cache) {//Fetch or create the service.Object service = Cache[mcacheindex];if(Service = =NULL) {service = CreateService (CTX); Cache[mcacheindex] = Service; }return(T) Service; } } Public AbstractTCreateService(Contextimpl CTX); }
There are two successive methods and an abstract inner class cachedservicefetcher. Since the phonelayoutinflater is returned in the concrete implementation of the abstract method Cachedservicefetcher#createservice (), the object used in the following article is always phonelayoutinflater. After getting the Layoutinflater object (which is actually its subclass Phonelayoutinflater object), call Layoutinflater#inflate (). Follow.
SOURCE Location: Frameworks/base/core/java/android/view/layoutinflater.java
Layoutinflater#inflate ()
publicinflateint resource, @Nullable ViewGroup root) { returnnull); }
Here is an example of Setcontentview, which in mLayoutInflater.inflate(layoutResID, mContentParent)
passing will also explain mInflater.inflate(layoutResID,null)
the situation in adapter. That is root
, the parameters are null
and not null
two cases. , the third parameter is root==null
false
. root!=null
, and the third argument is true
. Follow.
Layoutinflater#inflate ()
publicinflateintboolean attachToRoot) { final Resources res = getContext().getResources(); final XmlResourceParser parser = res.getLayout(resource); try { return inflate(parser, root, attachToRoot); finally { parser.close(); } }
Follow.
Layoutinflater#inflate ()
Public View Inflate (xmlpullparser parser, @Nullable ViewGroup root, Boolean attachtoroot) {synchronized (Mconstruc Torargs) {...View result = root;Try{...Gets the string of the root node, such as LinearLayout final string name = Parser.getname (); Root node Merge startif(tag_merge.equals (name)) {...}Else{//Create root view, view Final View temp = Createviewfromtag (root, Name, Inflatercontext, attr s); Viewgroup.layoutparams params = null;if(root = null) {//get layoutparams params = Root.generatelayoutparams (attrs);if(!attachtoroot) {//apply Layoutparams to root node view temp.setlayoutparams (params); }}//Traverse parse child view and add to root node temp in Rinflatechildren (pars Er, temp, attrs, true); Root is not empty, add root view directly to rootif(root = null && attachtoroot) {Root.addview (temp, params); }//root equals null, return directly to root node tempif(Root = null | |!attachtoroot) {result = temp; }}}catch (Exception e) {...}returnResult } }
There are comments on each of the above steps, and the following highlights the Createviewfromtag () and the Rinflatechildren () method that generates the view of the root node and iterates through the generated sub-view.
Layoutinflater#createviewfromtag ()
private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) { return createViewFromTag(parent, name, context, attrs, false); } View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, boolean ignoreThemeAttr) { ... if (-1 == name.indexOf(‘.‘)) { view = onCreateView(parent, name, attrs); else { view = createView(name, null, attrs); } ... return view; }
Follow.
Layoutinflater#createview ()
Public final View CreateView (string name, string prefix, AttributeSet attrs) throws ClassNotFoundException, INF lateexception {constructor<? extends view> Constructor = sconstructormap.get (name); class<? Extends view> clazz = null;Try{if(constructor = = null) {clazz = Mcontext.getclassloader (). loadclass (prefix! = null?) (prefix + name): name). Assubclass (View.class);...constructor = Clazz.getconstructor (mconstructorsignature); Constructor.setaccessible (TRUE); Sconstructormap.put (name, constructor); }Else{...Final View view = Constructor.newinstance (args);returnView } catch (Exception e) {...} }
Sconstructormap is an HashMap<String, Constructor<? extends View>>
object. The first is based on the name of the root node, for example, LinearLayout
to find the cached constructor, and if it is executed for the first time, it is definitely returned null
. If returned as null
, the construction method is reflected by reflection, and the setting is forced to be accessible and then saved into the Sconstructormap. If there is a constructor in the cache, take it out directly. The last call newInstance
reflects the root node view instance. After the root node view instance is obtained, the properties are set, and finally the Rinflatechildren () traversal is called to create the child view. Follow.
Layoutinflater#rinflatechildren ()
finalvoid rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs, booleanthrows XmlPullParserException, IOException { rInflate(parser, parent, parent.getContext(), attrs, finishInflate); }
parent
The parameter is the root node view. Here is simply a transfer to the Rinflate () method processing. Follow.
Layoutinflater#rinflatechildren ()
void Rinflate (Xmlpullparser parser, View Parent, context context, AttributeSet Attrs, boolean finishinflate) th Rows Xmlpullparserexception, IOException {final int depth = parser.getdepth (); int type; while((type = Parser.next ())! = Xmlpullparser.end_tag | | Parser.getdepth () > Depth) && type! = xmlpullparser.end_document) {if(Type! = Xmlpullparser.start_tag) {continue; } final String name = Parser.getname ();if(tag_request_focus.equals (name)) {...}...}Else{Final View view = Createviewfromtag (parent, name, context, attrs); Final ViewGroup ViewGroup = (viewgroup) parent; Final Viewgroup.layoutparams params = Viewgroup.generatelayoutparams (attrs); Rinflatechildren (parser, view, attrs, true); Viewgroup.addview (view, params); } }if(finishinflate) {parent.onfinishinflate (); } }
Traversal is the While
name of the child node view, such as Textview,relativelayout, which is reflected on the loop. Several sub-nodes that start with tag, include, and so on, take the logic of the top few if, and our focus is on the else logic of the ordinary view walk. Can see: First, and create the root node view call the same method Createviewfromtag () Create a child view, and then set the parameters of the child view, and then call the recursive call Rinflatechildren () method to measure the child node all view, Finally, the child node is added to the parent layout, which may be the root node or a child node. After the traversal is complete, all child view is added to the layout and the corresponding layout parameters are set.
At this point, Layoutinflater.from (). Inflate () source parsing end ~
More framework source code Analysis, please visit the framework Source code parsing series [catalog]
Android Layoutinflater.from (). Inflate () source parsing