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