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