View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, boolean ignoreThemeAttr) { if (name.equals("view")) { name = attrs.getAttributeValue(null, "class"); }
// Apply a theme wrapper, if allowed and one is specified. if (!ignoreThemeAttr) { final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME); final int themeResId = ta.getResourceId(0, 0); if (themeResId != 0) { context = new ContextThemeWrapper(context, themeResId); } ta.recycle(); }
if (name.equals(TAG_1995)) { // Let's party like it's 1995! returnnew BlinkLayout(context, attrs); }
final View createView(View parent, final String name, @NonNull Context context, @NonNull AttributeSet attrs, boolean inheritContext, boolean readAndroidTheme, boolean readAppTheme, boolean wrapContext){ final Context originalContext = context;
// We can emulate Lollipop's android:theme attribute propagating down the view hierarchy // by using the parent's context if (inheritContext && parent != null) { context = parent.getContext(); } if (readAndroidTheme || readAppTheme) { // We then apply the theme on the context, if specified context = themifyContext(context, attrs, readAndroidTheme, readAppTheme); } if (wrapContext) { context = TintContextWrapper.wrap(context); }
View view = null;
// We need to 'inject' our tint aware Views in place of the standard framework versions switch (name) { case"TextView": view = createTextView(context, attrs); verifyNotNull(view, name); break; case"ImageView": view = createImageView(context, attrs); verifyNotNull(view, name); break; case"Button": view = createButton(context, attrs); verifyNotNull(view, name); break; case"EditText": view = createEditText(context, attrs); verifyNotNull(view, name); break; case"Spinner": view = createSpinner(context, attrs); verifyNotNull(view, name); break; case"ImageButton": view = createImageButton(context, attrs); verifyNotNull(view, name); break; case"CheckBox": view = createCheckBox(context, attrs); verifyNotNull(view, name); break; case"RadioButton": view = createRadioButton(context, attrs); verifyNotNull(view, name); break; case"CheckedTextView": view = createCheckedTextView(context, attrs); verifyNotNull(view, name); break; case"AutoCompleteTextView": view = createAutoCompleteTextView(context, attrs); verifyNotNull(view, name); break; case"MultiAutoCompleteTextView": view = createMultiAutoCompleteTextView(context, attrs); verifyNotNull(view, name); break; case"RatingBar": view = createRatingBar(context, attrs); verifyNotNull(view, name); break; case"SeekBar": view = createSeekBar(context, attrs); verifyNotNull(view, name); break; default: // The fallback that allows extending class to take over view inflation // for other tags. Note that we don't check that the result is not-null. // That allows the custom inflater path to fall back on the default one // later in this method. view = createView(context, name, attrs); }
if (view == null && originalContext != context) { // If the original context does not equal our themed context, then we need to manually // inflate it using the name so that android:theme takes effect. view = createViewFromTag(context, name, attrs); }
if (view != null) { // If we have created a view, check its android:onClick checkOnClickListener(view, attrs); }
if (view == null && originalContext != context) { // If the original context does not equal our themed context, then we need to manually // inflate it using the name so that android:theme takes effect. view = createViewFromTag(context, name, attrs); }
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);
if (mFilter != null && clazz != null) { boolean allowed = mFilter.onLoadClass(clazz); if (!allowed) { failNotAllowed(name, prefix, attrs); } } constructor = clazz.getConstructor(mConstructorSignature); constructor.setAccessible(true); sConstructorMap.put(name, constructor); } else{ // If we have a filter, apply it to cached constructor if (mFilter != null) { // Have we seen this name before? Boolean allowedState = mFilterMap.get(name); if (allowedState == null) { // New class -- remember whether it is allowed 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;
final View view = constructor.newInstance(args);// 真正需要关注的关键代码,就是这一行,执行了构造函数,返回了一个View对象 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;