blob: e9d1b8784914b3610c82578dac875cde1b701ecb [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
17package android.view;
18
Tor Norbye7b9c9122013-05-30 16:48:33 -070019import android.annotation.IdRes;
20import android.annotation.LayoutRes;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.content.Context;
22import android.content.res.TypedArray;
23import android.graphics.Canvas;
24import android.util.AttributeSet;
Jeff Sharkeyb27b7a12012-04-02 21:07:29 -070025import android.widget.RemoteViews.RemoteView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026
27import com.android.internal.R;
28
Mihai Predab6af5332009-04-28 14:21:57 +020029import java.lang.ref.WeakReference;
30
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031/**
32 * A ViewStub is an invisible, zero-sized View that can be used to lazily inflate
33 * layout resources at runtime.
34 *
35 * When a ViewStub is made visible, or when {@link #inflate()} is invoked, the layout resource
36 * is inflated. The ViewStub then replaces itself in its parent with the inflated View or Views.
37 * Therefore, the ViewStub exists in the view hierarchy until {@link #setVisibility(int)} or
38 * {@link #inflate()} is invoked.
39 *
40 * The inflated View is added to the ViewStub's parent with the ViewStub's layout
41 * parameters. Similarly, you can define/override the inflate View's id by using the
42 * ViewStub's inflatedId property. For instance:
43 *
44 * <pre>
45 * &lt;ViewStub android:id="@+id/stub"
46 * android:inflatedId="@+id/subTree"
47 * android:layout="@layout/mySubTree"
48 * android:layout_width="120dip"
49 * android:layout_height="40dip" /&gt;
50 * </pre>
51 *
52 * The ViewStub thus defined can be found using the id "stub." After inflation of
53 * the layout resource "mySubTree," the ViewStub is removed from its parent. The
54 * View created by inflating the layout resource "mySubTree" can be found using the
55 * id "subTree," specified by the inflatedId property. The inflated View is finally
56 * assigned a width of 120dip and a height of 40dip.
57 *
58 * The preferred way to perform the inflation of the layout resource is the following:
59 *
60 * <pre>
Alan Viverette51efddb2017-04-05 10:00:01 -040061 * ViewStub stub = findViewById(R.id.stub);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062 * View inflated = stub.inflate();
63 * </pre>
64 *
65 * When {@link #inflate()} is invoked, the ViewStub is replaced by the inflated View
66 * and the inflated View is returned. This lets applications get a reference to the
67 * inflated View without executing an extra findViewById().
68 *
69 * @attr ref android.R.styleable#ViewStub_inflatedId
70 * @attr ref android.R.styleable#ViewStub_layout
71 */
Jeff Sharkeyb27b7a12012-04-02 21:07:29 -070072@RemoteView
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073public final class ViewStub extends View {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074 private int mInflatedId;
Alan Viverette39a72dd2014-12-15 11:15:49 -080075 private int mLayoutResource;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076
Mihai Predab6af5332009-04-28 14:21:57 +020077 private WeakReference<View> mInflatedViewRef;
78
Jeff Sharkeyb27b7a12012-04-02 21:07:29 -070079 private LayoutInflater mInflater;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080 private OnInflateListener mInflateListener;
81
82 public ViewStub(Context context) {
Alan Viverette39a72dd2014-12-15 11:15:49 -080083 this(context, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 }
85
86 /**
87 * Creates a new ViewStub with the specified layout resource.
88 *
89 * @param context The application's environment.
90 * @param layoutResource The reference to a layout resource that will be inflated.
91 */
Tor Norbye7b9c9122013-05-30 16:48:33 -070092 public ViewStub(Context context, @LayoutRes int layoutResource) {
Alan Viverette39a72dd2014-12-15 11:15:49 -080093 this(context, null);
94
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 mLayoutResource = layoutResource;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 }
97
98 public ViewStub(Context context, AttributeSet attrs) {
99 this(context, attrs, 0);
100 }
101
Alan Viverette617feb92013-09-09 18:09:13 -0700102 public ViewStub(Context context, AttributeSet attrs, int defStyleAttr) {
103 this(context, attrs, defStyleAttr, 0);
104 }
105
106 public ViewStub(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
Alan Viverette39a72dd2014-12-15 11:15:49 -0800107 super(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108
Alan Viverette39a72dd2014-12-15 11:15:49 -0800109 final TypedArray a = context.obtainStyledAttributes(attrs,
110 R.styleable.ViewStub, defStyleAttr, defStyleRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
112 mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);
Alan Viverette39a72dd2014-12-15 11:15:49 -0800113 mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 a.recycle();
115
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 setVisibility(GONE);
117 setWillNotDraw(true);
118 }
119
120 /**
121 * Returns the id taken by the inflated view. If the inflated id is
122 * {@link View#NO_ID}, the inflated view keeps its original id.
123 *
124 * @return A positive integer used to identify the inflated view or
125 * {@link #NO_ID} if the inflated view should keep its id.
126 *
127 * @see #setInflatedId(int)
128 * @attr ref android.R.styleable#ViewStub_inflatedId
129 */
Tor Norbye7b9c9122013-05-30 16:48:33 -0700130 @IdRes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131 public int getInflatedId() {
132 return mInflatedId;
133 }
134
135 /**
136 * Defines the id taken by the inflated view. If the inflated id is
137 * {@link View#NO_ID}, the inflated view keeps its original id.
138 *
139 * @param inflatedId A positive integer used to identify the inflated view or
140 * {@link #NO_ID} if the inflated view should keep its id.
141 *
142 * @see #getInflatedId()
143 * @attr ref android.R.styleable#ViewStub_inflatedId
144 */
Sunny Goyal7b0e2c72016-11-03 14:48:05 -0700145 @android.view.RemotableViewMethod(asyncImpl = "setInflatedIdAsync")
Tor Norbye7b9c9122013-05-30 16:48:33 -0700146 public void setInflatedId(@IdRes int inflatedId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147 mInflatedId = inflatedId;
148 }
149
Sunny Goyal7b0e2c72016-11-03 14:48:05 -0700150 /** @hide **/
151 public Runnable setInflatedIdAsync(@IdRes int inflatedId) {
152 mInflatedId = inflatedId;
153 return null;
154 }
155
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156 /**
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 */
Tor Norbye7b9c9122013-05-30 16:48:33 -0700168 @LayoutRes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 public int getLayoutResource() {
170 return mLayoutResource;
171 }
172
173 /**
174 * Specifies the layout resource to inflate when this StubbedView becomes visible or invisible
175 * or when {@link #inflate()} is invoked. The View created by inflating the layout resource is
176 * used to replace this StubbedView in its parent.
177 *
178 * @param layoutResource A valid layout resource identifier (different from 0.)
179 *
180 * @see #getLayoutResource()
181 * @see #setVisibility(int)
182 * @see #inflate()
183 * @attr ref android.R.styleable#ViewStub_layout
184 */
Sunny Goyal7b0e2c72016-11-03 14:48:05 -0700185 @android.view.RemotableViewMethod(asyncImpl = "setLayoutResourceAsync")
Tor Norbye7b9c9122013-05-30 16:48:33 -0700186 public void setLayoutResource(@LayoutRes int layoutResource) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187 mLayoutResource = layoutResource;
188 }
189
Sunny Goyal7b0e2c72016-11-03 14:48:05 -0700190 /** @hide **/
191 public Runnable setLayoutResourceAsync(@LayoutRes int layoutResource) {
192 mLayoutResource = layoutResource;
193 return null;
194 }
195
Jeff Sharkeyb27b7a12012-04-02 21:07:29 -0700196 /**
197 * Set {@link LayoutInflater} to use in {@link #inflate()}, or {@code null}
198 * to use the default.
199 */
200 public void setLayoutInflater(LayoutInflater inflater) {
201 mInflater = inflater;
202 }
203
204 /**
205 * Get current {@link LayoutInflater} used in {@link #inflate()}.
206 */
207 public LayoutInflater getLayoutInflater() {
208 return mInflater;
209 }
210
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 @Override
212 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
213 setMeasuredDimension(0, 0);
214 }
215
216 @Override
217 public void draw(Canvas canvas) {
218 }
219
220 @Override
221 protected void dispatchDraw(Canvas canvas) {
222 }
223
224 /**
225 * When visibility is set to {@link #VISIBLE} or {@link #INVISIBLE},
226 * {@link #inflate()} is invoked and this StubbedView is replaced in its parent
Tim Huttf54df842012-11-02 15:13:09 +0000227 * by the inflated layout resource. After that calls to this function are passed
228 * through to the inflated view.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229 *
230 * @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
231 *
232 * @see #inflate()
233 */
234 @Override
Sunny Goyal7b0e2c72016-11-03 14:48:05 -0700235 @android.view.RemotableViewMethod(asyncImpl = "setVisibilityAsync")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236 public void setVisibility(int visibility) {
Mihai Predab6af5332009-04-28 14:21:57 +0200237 if (mInflatedViewRef != null) {
238 View view = mInflatedViewRef.get();
239 if (view != null) {
240 view.setVisibility(visibility);
241 } else {
242 throw new IllegalStateException("setVisibility called on un-referenced view");
243 }
Adam Powell58acbaf2009-12-15 11:43:34 -0800244 } else {
Mihai Predab6af5332009-04-28 14:21:57 +0200245 super.setVisibility(visibility);
Adam Powell58acbaf2009-12-15 11:43:34 -0800246 if (visibility == VISIBLE || visibility == INVISIBLE) {
247 inflate();
248 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 }
250 }
251
Sunny Goyal7b0e2c72016-11-03 14:48:05 -0700252 /** @hide **/
253 public Runnable setVisibilityAsync(int visibility) {
254 if (visibility == VISIBLE || visibility == INVISIBLE) {
255 ViewGroup parent = (ViewGroup) getParent();
256 return new ViewReplaceRunnable(inflateViewNoAdd(parent));
257 } else {
258 return null;
259 }
260 }
261
262 private View inflateViewNoAdd(ViewGroup parent) {
263 final LayoutInflater factory;
264 if (mInflater != null) {
265 factory = mInflater;
266 } else {
267 factory = LayoutInflater.from(mContext);
268 }
269 final View view = factory.inflate(mLayoutResource, parent, false);
270
271 if (mInflatedId != NO_ID) {
272 view.setId(mInflatedId);
273 }
274 return view;
275 }
276
277 private void replaceSelfWithView(View view, ViewGroup parent) {
278 final int index = parent.indexOfChild(this);
279 parent.removeViewInLayout(this);
280
281 final ViewGroup.LayoutParams layoutParams = getLayoutParams();
282 if (layoutParams != null) {
283 parent.addView(view, index, layoutParams);
284 } else {
285 parent.addView(view, index);
286 }
287 }
288
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 /**
290 * Inflates the layout resource identified by {@link #getLayoutResource()}
291 * and replaces this StubbedView in its parent by the inflated layout resource.
292 *
293 * @return The inflated layout resource.
294 *
295 */
296 public View inflate() {
297 final ViewParent viewParent = getParent();
298
299 if (viewParent != null && viewParent instanceof ViewGroup) {
300 if (mLayoutResource != 0) {
301 final ViewGroup parent = (ViewGroup) viewParent;
Sunny Goyal7b0e2c72016-11-03 14:48:05 -0700302 final View view = inflateViewNoAdd(parent);
303 replaceSelfWithView(view, parent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304
Sunny Goyal7b0e2c72016-11-03 14:48:05 -0700305 mInflatedViewRef = new WeakReference<>(view);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306 if (mInflateListener != null) {
307 mInflateListener.onInflate(this, view);
308 }
309
310 return view;
311 } else {
312 throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
313 }
314 } else {
315 throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
316 }
317 }
318
319 /**
320 * Specifies the inflate listener to be notified after this ViewStub successfully
321 * inflated its layout resource.
322 *
323 * @param inflateListener The OnInflateListener to notify of successful inflation.
324 *
325 * @see android.view.ViewStub.OnInflateListener
326 */
327 public void setOnInflateListener(OnInflateListener inflateListener) {
328 mInflateListener = inflateListener;
329 }
330
331 /**
332 * Listener used to receive a notification after a ViewStub has successfully
333 * inflated its layout resource.
334 *
335 * @see android.view.ViewStub#setOnInflateListener(android.view.ViewStub.OnInflateListener)
336 */
337 public static interface OnInflateListener {
338 /**
339 * Invoked after a ViewStub successfully inflated its layout resource.
340 * This method is invoked after the inflated view was added to the
341 * hierarchy but before the layout pass.
342 *
343 * @param stub The ViewStub that initiated the inflation.
344 * @param inflated The inflated View.
345 */
346 void onInflate(ViewStub stub, View inflated);
347 }
Sunny Goyal7b0e2c72016-11-03 14:48:05 -0700348
349 /** @hide **/
350 public class ViewReplaceRunnable implements Runnable {
351 public final View view;
352
353 ViewReplaceRunnable(View view) {
354 this.view = view;
355 }
356
357 @Override
358 public void run() {
359 replaceSelfWithView(view, (ViewGroup) getParent());
360 }
361 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362}