LayoutInflater_Delegate.java revision fb93ce9684120a36862b5b5e67b1865a652907e9
1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.view;
18
19import com.android.layoutlib.bridge.android.BridgeInflater;
20import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
21
22import org.xmlpull.v1.XmlPullParser;
23import org.xmlpull.v1.XmlPullParserException;
24
25import android.content.res.TypedArray;
26import android.content.res.XmlResourceParser;
27import android.util.AttributeSet;
28import android.util.Xml;
29
30import java.io.IOException;
31
32/**
33 * Delegate used to provide new implementation of a select few methods of {@link LayoutInflater}
34 *
35 * Through the layoutlib_create tool, the original  methods of LayoutInflater have been replaced
36 * by calls to methods of the same name in this delegate class.
37 *
38 */
39public class LayoutInflater_Delegate {
40
41    public static boolean sIsInInclude = false;
42
43    /**
44     * Recursive method used to descend down the xml hierarchy and instantiate
45     * views, instantiate their children, and then call onFinishInflate().
46     */
47    @LayoutlibDelegate
48    /*package*/ static void rInflate(LayoutInflater thisInflater,
49            XmlPullParser parser, View parent, final AttributeSet attrs,
50            boolean finishInflate) throws XmlPullParserException, IOException {
51
52        if (finishInflate == false) {
53            // this is a merge rInflate!
54            if (thisInflater instanceof BridgeInflater) {
55                ((BridgeInflater) thisInflater).setIsInMerge(true);
56            }
57        }
58
59        // ---- START DEFAULT IMPLEMENTATION.
60
61        final int depth = parser.getDepth();
62        int type;
63
64        while (((type = parser.next()) != XmlPullParser.END_TAG ||
65                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
66
67            if (type != XmlPullParser.START_TAG) {
68                continue;
69            }
70
71            final String name = parser.getName();
72
73            if (LayoutInflater.TAG_REQUEST_FOCUS.equals(name)) {
74                thisInflater.parseRequestFocus(parser, parent);
75            } else if (LayoutInflater.TAG_INCLUDE.equals(name)) {
76                if (parser.getDepth() == 0) {
77                    throw new InflateException("<include /> cannot be the root element");
78                }
79                thisInflater.parseInclude(parser, parent, attrs);
80            } else if (LayoutInflater.TAG_MERGE.equals(name)) {
81                throw new InflateException("<merge /> must be the root element");
82            } else {
83                final View view = thisInflater.createViewFromTag(parent, name, attrs);
84                final ViewGroup viewGroup = (ViewGroup) parent;
85                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
86                thisInflater.rInflate(parser, view, attrs, true);
87                viewGroup.addView(view, params);
88            }
89        }
90
91        if (finishInflate) parent.onFinishInflate();
92
93        // ---- END DEFAULT IMPLEMENTATION.
94
95        if (finishInflate == false) {
96            // this is a merge rInflate!
97            if (thisInflater instanceof BridgeInflater) {
98                ((BridgeInflater) thisInflater).setIsInMerge(false);
99            }
100        }
101    }
102
103    @LayoutlibDelegate
104    public static void parseInclude(
105            LayoutInflater thisInflater,
106            XmlPullParser parser, View parent, AttributeSet attrs)
107            throws XmlPullParserException, IOException {
108
109        int type;
110
111        if (parent instanceof ViewGroup) {
112            final int layout = attrs.getAttributeResourceValue(null, "layout", 0);
113            if (layout == 0) {
114                final String value = attrs.getAttributeValue(null, "layout");
115                if (value == null) {
116                    throw new InflateException("You must specifiy a layout in the"
117                            + " include tag: <include layout=\"@layout/layoutID\" />");
118                } else {
119                    throw new InflateException("You must specifiy a valid layout "
120                            + "reference. The layout ID " + value + " is not valid.");
121                }
122            } else {
123                final XmlResourceParser childParser =
124                    thisInflater.getContext().getResources().getLayout(layout);
125
126                try {
127                    final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
128
129                    while ((type = childParser.next()) != XmlPullParser.START_TAG &&
130                            type != XmlPullParser.END_DOCUMENT) {
131                        // Empty.
132                    }
133
134                    if (type != XmlPullParser.START_TAG) {
135                        throw new InflateException(childParser.getPositionDescription() +
136                                ": No start tag found!");
137                    }
138
139                    final String childName = childParser.getName();
140
141                    if (LayoutInflater.TAG_MERGE.equals(childName)) {
142                        // Inflate all children.
143                        thisInflater.rInflate(childParser, parent, childAttrs, false);
144                    } else {
145                        final View view = thisInflater.createViewFromTag(parent, childName, childAttrs);
146                        final ViewGroup group = (ViewGroup) parent;
147
148                        // We try to load the layout params set in the <include /> tag. If
149                        // they don't exist, we will rely on the layout params set in the
150                        // included XML file.
151                        // During a layoutparams generation, a runtime exception is thrown
152                        // if either layout_width or layout_height is missing. We catch
153                        // this exception and set localParams accordingly: true means we
154                        // successfully loaded layout params from the <include /> tag,
155                        // false means we need to rely on the included layout params.
156                        ViewGroup.LayoutParams params = null;
157                        try {
158                            // ---- START CHANGES
159                            sIsInInclude = true;
160                            // ---- END CHANGES
161
162                            params = group.generateLayoutParams(attrs);
163
164                        } catch (RuntimeException e) {
165                            // ---- START CHANGES
166                            sIsInInclude = false;
167                            // ---- END CHANGES
168
169                            params = group.generateLayoutParams(childAttrs);
170                        } finally {
171                            // ---- START CHANGES
172                            sIsInInclude = false;
173                            // ---- END CHANGES
174
175                            if (params != null) {
176                                view.setLayoutParams(params);
177                            }
178                        }
179
180                        // Inflate all children.
181                        thisInflater.rInflate(childParser, view, childAttrs, true);
182
183                        // Attempt to override the included layout's android:id with the
184                        // one set on the <include /> tag itself.
185                        TypedArray a = thisInflater.mContext.obtainStyledAttributes(attrs,
186                            com.android.internal.R.styleable.View, 0, 0);
187                        int id = a.getResourceId(com.android.internal.R.styleable.View_id, View.NO_ID);
188                        // While we're at it, let's try to override android:visibility.
189                        int visibility = a.getInt(com.android.internal.R.styleable.View_visibility, -1);
190                        a.recycle();
191
192                        if (id != View.NO_ID) {
193                            view.setId(id);
194                        }
195
196                        switch (visibility) {
197                            case 0:
198                                view.setVisibility(View.VISIBLE);
199                                break;
200                            case 1:
201                                view.setVisibility(View.INVISIBLE);
202                                break;
203                            case 2:
204                                view.setVisibility(View.GONE);
205                                break;
206                        }
207
208                        group.addView(view);
209                    }
210                } finally {
211                    childParser.close();
212                }
213            }
214        } else {
215            throw new InflateException("<include /> can only be used inside of a ViewGroup");
216        }
217
218        final int currentDepth = parser.getDepth();
219        while (((type = parser.next()) != XmlPullParser.END_TAG ||
220                parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
221            // Empty
222        }
223    }
224
225
226}
227