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 17 package android.view; 18 19 import com.android.tools.layoutlib.annotations.LayoutlibDelegate; 20 21 import org.xmlpull.v1.XmlPullParser; 22 import org.xmlpull.v1.XmlPullParserException; 23 24 import android.content.res.TypedArray; 25 import android.content.res.XmlResourceParser; 26 import android.util.AttributeSet; 27 import android.util.Xml; 28 29 import java.io.IOException; 30 31 /** 32 * Delegate used to provide new implementation of a select few methods of {@link LayoutInflater} 33 * 34 * Through the layoutlib_create tool, the original methods of LayoutInflater have been replaced 35 * by calls to methods of the same name in this delegate class. 36 * 37 */ 38 public class LayoutInflater_Delegate { 39 40 private static final String TAG_MERGE = "merge"; 41 42 public static boolean sIsInInclude = false; 43 44 /** 45 * Recursive method used to descend down the xml hierarchy and instantiate 46 * views, instantiate their children, and then call onFinishInflate(). 47 * 48 * This implementation just records the merge status before calling the default implementation. 49 */ 50 @LayoutlibDelegate 51 /*package*/ static void rInflate(LayoutInflater thisInflater, 52 XmlPullParser parser, View parent, final AttributeSet attrs, 53 boolean finishInflate) throws XmlPullParserException, IOException { 54 55 if (finishInflate == false) { 56 // this is a merge rInflate! 57 if (thisInflater instanceof BridgeInflater) { 58 ((BridgeInflater) thisInflater).setIsInMerge(true); 59 } 60 } 61 62 // ---- START DEFAULT IMPLEMENTATION. 63 64 thisInflater.rInflate_Original(parser, parent, attrs, finishInflate); 65 66 // ---- END DEFAULT IMPLEMENTATION. 67 68 if (finishInflate == false) { 69 // this is a merge rInflate! 70 if (thisInflater instanceof BridgeInflater) { 71 ((BridgeInflater) thisInflater).setIsInMerge(false); 72 } 73 } 74 } 75 76 @LayoutlibDelegate 77 public static void parseInclude( 78 LayoutInflater thisInflater, 79 XmlPullParser parser, View parent, AttributeSet attrs) 80 throws XmlPullParserException, IOException { 81 82 int type; 83 84 if (parent instanceof ViewGroup) { 85 final int layout = attrs.getAttributeResourceValue(null, "layout", 0); 86 if (layout == 0) { 87 final String value = attrs.getAttributeValue(null, "layout"); 88 if (value == null) { 89 throw new InflateException("You must specifiy a layout in the" 90 + " include tag: <include layout=\"@layout/layoutID\" />"); 91 } else { 92 throw new InflateException("You must specifiy a valid layout " 93 + "reference. The layout ID " + value + " is not valid."); 94 } 95 } else { 96 final XmlResourceParser childParser = 97 thisInflater.getContext().getResources().getLayout(layout); 98 99 try { 100 final AttributeSet childAttrs = Xml.asAttributeSet(childParser); 101 102 while ((type = childParser.next()) != XmlPullParser.START_TAG && 103 type != XmlPullParser.END_DOCUMENT) { 104 // Empty. 105 } 106 107 if (type != XmlPullParser.START_TAG) { 108 throw new InflateException(childParser.getPositionDescription() + 109 ": No start tag found!"); 110 } 111 112 final String childName = childParser.getName(); 113 114 if (TAG_MERGE.equals(childName)) { 115 // Inflate all children. 116 thisInflater.rInflate(childParser, parent, childAttrs, false); 117 } else { 118 final View view = thisInflater.createViewFromTag(parent, childName, childAttrs); 119 final ViewGroup group = (ViewGroup) parent; 120 121 // We try to load the layout params set in the <include /> tag. If 122 // they don't exist, we will rely on the layout params set in the 123 // included XML file. 124 // During a layoutparams generation, a runtime exception is thrown 125 // if either layout_width or layout_height is missing. We catch 126 // this exception and set localParams accordingly: true means we 127 // successfully loaded layout params from the <include /> tag, 128 // false means we need to rely on the included layout params. 129 ViewGroup.LayoutParams params = null; 130 try { 131 // ---- START CHANGES 132 sIsInInclude = true; 133 // ---- END CHANGES 134 135 params = group.generateLayoutParams(attrs); 136 137 } catch (RuntimeException e) { 138 // ---- START CHANGES 139 sIsInInclude = false; 140 // ---- END CHANGES 141 142 params = group.generateLayoutParams(childAttrs); 143 } finally { 144 // ---- START CHANGES 145 sIsInInclude = false; 146 // ---- END CHANGES 147 148 if (params != null) { 149 view.setLayoutParams(params); 150 } 151 } 152 153 // Inflate all children. 154 thisInflater.rInflate(childParser, view, childAttrs, true); 155 156 // Attempt to override the included layout's android:id with the 157 // one set on the <include /> tag itself. 158 TypedArray a = thisInflater.mContext.obtainStyledAttributes(attrs, 159 com.android.internal.R.styleable.View, 0, 0); 160 int id = a.getResourceId(com.android.internal.R.styleable.View_id, View.NO_ID); 161 // While we're at it, let's try to override android:visibility. 162 int visibility = a.getInt(com.android.internal.R.styleable.View_visibility, -1); 163 a.recycle(); 164 165 if (id != View.NO_ID) { 166 view.setId(id); 167 } 168 169 switch (visibility) { 170 case 0: 171 view.setVisibility(View.VISIBLE); 172 break; 173 case 1: 174 view.setVisibility(View.INVISIBLE); 175 break; 176 case 2: 177 view.setVisibility(View.GONE); 178 break; 179 } 180 181 group.addView(view); 182 } 183 } finally { 184 childParser.close(); 185 } 186 } 187 } else { 188 throw new InflateException("<include /> can only be used inside of a ViewGroup"); 189 } 190 191 final int currentDepth = parser.getDepth(); 192 while (((type = parser.next()) != XmlPullParser.END_TAG || 193 parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) { 194 // Empty 195 } 196 } 197 198 199 } 200