@Override publicvoidsetContentView(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(); } elseif (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); }
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext()); transitionTo(newScene); } else { // 将View添加到DecorView的mContentParent中 // 调用LayoutInflater的inflate方法解析布局文件,并生成View树,mContentParent为View树的根节点 mLayoutInflater.inflate(layoutResID, mContentParent); } mContentParent.requestApplyInsets(); //回调Activity的onContentChanged方法通知视图发生改变 final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } mContentParentExplicitlySet = true; }
/** * Instantiates a layout XML file into its corresponding {@link android.view.View} * objects. It is never used directly. Instead, use * {@link android.app.Activity#getLayoutInflater()} or * {@link Context#getSystemService} to retrieve a standard LayoutInflater instance * that is already hooked up to the current context and correctly configured * for the device you are running on. * * <p> * To create a new LayoutInflater with an additional {@link Factory} for your * own views, you can use {@link #cloneInContext} to clone an existing * ViewFactory, and then call {@link #setFactory} on it to include your * Factory. * * <p> * For performance reasons, view inflation relies heavily on pre-processing of * XML files that is done at build time. Therefore, it is not currently possible * to use LayoutInflater with an XmlPullParser over a plain XML file at runtime; * it only works with an XmlPullParser returned from a compiled resource * (R.<em>something</em> file.) */ @SystemService(Context.LAYOUT_INFLATER_SERVICE) publicabstractclassLayoutInflater{···}
static { registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class, new CachedServiceFetcher<AccessibilityManager>() { @Override public AccessibilityManager createService(ContextImpl ctx){ return AccessibilityManager.getInstance(ctx); }});
registerService(Context.CAPTIONING_SERVICE, CaptioningManager.class, new CachedServiceFetcher<CaptioningManager>() { @Override public CaptioningManager createService(ContextImpl ctx){ returnnew CaptioningManager(ctx); }}); ··· }
/** * Statically registers a system service with the context. * This method must be called during static initialization only. */ privatestatic <T> voidregisterService(String serviceName, Class<T> serviceClass, ServiceFetcher<T> serviceFetcher){ SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName); SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher); }
final Context inflaterContext = mContext; final AttributeSet attrs = Xml.asAttributeSet(parser); Context lastContext = (Context) mConstructorArgs[0]; mConstructorArgs[0] = inflaterContext; View result = root;
try { // Look for the root node. int type; while ((type = parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) { // Empty }
final String name = parser.getName();
···
//第一部分merge标签
//内部静态常量private static final String TAG_MERGE = "merge";
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 params for 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);
···
// We are supposed to attach all the views we found (int temp)
// to root. Do that now.
if (root != null && attachToRoot) {
root.addView(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;
}
}
} catch (XmlPullParserException e) {
···
} finally {
// Don't retain static reference on context.
mConstructorArgs[0] = lastContext;
mConstructorArgs[1] = null;
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
return result;
}
}
if (parent instanceof ViewGroup) { //private static final int[] ATTRS_THEME = new int[] {com.android.internal.R.attr.theme };
final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME); finalint themeResId = ta.getResourceId(0, 0); finalboolean hasThemeOverride = themeResId != 0; if (hasThemeOverride) { context = new ContextThemeWrapper(context, themeResId); } ta.recycle();
int layout = attrs.getAttributeResourceValue(null, ATTR_LAYOUT, 0); if (layout == 0) { final String value = attrs.getAttributeValue(null, ATTR_LAYOUT); if (value == null || value.length() <= 0) { thrownew InflateException("You must specify a layout in the" + " include tag: <include layout=\"@layout/layoutID\" />"); }
// Attempt to resolve the "?attr/name" string to an attribute // within the default (e.g. application) package. layout = context.getResources().getIdentifier( value.substring(1), "attr", context.getPackageName());
}
// The layout might be referencing a theme attribute. if (mTempValue == null) { mTempValue = new TypedValue(); } if (layout != 0 && context.getTheme().resolveAttribute(layout, mTempValue, true)) { layout = mTempValue.resourceId; }
if (layout == 0) { final String value = attrs.getAttributeValue(null, ATTR_LAYOUT); thrownew InflateException("You must specify a valid layout " + "reference. The layout ID " + value + " is not valid."); } else { final XmlResourceParser childParser = context.getResources().getLayout(layout);
try { final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
while ((type = childParser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) { // Empty. }
if (type != XmlPullParser.START_TAG) { thrownew InflateException(childParser.getPositionDescription() + ": No start tag found!"); }
final String childName = childParser.getName();
if (TAG_MERGE.equals(childName)) { rInflate(childParser, parent, context, childAttrs, false); } else { final View view = createViewFromTag(parent, childName, context, childAttrs, hasThemeOverride); final ViewGroup group = (ViewGroup) parent;
final TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.Include); finalint id = a.getResourceId(R.styleable.Include_id, View.NO_ID); finalint visibility = a.getInt(R.styleable.Include_visibility, -1); a.recycle();
ViewGroup.LayoutParams params = null; try { params = group.generateLayoutParams(attrs); } catch (RuntimeException e) { // Ignore, just fail over to child attrs. } if (params == null) { params = group.generateLayoutParams(childAttrs); } view.setLayoutParams(params);
// Inflate all children. rInflateChildren(childParser, view, childAttrs, true);
group.addView(view); } } finally { childParser.close(); } } } else { thrownew InflateException("<include /> can only be used inside of a ViewGroup"); }
··· // 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 params for 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); ··· // We are supposed to attach all the views we found (int temp) // to root. Do that now. if (root != null && attachToRoot) { root.addView(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; } ···
if (constructor == null) { // Class not found in the cache, see if it's real, and try to add it clazz = mContext.getClassLoader().loadClass( prefix != null ? (prefix + name) : name).asSubclass(View.class);
Object lastContext = mConstructorArgs[0]; if (mConstructorArgs[0] == null) { // Fill in the context if not already within inflation. mConstructorArgs[0] = mContext; } Object[] args = mConstructorArgs; args[1] = attrs;
//注释1 final View view = constructor.newInstance(args); if (view instanceof ViewStub) { // Use the same context when inflating ViewStub later. final ViewStub viewStub = (ViewStub) view; viewStub.setLayoutInflater(cloneInContext((Context) args[0])); } mConstructorArgs[0] = lastContext; return view;
/** Override onCreateView to instantiate names that correspond to the widgets known to the Widget factory. If we don't find a match, call through to our super class. */ @Overrideprotected 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. } }
try { Trace.traceBegin(Trace.TRACE_TAG_VIEW, name); //没有缓存的构造器 if (constructor == null) { // Class not found in the cache, see if it's real, and try to add it //通过传入的prefix构造出完整的类名 并加载该类 clazz = mContext.getClassLoader().loadClass( prefix != null ? (prefix + name) : name).asSubclass(View.class);
//代码省略 //通过反射创建View final View view = constructor.newInstance(args); if (view instanceof ViewStub) { // Use the same context when inflating ViewStub later. final ViewStub viewStub = (ViewStub) view; viewStub.setLayoutInflater(cloneInContext((Context) args[0])); } mConstructorArgs[0] = lastContext; return view;