Android layout file Xml
By converting setContentView(@LayoutResint layoutResID) or (context).inflate(int ResID) into Java objects, the preview function provided by the development tool Android Studio can be developed in parallel during the development process, improving development efficiency. The following analysis process is based on the Android API 25 Platform source code and uses the setContentView() method as the entry.
How to convert Xml to Java object
1. The setContentView(@LayoutResint layoutResID) method in the Activity;
This method will be overloaded by each inherited subclass;
2、(Context context).inflate(@LayoutResint resource, ...)。
Generally use Activity
- 1). .
- 2). .
- 3).
- 4). Other Activity
Start with the setContentView() method in Activity
public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar(); }
Tracking getWindow() source code
public Window getWindow() { return mWindow; }
mWindow is initialized in the attach() method
final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, String referrer, IVoiceInteractor voiceInteractor, Window window) { ... mWindow = new PhoneWindow(this, window); ... }
So the implementation class is a class,@hide
representPhoneWindow
The source code is hidden in sdk, check(layoutResID)
as follows:
@Override public void setContentView(int layoutResID) { // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window // decor, when theme attributes and the like are crystalized. Do not check the feature // before this happens. if (mContentParent == null) { installDecor(); } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { (); } if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { final Scene newScene = (mContentParent, layoutResID, getContext()); transitionTo(newScene); } else { (layoutResID, mContentParent); } (); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { (); } mContentParentExplicitlySet = true; }
From the above code, we can find that if there is no transition animation, the execution is
(layoutResID, mContentParent);
In the PhoneWindow constructor, I found the mLayoutInflater object assignment code as follows:
public PhoneWindow(Context context) { super(context); mLayoutInflater = (context); }
So we can draw a conclusion (resId) and finally use (context).inflate(resId, ...).
Check out other activities.
and.
Discover.
No reload(resId)
but.
Reloaded
@Override public void setContentView(@LayoutRes int layoutResID) { getDelegate().setContentView(layoutResID); }
The getDelegate() source code will eventually be called..(resId)
@Override public void setContentView(int resId) { ensureSubDecor(); ViewGroup contentParent = (ViewGroup) (); (); (mContext).inflate(resId, contentParent); (); }
Therefore, converting xml into Java objects is throughLayoutInflater
ofinflate()
Method to complete
LayoutInflater object acquisition method
Keywordsabstract
,LayoutInflater
is an abstract class that cannot be instantiated.LayoutInflater
The methods for obtaining objects are:
1). Get it in Activity via getLayoutInflater()
2). Get the static method from(context) in LayoutInflater
3). (Context.LAYOUT_INFLATER_SERVICE) Get
As in ActivitygetLayoutInflater()
/** * Convenience for calling * {@link #getLayoutInflater}. */ @NonNull public LayoutInflater getLayoutInflater() { return getWindow().getLayoutInflater(); }
It can be seen that the Activity gets the mLayoutInflater of PhoneWindow through getLayoutInflater().
(context)
/** * Obtains the LayoutInflater from the given context. */ public static LayoutInflater from(Context context) { LayoutInflater LayoutInflater = (LayoutInflater) (Context.LAYOUT_INFLATER_SERVICE); if (LayoutInflater == null) { throw new AssertionError("LayoutInflater not found."); } return LayoutInflater; }
Therefore, the LayoutInflater object is obtained through the service.
Track the source code (Context.LAYOUT_INFLATER_SERVICE);Context
The implementation class is,like:
@Override public Object getSystemService(String name) { return (this, name); }
track
/** * Gets a system service from a given context. */ public static Object getSystemService(ContextImpl ctx, String name) { ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name); return fetcher != null ? (ctx) : null; } /** * Statically registers a system service with the context. * This method must be called during static initialization only. */ 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); }
In the SystemServiceRegistry class, only various system services are registered here. The registration code place is found through Context.LAYOUT_INFLATER_SERVICE, as follows:
registerService(Context.LAYOUT_INFLATER_SERVICE, , new CachedServiceFetcher<LayoutInflater>() { @Override public LayoutInflater createService(ContextImpl ctx) { return new PhoneLayoutInflater(()); }});
Through the above code, I foundLayoutInflater
The implementation class isPhoneLayoutInflater
LayoutInflater Reads the Xml file and creates a View object, and continues to track the() method
1).View inflate(@LayoutRes int resource, @Nullable ViewGroup root)
2).View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)
Focus on the second method, the code is as follows:
/** * Inflate a new view hierarchy from the specified xml resource. Throws * {@link InflateException} if there is an error. * * @param resource ID for an XML layout resource to load (., * <code>.main_page</code>) * @param root Optional view to be the parent of the generated hierarchy (if * <em>attachToRoot</em> is true), or else simply an object that * provides a set of LayoutParams values for root of the returned * hierarchy (if <em>attachToRoot</em> is false.) * @param attachToRoot Whether the inflated hierarchy should be attached to * the root parameter? If false, root is only used to create the * correct subclass of LayoutParams for the root view in the XML. * @return The root View of the inflated hierarchy. If root was supplied and * attachToRoot is true, this is root; otherwise it is the root of * the inflated XML file. */ public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) { final Resources res = getContext().getResources(); if (DEBUG) { (TAG, "INFLATING from resource: \"" + (resource) + "\" (" + (resource) + ")"); } final XmlResourceParser parser = (resource); try { return inflate(parser, root, attachToRoot); } finally { (); } }
According to the above code logic, first passresource
The object converts the xml file pointed to by resId intoXmlResourceParser
, and then executeinflate(parser, root, attachToRoot)
The core code is as follows:
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) { synchronized (mConstructorArgs) { (Trace.TRACE_TAG_VIEW, "inflate"); final Context inflaterContext = mContext; final AttributeSet attrs = (parser); Context lastContext = (Context) mConstructorArgs[0]; mConstructorArgs[0] = inflaterContext; View result = root; try { // Look for the root node. ... final String name = (); //Analysis 1 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"); } //Analysis 2 rInflate(parser, root, inflaterContext, attrs, false); } else { //Analysis 3 // Temp is the root view that was found in the xml final View temp = createViewFromTag(root, name, inflaterContext, attrs); params = null; if (root != null) { // Create layout params that match root, if supplied params = (attrs); if (!attachToRoot) { // Set the layout params for temp if we are not // attaching. (If we are, we use addView, below) (params); } } //Analysis 4 // Inflate all children under temp against its context. rInflateChildren(parser, temp, attrs, true); // We are supposed to attach all the views we found (int temp) // to root. Do that now. if (root != null && attachToRoot) { (temp, params); } // Decide whether to return the root that was passed in or the // top view found in xml. if (root == null || !attachToRoot) { result = temp; } } //Exception handling part return result; } }
analyze
Analysis 1:
If the Xml root tag isTAG_MERGE (i.e. merge)
, then root cannot be empty, attachToRoot is true, and it is executedrInflate(parser, root, inflaterContext, attrs, false)
Analysis 2 rInflate() method
void rInflate(XmlPullParser parser, View parent, Context context, AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException { final int depth = (); int type; while (((type = ()) != XmlPullParser.END_TAG || () > depth) && type != XmlPullParser.END_DOCUMENT) { if (type != XmlPullParser.START_TAG) { continue; } final String name = (); if (TAG_REQUEST_FOCUS.equals(name)) { 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); (view, params); } } if (finishInflate) { (); } }
rInflate(parser, root, inflaterContext, attrs, false) is summarized as follows
1). While traversing the child nodes of the node
2). The child nodes include "requestFocus", "tag", "", "include"
3). The child node cannot be "merge"
4). Other situations of child nodes are the tags of various Views
5). View tags and "include" tags will create View objects
6). Execute after the traversal is completed ()
If the child node is include, the source code of parseInclude() is similar to inflate(parser, root, attachToRoot), both read the file corresponding to xml, convert it into XmlResourceParser and then traverse the tags in it.
createViewFromTag(parent, name, context, attrs)
Responsible for creating View objects
Analysis 3, 4
1). Only if root is not null, the params attributes of xml and layout will be read;
2). attachToRoot is True, and the return is a root object. Otherwise, the view specified by the root tag created by xml is returned.
3). The createViewFromTag(root, name, inflaterContext, attrs) method is called to create View
4). rInflateChildren()->rInflate(); is the same as Analysis 2
In summary,()
Create a View object, the source code is as follows:
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, boolean ignoreThemeAttr) { if (("view")) { name = (null, "class"); } // Apply a theme wrapper, if allowed and one is specified. if (!ignoreThemeAttr) { final TypedArray ta = (attrs, ATTRS_THEME); final int themeResId = (0, 0); if (themeResId != 0) { context = new ContextThemeWrapper(context, themeResId); } (); } if ((TAG_1995)) { // Let's party like it's 1995! return new BlinkLayout(context, attrs); } try { View view; if (mFactory2 != null) { view = (parent, name, context, attrs); } else if (mFactory != null) { view = (name, context, attrs); } else { view = null; } if (view == null && mPrivateFactory != null) { view = (parent, name, context, attrs); } if (view == null) { final Object lastContext = mConstructorArgs[0]; mConstructorArgs[0] = context; try { if (-1 == ('.')) { view = onCreateView(parent, name, attrs); } else { view = createView(name, null, attrs); } } finally { mConstructorArgs[0] = lastContext; } } return view; //Exception handling ... }
mFactory2
、mFactory
、mPrivateFactory
It seems that all three objects can be created. For these three objects are null or empty implementations, just look at the following code to create a View object:
final Object lastContext = mConstructorArgs[0]; mConstructorArgs[0] = context; try { if (-1 == ('.')) { view = onCreateView(parent, name, attrs); } else { //Customize view = createView(name, null, attrs); } } finally { mConstructorArgs[0] = lastContext; }
Note: Ifname
The attribute contains.
It means that this is a custom View, and the system comes with a View. We can omit the path to the class, while the custom View cannot be omitted.
Custom View creation, the core code is as follows:
public final View createView(String name, String prefix, AttributeSet attrs) throws ClassNotFoundException, InflateException { Constructor<? extends View> constructor = (name); if (constructor != null && !verifyClassLoader(constructor)) { constructor = null; (name); } Class<? extends View> clazz = null; try { (Trace.TRACE_TAG_VIEW, name); if (constructor == null) { // Class not found in the cache, see if it's real, and try to add it 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) { // 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); } } } Object[] args = mConstructorArgs; args[1] = attrs; final View view = (args); if (view instanceof ViewStub) { // Use the same context when inflating ViewStub later. final ViewStub viewStub = (ViewStub) view; (cloneInContext((Context) args[0])); } return view; //Exception handling ...... }
You can see that the above code(args)
, create View objects through reflection
For the various built-in Views in LayoutInflater implementation classes for AndroidPhoneLayoutInflater
ReloadedonCreateView()
method
private static final String[] sClassPrefixList = { ".", ".", "." }; @Override protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException { for (String prefix : sClassPrefixList) { try { View view = createView(name, prefix, attrs); if (view != null) { return view; } } catch (ClassNotFoundException e) { // In this case we want to let the base class take a crack // at it. } } return (name, attrs); }
The code in LayoutInflater is as follows:
protected View onCreateView(View parent, String name, AttributeSet attrs) throws ClassNotFoundException { return onCreateView(name, attrs); } protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException { return createView(name, ".", attrs); }
For the built-in Views of the system, the View will be added in front of the labels in turn.
,.
,.
,.
Then create a View through reflection.
The above is the detailed explanation of the process of converting Android Xml to View. For more information about converting Android Xml to View, please follow my other related articles!