Loading core/java/android/view/LayoutInflater.java +104 −79 Original line number Diff line number Diff line Loading @@ -452,9 +452,10 @@ public abstract class LayoutInflater { synchronized (mConstructorArgs) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate"); final Context inflaterContext = mContext; final AttributeSet attrs = Xml.asAttributeSet(parser); Context lastContext = (Context) mConstructorArgs[0]; mConstructorArgs[0] = mContext; mConstructorArgs[0] = inflaterContext; View result = root; try { Loading Loading @@ -485,10 +486,10 @@ public abstract class LayoutInflater { + "ViewGroup root and attachToRoot=true"); } rInflate(parser, root, attrs, false, false); rInflate(parser, root, inflaterContext, attrs, false); } else { // Temp is the root view that was found in the xml final View temp = createViewFromTag(root, name, attrs, false); final View temp = createViewFromTag(root, name, inflaterContext, attrs); ViewGroup.LayoutParams params = null; Loading @@ -509,8 +510,10 @@ public abstract class LayoutInflater { if (DEBUG) { System.out.println("-----> start inflating children"); } // Inflate all children under temp rInflate(parser, temp, attrs, true, true); // Inflate all children under temp against its context. rInflateChildren(parser, temp, attrs, true); if (DEBUG) { System.out.println("-----> done inflating children"); } Loading Loading @@ -691,60 +694,69 @@ public abstract class LayoutInflater { return onCreateView(name, attrs); } /** * Convenience method for calling through to the five-arg createViewFromTag * method. This method passes {@code false} for the {@code ignoreThemeAttr} * argument and should be used for everything except {@code >include>} * tag parsing. */ private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) { return createViewFromTag(parent, name, context, attrs, false); } /** * Creates a view from a tag name using the supplied attribute set. * <p> * If {@code inheritContext} is true and the parent is non-null, the view * will be inflated in parent view's context. If the view specifies a * <theme> attribute, the inflation context will be wrapped with the * specified theme. * <p> * Note: Default visibility so the BridgeInflater can override it. * <strong>Note:</strong> Default visibility so the BridgeInflater can * override it. * * @param parent the parent view, used to inflate layout params * @param name the name of the XML tag used to define the view * @param context the inflation context for the view, typically the * {@code parent} or base layout inflater context * @param attrs the attribute set for the XML tag used to define the view * @param ignoreThemeAttr {@code true} to ignore the {@code android:theme} * attribute (if set) for the view being inflated, * {@code false} otherwise */ View createViewFromTag(View parent, String name, AttributeSet attrs, boolean inheritContext) { View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, boolean ignoreThemeAttr) { if (name.equals("view")) { name = attrs.getAttributeValue(null, "class"); } Context viewContext; if (parent != null && inheritContext) { viewContext = parent.getContext(); } else { viewContext = mContext; } // Apply a theme wrapper, if requested. final TypedArray ta = viewContext.obtainStyledAttributes(attrs, ATTRS_THEME); // 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) { viewContext = new ContextThemeWrapper(viewContext, themeResId); context = new ContextThemeWrapper(context, themeResId); } ta.recycle(); } if (name.equals(TAG_1995)) { // Let's party like it's 1995! return new BlinkLayout(viewContext, attrs); return new BlinkLayout(context, attrs); } if (DEBUG) System.out.println("******** Creating view: " + name); try { View view; if (mFactory2 != null) { view = mFactory2.onCreateView(parent, name, viewContext, attrs); view = mFactory2.onCreateView(parent, name, context, attrs); } else if (mFactory != null) { view = mFactory.onCreateView(name, viewContext, attrs); view = mFactory.onCreateView(name, context, attrs); } else { view = null; } if (view == null && mPrivateFactory != null) { view = mPrivateFactory.onCreateView(parent, name, viewContext, attrs); view = mPrivateFactory.onCreateView(parent, name, context, attrs); } if (view == null) { final Object lastContext = mConstructorArgs[0]; mConstructorArgs[0] = viewContext; mConstructorArgs[0] = context; try { if (-1 == name.indexOf('.')) { view = onCreateView(parent, name, attrs); Loading @@ -756,37 +768,45 @@ public abstract class LayoutInflater { } } if (DEBUG) System.out.println("Created view is: " + view); return view; } catch (InflateException e) { throw e; } catch (ClassNotFoundException e) { InflateException ie = new InflateException(attrs.getPositionDescription() final InflateException ie = new InflateException(attrs.getPositionDescription() + ": Error inflating class " + name); ie.initCause(e); throw ie; } catch (Exception e) { InflateException ie = new InflateException(attrs.getPositionDescription() final InflateException ie = new InflateException(attrs.getPositionDescription() + ": Error inflating class " + name); ie.initCause(e); throw ie; } } /** * Recursive method used to inflate internal (non-root) children. This * method calls through to {@link #rInflate} using the parent context as * the inflation context. * <strong>Note:</strong> Default visibility so the BridgeInflater can * call it. */ final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException { rInflate(parser, parent, parent.getContext(), attrs, finishInflate); } /** * Recursive method used to descend down the xml hierarchy and instantiate * views, instantiate their children, and then call onFinishInflate(). * * @param inheritContext Whether the root view should be inflated in its * parent's context. This should be true when called inflating * child views recursively, or false otherwise. * <p> * <strong>Note:</strong> Default visibility so the BridgeInflater can * override it. */ void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs, boolean finishInflate, boolean inheritContext) throws XmlPullParserException, IOException { void rInflate(XmlPullParser parser, View parent, Context context, AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException { final int depth = parser.getDepth(); int type; Loading @@ -808,19 +828,21 @@ public abstract class LayoutInflater { if (parser.getDepth() == 0) { throw new InflateException("<include /> cannot be the root element"); } parseInclude(parser, parent, attrs, inheritContext); 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, attrs, inheritContext); final View view = createViewFromTag(parent, name, context, attrs); final ViewGroup viewGroup = (ViewGroup) parent; final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs); rInflate(parser, view, attrs, true, true); rInflateChildren(parser, view, attrs, true); viewGroup.addView(view, params); } } if (finishInflate) parent.onFinishInflate(); if (finishInflate) { parent.onFinishInflate(); } } /** Loading @@ -829,13 +851,9 @@ public abstract class LayoutInflater { */ private void parseRequestFocus(XmlPullParser parser, View view) throws XmlPullParserException, IOException { int type; view.requestFocus(); final int currentDepth = parser.getDepth(); while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) { // Empty } consumeChildElements(parser); } /** Loading @@ -844,33 +862,29 @@ public abstract class LayoutInflater { */ private void parseViewTag(XmlPullParser parser, View view, AttributeSet attrs) throws XmlPullParserException, IOException { int type; final TypedArray ta = view.getContext().obtainStyledAttributes( attrs, com.android.internal.R.styleable.ViewTag); final int key = ta.getResourceId(com.android.internal.R.styleable.ViewTag_id, 0); final CharSequence value = ta.getText(com.android.internal.R.styleable.ViewTag_value); final Context context = view.getContext(); final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ViewTag); final int key = ta.getResourceId(R.styleable.ViewTag_id, 0); final CharSequence value = ta.getText(R.styleable.ViewTag_value); view.setTag(key, value); ta.recycle(); final int currentDepth = parser.getDepth(); while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) { // Empty } consumeChildElements(parser); } private void parseInclude(XmlPullParser parser, View parent, AttributeSet attrs, boolean inheritContext) throws XmlPullParserException, IOException { private void parseInclude(XmlPullParser parser, Context context, View parent, AttributeSet attrs) throws XmlPullParserException, IOException { int type; if (parent instanceof ViewGroup) { Context context = inheritContext ? parent.getContext() : mContext; // Apply a theme wrapper, if requested. // Apply a theme wrapper, if requested. This is sort of a weird // edge case, since developers think the <include> overwrites // values in the AttributeSet of the included View. So, if the // included View has a theme attribute, we'll need to ignore it. final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME); final int themeResId = ta.getResourceId(0, 0); if (themeResId != 0) { final boolean hasThemeOverride = themeResId != 0; if (hasThemeOverride) { context = new ContextThemeWrapper(context, themeResId); } ta.recycle(); Loading @@ -880,11 +894,12 @@ public abstract class LayoutInflater { int layout = attrs.getAttributeResourceValue(null, ATTR_LAYOUT, 0); if (layout == 0) { final String value = attrs.getAttributeValue(null, ATTR_LAYOUT); if (value == null || value.length() < 1) { if (value == null || value.length() <= 0) { throw new InflateException("You must specify a layout in the" + " include tag: <include layout=\"@layout/layoutID\" />"); } // Attempt to resolve the "?attr/name" string to an identifier. layout = context.getResources().getIdentifier(value.substring(1), null, null); } Loading @@ -901,8 +916,7 @@ public abstract class LayoutInflater { throw new InflateException("You must specify a valid layout " + "reference. The layout ID " + value + " is not valid."); } else { final XmlResourceParser childParser = getContext().getResources().getLayout(layout); final XmlResourceParser childParser = context.getResources().getLayout(layout); try { final AttributeSet childAttrs = Xml.asAttributeSet(childParser); Loading @@ -920,11 +934,12 @@ public abstract class LayoutInflater { final String childName = childParser.getName(); if (TAG_MERGE.equals(childName)) { // Inflate all children. rInflate(childParser, parent, childAttrs, false, inheritContext); // The <merge> tag doesn't support android:theme, so // nothing special to do here. rInflate(childParser, parent, context, childAttrs, false); } else { final View view = createViewFromTag(parent, childName, childAttrs, inheritContext); final View view = createViewFromTag(parent, childName, context, childAttrs, hasThemeOverride); final ViewGroup group = (ViewGroup) parent; final TypedArray a = context.obtainStyledAttributes( Loading Loading @@ -957,7 +972,7 @@ public abstract class LayoutInflater { view.setLayoutParams(params); // Inflate all children. rInflate(childParser, view, childAttrs, true, true); rInflateChildren(childParser, view, childAttrs, true); if (id != View.NO_ID) { view.setId(id); Loading Loading @@ -985,6 +1000,16 @@ public abstract class LayoutInflater { throw new InflateException("<include /> can only be used inside of a ViewGroup"); } LayoutInflater.consumeChildElements(parser); } /** * <strong>Note:</strong> default visibility so that * LayoutInflater_Delegate can call it. */ final static void consumeChildElements(XmlPullParser parser) throws XmlPullParserException, IOException { int type; final int currentDepth = parser.getDepth(); while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) { Loading tools/layoutlib/bridge/src/android/view/BridgeInflater.java +3 −3 Original line number Diff line number Diff line Loading @@ -131,11 +131,11 @@ public final class BridgeInflater extends LayoutInflater { } @Override public View createViewFromTag(View parent, String name, AttributeSet attrs, boolean inheritContext) { public View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, boolean ignoreThemeAttrs) { View view; try { view = super.createViewFromTag(parent, name, attrs, inheritContext); view = super.createViewFromTag(parent, name, context, attrs, ignoreThemeAttrs); } catch (InflateException e) { // try to load the class from using the custom view loader try { Loading tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java +81 −48 Original line number Diff line number Diff line Loading @@ -21,9 +21,11 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import android.content.Context; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.util.AttributeSet; import android.util.TypedValue; import android.util.Xml; import java.io.IOException; Loading @@ -36,9 +38,13 @@ import java.io.IOException; * */ public class LayoutInflater_Delegate { private static final String TAG_MERGE = "merge"; private static final String ATTR_LAYOUT = "layout"; private static final int[] ATTRS_THEME = new int[] { com.android.internal.R.attr.theme }; public static boolean sIsInInclude = false; /** Loading @@ -49,7 +55,7 @@ public class LayoutInflater_Delegate { */ @LayoutlibDelegate /* package */ static void rInflate(LayoutInflater thisInflater, XmlPullParser parser, View parent, final AttributeSet attrs, boolean finishInflate, boolean inheritContext) View parent, Context context, AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException { if (finishInflate == false) { Loading @@ -61,7 +67,7 @@ public class LayoutInflater_Delegate { // ---- START DEFAULT IMPLEMENTATION. thisInflater.rInflate_Original(parser, parent, attrs, finishInflate, inheritContext); thisInflater.rInflate_Original(parser, parent, context, attrs, finishInflate); // ---- END DEFAULT IMPLEMENTATION. Loading @@ -74,15 +80,50 @@ public class LayoutInflater_Delegate { } @LayoutlibDelegate public static void parseInclude(LayoutInflater thisInflater, XmlPullParser parser, View parent, AttributeSet attrs, boolean inheritContext) throws XmlPullParserException, IOException { public static void parseInclude(LayoutInflater thisInflater, XmlPullParser parser, Context context, View parent, AttributeSet attrs) throws XmlPullParserException, IOException { int type; if (parent instanceof ViewGroup) { final int layout = attrs.getAttributeResourceValue(null, "layout", 0); // Apply a theme wrapper, if requested. This is sort of a weird // edge case, since developers think the <include> overwrites // values in the AttributeSet of the included View. So, if the // included View has a theme attribute, we'll need to ignore it. final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME); final int themeResId = ta.getResourceId(0, 0); final boolean hasThemeOverride = themeResId != 0; if (hasThemeOverride) { context = new ContextThemeWrapper(context, themeResId); } ta.recycle(); // If the layout is pointing to a theme attribute, we have to // massage the value to get a resource identifier out of it. 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) { throw new InflateException("You must specify a layout in the" + " include tag: <include layout=\"@layout/layoutID\" />"); } // Attempt to resolve the "?attr/name" string to an identifier. layout = context.getResources().getIdentifier(value.substring(1), null, null); } // The layout might be referencing a theme attribute. // ---- START CHANGES if (layout != 0) { final TypedValue tempValue = new TypedValue(); if (context.getTheme().resolveAttribute(layout, tempValue, true)) { layout = tempValue.resourceId; } } // ---- END CHANGES if (layout == 0) { final String value = attrs.getAttributeValue(null, "layout"); final String value = attrs.getAttributeValue(null, ATTR_LAYOUT); if (value == null) { throw new InflateException("You must specifiy a layout in the" + " include tag: <include layout=\"@layout/layoutID\" />"); Loading Loading @@ -111,13 +152,24 @@ public class LayoutInflater_Delegate { if (TAG_MERGE.equals(childName)) { // Inflate all children. thisInflater.rInflate(childParser, parent, childAttrs, false, inheritContext); thisInflater.rInflate(childParser, parent, context, childAttrs, false); } else { final View view = thisInflater.createViewFromTag(parent, childName, childAttrs, inheritContext); context, childAttrs, hasThemeOverride); final ViewGroup group = (ViewGroup) parent; final TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.Include); final int id = a.getResourceId( com.android.internal.R.styleable.Include_id, View.NO_ID); final int visibility = a.getInt( com.android.internal.R.styleable.Include_visibility, -1); final boolean hasWidth = a.hasValue( com.android.internal.R.styleable.Include_layout_width); final boolean hasHeight = a.hasValue( com.android.internal.R.styleable.Include_layout_height); a.recycle(); // We try to load the layout params set in the <include /> tag. If // they don't exist, we will rely on the layout params set in the // included XML file. Loading @@ -127,6 +179,7 @@ public class LayoutInflater_Delegate { // successfully loaded layout params from the <include /> tag, // false means we need to rely on the included layout params. ViewGroup.LayoutParams params = null; if (hasWidth && hasHeight) { try { // ---- START CHANGES sIsInInclude = true; Loading @@ -134,33 +187,19 @@ public class LayoutInflater_Delegate { params = group.generateLayoutParams(attrs); } catch (RuntimeException e) { // ---- START CHANGES sIsInInclude = false; // ---- END CHANGES params = group.generateLayoutParams(childAttrs); } finally { // ---- START CHANGES sIsInInclude = false; // ---- END CHANGES if (params != null) { view.setLayoutParams(params); } } if (params == null) { params = group.generateLayoutParams(childAttrs); } view.setLayoutParams(params); // Inflate all children. thisInflater.rInflate(childParser, view, childAttrs, true, true); // Attempt to override the included layout's android:id with the // one set on the <include /> tag itself. TypedArray a = thisInflater.mContext.obtainStyledAttributes(attrs, com.android.internal.R.styleable.View, 0, 0); int id = a.getResourceId(com.android.internal.R.styleable.View_id, View.NO_ID); // While we're at it, let's try to override android:visibility. int visibility = a.getInt(com.android.internal.R.styleable.View_visibility, -1); a.recycle(); thisInflater.rInflateChildren(childParser, view, childAttrs, true); if (id != View.NO_ID) { view.setId(id); Loading Loading @@ -188,12 +227,6 @@ public class LayoutInflater_Delegate { throw new InflateException("<include /> can only be used inside of a ViewGroup"); } final int currentDepth = parser.getDepth(); while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) { // Empty } LayoutInflater.consumeChildElements(parser); } } Loading
core/java/android/view/LayoutInflater.java +104 −79 Original line number Diff line number Diff line Loading @@ -452,9 +452,10 @@ public abstract class LayoutInflater { synchronized (mConstructorArgs) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate"); final Context inflaterContext = mContext; final AttributeSet attrs = Xml.asAttributeSet(parser); Context lastContext = (Context) mConstructorArgs[0]; mConstructorArgs[0] = mContext; mConstructorArgs[0] = inflaterContext; View result = root; try { Loading Loading @@ -485,10 +486,10 @@ public abstract class LayoutInflater { + "ViewGroup root and attachToRoot=true"); } rInflate(parser, root, attrs, false, false); rInflate(parser, root, inflaterContext, attrs, false); } else { // Temp is the root view that was found in the xml final View temp = createViewFromTag(root, name, attrs, false); final View temp = createViewFromTag(root, name, inflaterContext, attrs); ViewGroup.LayoutParams params = null; Loading @@ -509,8 +510,10 @@ public abstract class LayoutInflater { if (DEBUG) { System.out.println("-----> start inflating children"); } // Inflate all children under temp rInflate(parser, temp, attrs, true, true); // Inflate all children under temp against its context. rInflateChildren(parser, temp, attrs, true); if (DEBUG) { System.out.println("-----> done inflating children"); } Loading Loading @@ -691,60 +694,69 @@ public abstract class LayoutInflater { return onCreateView(name, attrs); } /** * Convenience method for calling through to the five-arg createViewFromTag * method. This method passes {@code false} for the {@code ignoreThemeAttr} * argument and should be used for everything except {@code >include>} * tag parsing. */ private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) { return createViewFromTag(parent, name, context, attrs, false); } /** * Creates a view from a tag name using the supplied attribute set. * <p> * If {@code inheritContext} is true and the parent is non-null, the view * will be inflated in parent view's context. If the view specifies a * <theme> attribute, the inflation context will be wrapped with the * specified theme. * <p> * Note: Default visibility so the BridgeInflater can override it. * <strong>Note:</strong> Default visibility so the BridgeInflater can * override it. * * @param parent the parent view, used to inflate layout params * @param name the name of the XML tag used to define the view * @param context the inflation context for the view, typically the * {@code parent} or base layout inflater context * @param attrs the attribute set for the XML tag used to define the view * @param ignoreThemeAttr {@code true} to ignore the {@code android:theme} * attribute (if set) for the view being inflated, * {@code false} otherwise */ View createViewFromTag(View parent, String name, AttributeSet attrs, boolean inheritContext) { View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, boolean ignoreThemeAttr) { if (name.equals("view")) { name = attrs.getAttributeValue(null, "class"); } Context viewContext; if (parent != null && inheritContext) { viewContext = parent.getContext(); } else { viewContext = mContext; } // Apply a theme wrapper, if requested. final TypedArray ta = viewContext.obtainStyledAttributes(attrs, ATTRS_THEME); // 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) { viewContext = new ContextThemeWrapper(viewContext, themeResId); context = new ContextThemeWrapper(context, themeResId); } ta.recycle(); } if (name.equals(TAG_1995)) { // Let's party like it's 1995! return new BlinkLayout(viewContext, attrs); return new BlinkLayout(context, attrs); } if (DEBUG) System.out.println("******** Creating view: " + name); try { View view; if (mFactory2 != null) { view = mFactory2.onCreateView(parent, name, viewContext, attrs); view = mFactory2.onCreateView(parent, name, context, attrs); } else if (mFactory != null) { view = mFactory.onCreateView(name, viewContext, attrs); view = mFactory.onCreateView(name, context, attrs); } else { view = null; } if (view == null && mPrivateFactory != null) { view = mPrivateFactory.onCreateView(parent, name, viewContext, attrs); view = mPrivateFactory.onCreateView(parent, name, context, attrs); } if (view == null) { final Object lastContext = mConstructorArgs[0]; mConstructorArgs[0] = viewContext; mConstructorArgs[0] = context; try { if (-1 == name.indexOf('.')) { view = onCreateView(parent, name, attrs); Loading @@ -756,37 +768,45 @@ public abstract class LayoutInflater { } } if (DEBUG) System.out.println("Created view is: " + view); return view; } catch (InflateException e) { throw e; } catch (ClassNotFoundException e) { InflateException ie = new InflateException(attrs.getPositionDescription() final InflateException ie = new InflateException(attrs.getPositionDescription() + ": Error inflating class " + name); ie.initCause(e); throw ie; } catch (Exception e) { InflateException ie = new InflateException(attrs.getPositionDescription() final InflateException ie = new InflateException(attrs.getPositionDescription() + ": Error inflating class " + name); ie.initCause(e); throw ie; } } /** * Recursive method used to inflate internal (non-root) children. This * method calls through to {@link #rInflate} using the parent context as * the inflation context. * <strong>Note:</strong> Default visibility so the BridgeInflater can * call it. */ final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException { rInflate(parser, parent, parent.getContext(), attrs, finishInflate); } /** * Recursive method used to descend down the xml hierarchy and instantiate * views, instantiate their children, and then call onFinishInflate(). * * @param inheritContext Whether the root view should be inflated in its * parent's context. This should be true when called inflating * child views recursively, or false otherwise. * <p> * <strong>Note:</strong> Default visibility so the BridgeInflater can * override it. */ void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs, boolean finishInflate, boolean inheritContext) throws XmlPullParserException, IOException { void rInflate(XmlPullParser parser, View parent, Context context, AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException { final int depth = parser.getDepth(); int type; Loading @@ -808,19 +828,21 @@ public abstract class LayoutInflater { if (parser.getDepth() == 0) { throw new InflateException("<include /> cannot be the root element"); } parseInclude(parser, parent, attrs, inheritContext); 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, attrs, inheritContext); final View view = createViewFromTag(parent, name, context, attrs); final ViewGroup viewGroup = (ViewGroup) parent; final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs); rInflate(parser, view, attrs, true, true); rInflateChildren(parser, view, attrs, true); viewGroup.addView(view, params); } } if (finishInflate) parent.onFinishInflate(); if (finishInflate) { parent.onFinishInflate(); } } /** Loading @@ -829,13 +851,9 @@ public abstract class LayoutInflater { */ private void parseRequestFocus(XmlPullParser parser, View view) throws XmlPullParserException, IOException { int type; view.requestFocus(); final int currentDepth = parser.getDepth(); while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) { // Empty } consumeChildElements(parser); } /** Loading @@ -844,33 +862,29 @@ public abstract class LayoutInflater { */ private void parseViewTag(XmlPullParser parser, View view, AttributeSet attrs) throws XmlPullParserException, IOException { int type; final TypedArray ta = view.getContext().obtainStyledAttributes( attrs, com.android.internal.R.styleable.ViewTag); final int key = ta.getResourceId(com.android.internal.R.styleable.ViewTag_id, 0); final CharSequence value = ta.getText(com.android.internal.R.styleable.ViewTag_value); final Context context = view.getContext(); final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ViewTag); final int key = ta.getResourceId(R.styleable.ViewTag_id, 0); final CharSequence value = ta.getText(R.styleable.ViewTag_value); view.setTag(key, value); ta.recycle(); final int currentDepth = parser.getDepth(); while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) { // Empty } consumeChildElements(parser); } private void parseInclude(XmlPullParser parser, View parent, AttributeSet attrs, boolean inheritContext) throws XmlPullParserException, IOException { private void parseInclude(XmlPullParser parser, Context context, View parent, AttributeSet attrs) throws XmlPullParserException, IOException { int type; if (parent instanceof ViewGroup) { Context context = inheritContext ? parent.getContext() : mContext; // Apply a theme wrapper, if requested. // Apply a theme wrapper, if requested. This is sort of a weird // edge case, since developers think the <include> overwrites // values in the AttributeSet of the included View. So, if the // included View has a theme attribute, we'll need to ignore it. final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME); final int themeResId = ta.getResourceId(0, 0); if (themeResId != 0) { final boolean hasThemeOverride = themeResId != 0; if (hasThemeOverride) { context = new ContextThemeWrapper(context, themeResId); } ta.recycle(); Loading @@ -880,11 +894,12 @@ public abstract class LayoutInflater { int layout = attrs.getAttributeResourceValue(null, ATTR_LAYOUT, 0); if (layout == 0) { final String value = attrs.getAttributeValue(null, ATTR_LAYOUT); if (value == null || value.length() < 1) { if (value == null || value.length() <= 0) { throw new InflateException("You must specify a layout in the" + " include tag: <include layout=\"@layout/layoutID\" />"); } // Attempt to resolve the "?attr/name" string to an identifier. layout = context.getResources().getIdentifier(value.substring(1), null, null); } Loading @@ -901,8 +916,7 @@ public abstract class LayoutInflater { throw new InflateException("You must specify a valid layout " + "reference. The layout ID " + value + " is not valid."); } else { final XmlResourceParser childParser = getContext().getResources().getLayout(layout); final XmlResourceParser childParser = context.getResources().getLayout(layout); try { final AttributeSet childAttrs = Xml.asAttributeSet(childParser); Loading @@ -920,11 +934,12 @@ public abstract class LayoutInflater { final String childName = childParser.getName(); if (TAG_MERGE.equals(childName)) { // Inflate all children. rInflate(childParser, parent, childAttrs, false, inheritContext); // The <merge> tag doesn't support android:theme, so // nothing special to do here. rInflate(childParser, parent, context, childAttrs, false); } else { final View view = createViewFromTag(parent, childName, childAttrs, inheritContext); final View view = createViewFromTag(parent, childName, context, childAttrs, hasThemeOverride); final ViewGroup group = (ViewGroup) parent; final TypedArray a = context.obtainStyledAttributes( Loading Loading @@ -957,7 +972,7 @@ public abstract class LayoutInflater { view.setLayoutParams(params); // Inflate all children. rInflate(childParser, view, childAttrs, true, true); rInflateChildren(childParser, view, childAttrs, true); if (id != View.NO_ID) { view.setId(id); Loading Loading @@ -985,6 +1000,16 @@ public abstract class LayoutInflater { throw new InflateException("<include /> can only be used inside of a ViewGroup"); } LayoutInflater.consumeChildElements(parser); } /** * <strong>Note:</strong> default visibility so that * LayoutInflater_Delegate can call it. */ final static void consumeChildElements(XmlPullParser parser) throws XmlPullParserException, IOException { int type; final int currentDepth = parser.getDepth(); while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) { Loading
tools/layoutlib/bridge/src/android/view/BridgeInflater.java +3 −3 Original line number Diff line number Diff line Loading @@ -131,11 +131,11 @@ public final class BridgeInflater extends LayoutInflater { } @Override public View createViewFromTag(View parent, String name, AttributeSet attrs, boolean inheritContext) { public View createViewFromTag(View parent, String name, Context context, AttributeSet attrs, boolean ignoreThemeAttrs) { View view; try { view = super.createViewFromTag(parent, name, attrs, inheritContext); view = super.createViewFromTag(parent, name, context, attrs, ignoreThemeAttrs); } catch (InflateException e) { // try to load the class from using the custom view loader try { Loading
tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java +81 −48 Original line number Diff line number Diff line Loading @@ -21,9 +21,11 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import android.content.Context; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.util.AttributeSet; import android.util.TypedValue; import android.util.Xml; import java.io.IOException; Loading @@ -36,9 +38,13 @@ import java.io.IOException; * */ public class LayoutInflater_Delegate { private static final String TAG_MERGE = "merge"; private static final String ATTR_LAYOUT = "layout"; private static final int[] ATTRS_THEME = new int[] { com.android.internal.R.attr.theme }; public static boolean sIsInInclude = false; /** Loading @@ -49,7 +55,7 @@ public class LayoutInflater_Delegate { */ @LayoutlibDelegate /* package */ static void rInflate(LayoutInflater thisInflater, XmlPullParser parser, View parent, final AttributeSet attrs, boolean finishInflate, boolean inheritContext) View parent, Context context, AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException { if (finishInflate == false) { Loading @@ -61,7 +67,7 @@ public class LayoutInflater_Delegate { // ---- START DEFAULT IMPLEMENTATION. thisInflater.rInflate_Original(parser, parent, attrs, finishInflate, inheritContext); thisInflater.rInflate_Original(parser, parent, context, attrs, finishInflate); // ---- END DEFAULT IMPLEMENTATION. Loading @@ -74,15 +80,50 @@ public class LayoutInflater_Delegate { } @LayoutlibDelegate public static void parseInclude(LayoutInflater thisInflater, XmlPullParser parser, View parent, AttributeSet attrs, boolean inheritContext) throws XmlPullParserException, IOException { public static void parseInclude(LayoutInflater thisInflater, XmlPullParser parser, Context context, View parent, AttributeSet attrs) throws XmlPullParserException, IOException { int type; if (parent instanceof ViewGroup) { final int layout = attrs.getAttributeResourceValue(null, "layout", 0); // Apply a theme wrapper, if requested. This is sort of a weird // edge case, since developers think the <include> overwrites // values in the AttributeSet of the included View. So, if the // included View has a theme attribute, we'll need to ignore it. final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME); final int themeResId = ta.getResourceId(0, 0); final boolean hasThemeOverride = themeResId != 0; if (hasThemeOverride) { context = new ContextThemeWrapper(context, themeResId); } ta.recycle(); // If the layout is pointing to a theme attribute, we have to // massage the value to get a resource identifier out of it. 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) { throw new InflateException("You must specify a layout in the" + " include tag: <include layout=\"@layout/layoutID\" />"); } // Attempt to resolve the "?attr/name" string to an identifier. layout = context.getResources().getIdentifier(value.substring(1), null, null); } // The layout might be referencing a theme attribute. // ---- START CHANGES if (layout != 0) { final TypedValue tempValue = new TypedValue(); if (context.getTheme().resolveAttribute(layout, tempValue, true)) { layout = tempValue.resourceId; } } // ---- END CHANGES if (layout == 0) { final String value = attrs.getAttributeValue(null, "layout"); final String value = attrs.getAttributeValue(null, ATTR_LAYOUT); if (value == null) { throw new InflateException("You must specifiy a layout in the" + " include tag: <include layout=\"@layout/layoutID\" />"); Loading Loading @@ -111,13 +152,24 @@ public class LayoutInflater_Delegate { if (TAG_MERGE.equals(childName)) { // Inflate all children. thisInflater.rInflate(childParser, parent, childAttrs, false, inheritContext); thisInflater.rInflate(childParser, parent, context, childAttrs, false); } else { final View view = thisInflater.createViewFromTag(parent, childName, childAttrs, inheritContext); context, childAttrs, hasThemeOverride); final ViewGroup group = (ViewGroup) parent; final TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.Include); final int id = a.getResourceId( com.android.internal.R.styleable.Include_id, View.NO_ID); final int visibility = a.getInt( com.android.internal.R.styleable.Include_visibility, -1); final boolean hasWidth = a.hasValue( com.android.internal.R.styleable.Include_layout_width); final boolean hasHeight = a.hasValue( com.android.internal.R.styleable.Include_layout_height); a.recycle(); // We try to load the layout params set in the <include /> tag. If // they don't exist, we will rely on the layout params set in the // included XML file. Loading @@ -127,6 +179,7 @@ public class LayoutInflater_Delegate { // successfully loaded layout params from the <include /> tag, // false means we need to rely on the included layout params. ViewGroup.LayoutParams params = null; if (hasWidth && hasHeight) { try { // ---- START CHANGES sIsInInclude = true; Loading @@ -134,33 +187,19 @@ public class LayoutInflater_Delegate { params = group.generateLayoutParams(attrs); } catch (RuntimeException e) { // ---- START CHANGES sIsInInclude = false; // ---- END CHANGES params = group.generateLayoutParams(childAttrs); } finally { // ---- START CHANGES sIsInInclude = false; // ---- END CHANGES if (params != null) { view.setLayoutParams(params); } } if (params == null) { params = group.generateLayoutParams(childAttrs); } view.setLayoutParams(params); // Inflate all children. thisInflater.rInflate(childParser, view, childAttrs, true, true); // Attempt to override the included layout's android:id with the // one set on the <include /> tag itself. TypedArray a = thisInflater.mContext.obtainStyledAttributes(attrs, com.android.internal.R.styleable.View, 0, 0); int id = a.getResourceId(com.android.internal.R.styleable.View_id, View.NO_ID); // While we're at it, let's try to override android:visibility. int visibility = a.getInt(com.android.internal.R.styleable.View_visibility, -1); a.recycle(); thisInflater.rInflateChildren(childParser, view, childAttrs, true); if (id != View.NO_ID) { view.setId(id); Loading Loading @@ -188,12 +227,6 @@ public class LayoutInflater_Delegate { throw new InflateException("<include /> can only be used inside of a ViewGroup"); } final int currentDepth = parser.getDepth(); while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) { // Empty } LayoutInflater.consumeChildElements(parser); } }