1 /* 2 * Copyright (C) 2008 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 android.content.Context; 20 import android.content.res.TypedArray; 21 import android.graphics.Canvas; 22 import android.util.AttributeSet; 23 24 import com.android.internal.R; 25 26 import java.lang.ref.WeakReference; 27 28 /** 29 * A ViewStub is an invisible, zero-sized View that can be used to lazily inflate 30 * layout resources at runtime. 31 * 32 * When a ViewStub is made visible, or when {@link #inflate()} is invoked, the layout resource 33 * is inflated. The ViewStub then replaces itself in its parent with the inflated View or Views. 34 * Therefore, the ViewStub exists in the view hierarchy until {@link #setVisibility(int)} or 35 * {@link #inflate()} is invoked. 36 * 37 * The inflated View is added to the ViewStub's parent with the ViewStub's layout 38 * parameters. Similarly, you can define/override the inflate View's id by using the 39 * ViewStub's inflatedId property. For instance: 40 * 41 * <pre> 42 * <ViewStub android:id="@+id/stub" 43 * android:inflatedId="@+id/subTree" 44 * android:layout="@layout/mySubTree" 45 * android:layout_width="120dip" 46 * android:layout_height="40dip" /> 47 * </pre> 48 * 49 * The ViewStub thus defined can be found using the id "stub." After inflation of 50 * the layout resource "mySubTree," the ViewStub is removed from its parent. The 51 * View created by inflating the layout resource "mySubTree" can be found using the 52 * id "subTree," specified by the inflatedId property. The inflated View is finally 53 * assigned a width of 120dip and a height of 40dip. 54 * 55 * The preferred way to perform the inflation of the layout resource is the following: 56 * 57 * <pre> 58 * ViewStub stub = (ViewStub) findViewById(R.id.stub); 59 * View inflated = stub.inflate(); 60 * </pre> 61 * 62 * When {@link #inflate()} is invoked, the ViewStub is replaced by the inflated View 63 * and the inflated View is returned. This lets applications get a reference to the 64 * inflated View without executing an extra findViewById(). 65 * 66 * @attr ref android.R.styleable#ViewStub_inflatedId 67 * @attr ref android.R.styleable#ViewStub_layout 68 */ 69 public final class ViewStub extends View { 70 private int mLayoutResource = 0; 71 private int mInflatedId; 72 73 private WeakReference<View> mInflatedViewRef; 74 75 private OnInflateListener mInflateListener; 76 ViewStub(Context context)77 public ViewStub(Context context) { 78 initialize(context); 79 } 80 81 /** 82 * Creates a new ViewStub with the specified layout resource. 83 * 84 * @param context The application's environment. 85 * @param layoutResource The reference to a layout resource that will be inflated. 86 */ ViewStub(Context context, int layoutResource)87 public ViewStub(Context context, int layoutResource) { 88 mLayoutResource = layoutResource; 89 initialize(context); 90 } 91 ViewStub(Context context, AttributeSet attrs)92 public ViewStub(Context context, AttributeSet attrs) { 93 this(context, attrs, 0); 94 } 95 96 @SuppressWarnings({"UnusedDeclaration"}) ViewStub(Context context, AttributeSet attrs, int defStyle)97 public ViewStub(Context context, AttributeSet attrs, int defStyle) { 98 TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.ViewStub, 99 defStyle, 0); 100 101 mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID); 102 mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0); 103 104 a.recycle(); 105 106 a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.View, defStyle, 0); 107 mID = a.getResourceId(R.styleable.View_id, NO_ID); 108 a.recycle(); 109 110 initialize(context); 111 } 112 initialize(Context context)113 private void initialize(Context context) { 114 mContext = context; 115 setVisibility(GONE); 116 setWillNotDraw(true); 117 } 118 119 /** 120 * Returns the id taken by the inflated view. If the inflated id is 121 * {@link View#NO_ID}, the inflated view keeps its original id. 122 * 123 * @return A positive integer used to identify the inflated view or 124 * {@link #NO_ID} if the inflated view should keep its id. 125 * 126 * @see #setInflatedId(int) 127 * @attr ref android.R.styleable#ViewStub_inflatedId 128 */ getInflatedId()129 public int getInflatedId() { 130 return mInflatedId; 131 } 132 133 /** 134 * Defines the id taken by the inflated view. If the inflated id is 135 * {@link View#NO_ID}, the inflated view keeps its original id. 136 * 137 * @param inflatedId A positive integer used to identify the inflated view or 138 * {@link #NO_ID} if the inflated view should keep its id. 139 * 140 * @see #getInflatedId() 141 * @attr ref android.R.styleable#ViewStub_inflatedId 142 */ setInflatedId(int inflatedId)143 public void setInflatedId(int inflatedId) { 144 mInflatedId = inflatedId; 145 } 146 147 /** 148 * Returns the layout resource that will be used by {@link #setVisibility(int)} or 149 * {@link #inflate()} to replace this StubbedView 150 * in its parent by another view. 151 * 152 * @return The layout resource identifier used to inflate the new View. 153 * 154 * @see #setLayoutResource(int) 155 * @see #setVisibility(int) 156 * @see #inflate() 157 * @attr ref android.R.styleable#ViewStub_layout 158 */ getLayoutResource()159 public int getLayoutResource() { 160 return mLayoutResource; 161 } 162 163 /** 164 * Specifies the layout resource to inflate when this StubbedView becomes visible or invisible 165 * or when {@link #inflate()} is invoked. The View created by inflating the layout resource is 166 * used to replace this StubbedView in its parent. 167 * 168 * @param layoutResource A valid layout resource identifier (different from 0.) 169 * 170 * @see #getLayoutResource() 171 * @see #setVisibility(int) 172 * @see #inflate() 173 * @attr ref android.R.styleable#ViewStub_layout 174 */ setLayoutResource(int layoutResource)175 public void setLayoutResource(int layoutResource) { 176 mLayoutResource = layoutResource; 177 } 178 179 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)180 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 181 setMeasuredDimension(0, 0); 182 } 183 184 @Override draw(Canvas canvas)185 public void draw(Canvas canvas) { 186 } 187 188 @Override dispatchDraw(Canvas canvas)189 protected void dispatchDraw(Canvas canvas) { 190 } 191 192 /** 193 * When visibility is set to {@link #VISIBLE} or {@link #INVISIBLE}, 194 * {@link #inflate()} is invoked and this StubbedView is replaced in its parent 195 * by the inflated layout resource. 196 * 197 * @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}. 198 * 199 * @see #inflate() 200 */ 201 @Override setVisibility(int visibility)202 public void setVisibility(int visibility) { 203 if (mInflatedViewRef != null) { 204 View view = mInflatedViewRef.get(); 205 if (view != null) { 206 view.setVisibility(visibility); 207 } else { 208 throw new IllegalStateException("setVisibility called on un-referenced view"); 209 } 210 } else { 211 super.setVisibility(visibility); 212 if (visibility == VISIBLE || visibility == INVISIBLE) { 213 inflate(); 214 } 215 } 216 } 217 218 /** 219 * Inflates the layout resource identified by {@link #getLayoutResource()} 220 * and replaces this StubbedView in its parent by the inflated layout resource. 221 * 222 * @return The inflated layout resource. 223 * 224 */ inflate()225 public View inflate() { 226 final ViewParent viewParent = getParent(); 227 228 if (viewParent != null && viewParent instanceof ViewGroup) { 229 if (mLayoutResource != 0) { 230 final ViewGroup parent = (ViewGroup) viewParent; 231 final LayoutInflater factory = LayoutInflater.from(mContext); 232 final View view = factory.inflate(mLayoutResource, parent, 233 false); 234 235 if (mInflatedId != NO_ID) { 236 view.setId(mInflatedId); 237 } 238 239 final int index = parent.indexOfChild(this); 240 parent.removeViewInLayout(this); 241 242 final ViewGroup.LayoutParams layoutParams = getLayoutParams(); 243 if (layoutParams != null) { 244 parent.addView(view, index, layoutParams); 245 } else { 246 parent.addView(view, index); 247 } 248 249 mInflatedViewRef = new WeakReference<View>(view); 250 251 if (mInflateListener != null) { 252 mInflateListener.onInflate(this, view); 253 } 254 255 return view; 256 } else { 257 throw new IllegalArgumentException("ViewStub must have a valid layoutResource"); 258 } 259 } else { 260 throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent"); 261 } 262 } 263 264 /** 265 * Specifies the inflate listener to be notified after this ViewStub successfully 266 * inflated its layout resource. 267 * 268 * @param inflateListener The OnInflateListener to notify of successful inflation. 269 * 270 * @see android.view.ViewStub.OnInflateListener 271 */ setOnInflateListener(OnInflateListener inflateListener)272 public void setOnInflateListener(OnInflateListener inflateListener) { 273 mInflateListener = inflateListener; 274 } 275 276 /** 277 * Listener used to receive a notification after a ViewStub has successfully 278 * inflated its layout resource. 279 * 280 * @see android.view.ViewStub#setOnInflateListener(android.view.ViewStub.OnInflateListener) 281 */ 282 public static interface OnInflateListener { 283 /** 284 * Invoked after a ViewStub successfully inflated its layout resource. 285 * This method is invoked after the inflated view was added to the 286 * hierarchy but before the layout pass. 287 * 288 * @param stub The ViewStub that initiated the inflation. 289 * @param inflated The inflated View. 290 */ onInflate(ViewStub stub, View inflated)291 void onInflate(ViewStub stub, View inflated); 292 } 293 } 294