blob: 9ca16327844c633c0598517e8faea9eed1c3fd1c [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);
Aurimas Liutikasab324cf2019-02-07 16:46:38 -0800111 saveAttributeDataForStyleable(context, R.styleable.ViewStub, attrs, a, defStyleAttr,
112 defStyleRes);
113
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
115 mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);
Alan Viverette39a72dd2014-12-15 11:15:49 -0800116 mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 a.recycle();
118
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119 setVisibility(GONE);
120 setWillNotDraw(true);
121 }
122
123 /**
124 * Returns the id taken by the inflated view. If the inflated id is
125 * {@link View#NO_ID}, the inflated view keeps its original id.
126 *
127 * @return A positive integer used to identify the inflated view or
128 * {@link #NO_ID} if the inflated view should keep its id.
129 *
130 * @see #setInflatedId(int)
131 * @attr ref android.R.styleable#ViewStub_inflatedId
132 */
Tor Norbye7b9c9122013-05-30 16:48:33 -0700133 @IdRes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 public int getInflatedId() {
135 return mInflatedId;
136 }
137
138 /**
139 * Defines the id taken by the inflated view. If the inflated id is
140 * {@link View#NO_ID}, the inflated view keeps its original id.
141 *
142 * @param inflatedId A positive integer used to identify the inflated view or
143 * {@link #NO_ID} if the inflated view should keep its id.
144 *
145 * @see #getInflatedId()
146 * @attr ref android.R.styleable#ViewStub_inflatedId
147 */
Sunny Goyal7b0e2c72016-11-03 14:48:05 -0700148 @android.view.RemotableViewMethod(asyncImpl = "setInflatedIdAsync")
Tor Norbye7b9c9122013-05-30 16:48:33 -0700149 public void setInflatedId(@IdRes int inflatedId) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800150 mInflatedId = inflatedId;
151 }
152
Sunny Goyal7b0e2c72016-11-03 14:48:05 -0700153 /** @hide **/
154 public Runnable setInflatedIdAsync(@IdRes int inflatedId) {
155 mInflatedId = inflatedId;
156 return null;
157 }
158
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800159 /**
160 * Returns the layout resource that will be used by {@link #setVisibility(int)} or
161 * {@link #inflate()} to replace this StubbedView
162 * in its parent by another view.
163 *
164 * @return The layout resource identifier used to inflate the new View.
165 *
166 * @see #setLayoutResource(int)
167 * @see #setVisibility(int)
168 * @see #inflate()
169 * @attr ref android.R.styleable#ViewStub_layout
170 */
Tor Norbye7b9c9122013-05-30 16:48:33 -0700171 @LayoutRes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 public int getLayoutResource() {
173 return mLayoutResource;
174 }
175
176 /**
177 * Specifies the layout resource to inflate when this StubbedView becomes visible or invisible
178 * or when {@link #inflate()} is invoked. The View created by inflating the layout resource is
179 * used to replace this StubbedView in its parent.
180 *
181 * @param layoutResource A valid layout resource identifier (different from 0.)
182 *
183 * @see #getLayoutResource()
184 * @see #setVisibility(int)
185 * @see #inflate()
186 * @attr ref android.R.styleable#ViewStub_layout
187 */
Sunny Goyal7b0e2c72016-11-03 14:48:05 -0700188 @android.view.RemotableViewMethod(asyncImpl = "setLayoutResourceAsync")
Tor Norbye7b9c9122013-05-30 16:48:33 -0700189 public void setLayoutResource(@LayoutRes int layoutResource) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 mLayoutResource = layoutResource;
191 }
192
Sunny Goyal7b0e2c72016-11-03 14:48:05 -0700193 /** @hide **/
194 public Runnable setLayoutResourceAsync(@LayoutRes int layoutResource) {
195 mLayoutResource = layoutResource;
196 return null;
197 }
198
Jeff Sharkeyb27b7a12012-04-02 21:07:29 -0700199 /**
200 * Set {@link LayoutInflater} to use in {@link #inflate()}, or {@code null}
201 * to use the default.
202 */
203 public void setLayoutInflater(LayoutInflater inflater) {
204 mInflater = inflater;
205 }
206
207 /**
208 * Get current {@link LayoutInflater} used in {@link #inflate()}.
209 */
210 public LayoutInflater getLayoutInflater() {
211 return mInflater;
212 }
213
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 @Override
215 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
216 setMeasuredDimension(0, 0);
217 }
218
219 @Override
220 public void draw(Canvas canvas) {
221 }
222
223 @Override
224 protected void dispatchDraw(Canvas canvas) {
225 }
226
227 /**
228 * When visibility is set to {@link #VISIBLE} or {@link #INVISIBLE},
229 * {@link #inflate()} is invoked and this StubbedView is replaced in its parent
Tim Huttf54df842012-11-02 15:13:09 +0000230 * by the inflated layout resource. After that calls to this function are passed
231 * through to the inflated view.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800232 *
233 * @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
234 *
235 * @see #inflate()
236 */
237 @Override
Sunny Goyal7b0e2c72016-11-03 14:48:05 -0700238 @android.view.RemotableViewMethod(asyncImpl = "setVisibilityAsync")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800239 public void setVisibility(int visibility) {
Mihai Predab6af5332009-04-28 14:21:57 +0200240 if (mInflatedViewRef != null) {
241 View view = mInflatedViewRef.get();
242 if (view != null) {
243 view.setVisibility(visibility);
244 } else {
245 throw new IllegalStateException("setVisibility called on un-referenced view");
246 }
Adam Powell58acbaf2009-12-15 11:43:34 -0800247 } else {
Mihai Predab6af5332009-04-28 14:21:57 +0200248 super.setVisibility(visibility);
Adam Powell58acbaf2009-12-15 11:43:34 -0800249 if (visibility == VISIBLE || visibility == INVISIBLE) {
250 inflate();
251 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 }
253 }
254
Sunny Goyal7b0e2c72016-11-03 14:48:05 -0700255 /** @hide **/
256 public Runnable setVisibilityAsync(int visibility) {
257 if (visibility == VISIBLE || visibility == INVISIBLE) {
258 ViewGroup parent = (ViewGroup) getParent();
259 return new ViewReplaceRunnable(inflateViewNoAdd(parent));
260 } else {
261 return null;
262 }
263 }
264
265 private View inflateViewNoAdd(ViewGroup parent) {
266 final LayoutInflater factory;
267 if (mInflater != null) {
268 factory = mInflater;
269 } else {
270 factory = LayoutInflater.from(mContext);
271 }
272 final View view = factory.inflate(mLayoutResource, parent, false);
273
274 if (mInflatedId != NO_ID) {
275 view.setId(mInflatedId);
276 }
277 return view;
278 }
279
280 private void replaceSelfWithView(View view, ViewGroup parent) {
281 final int index = parent.indexOfChild(this);
282 parent.removeViewInLayout(this);
283
284 final ViewGroup.LayoutParams layoutParams = getLayoutParams();
285 if (layoutParams != null) {
286 parent.addView(view, index, layoutParams);
287 } else {
288 parent.addView(view, index);
289 }
290 }
291
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292 /**
293 * Inflates the layout resource identified by {@link #getLayoutResource()}
294 * and replaces this StubbedView in its parent by the inflated layout resource.
295 *
296 * @return The inflated layout resource.
297 *
298 */
299 public View inflate() {
300 final ViewParent viewParent = getParent();
301
302 if (viewParent != null && viewParent instanceof ViewGroup) {
303 if (mLayoutResource != 0) {
304 final ViewGroup parent = (ViewGroup) viewParent;
Sunny Goyal7b0e2c72016-11-03 14:48:05 -0700305 final View view = inflateViewNoAdd(parent);
306 replaceSelfWithView(view, parent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307
Sunny Goyal7b0e2c72016-11-03 14:48:05 -0700308 mInflatedViewRef = new WeakReference<>(view);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 if (mInflateListener != null) {
310 mInflateListener.onInflate(this, view);
311 }
312
313 return view;
314 } else {
315 throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
316 }
317 } else {
318 throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
319 }
320 }
321
322 /**
323 * Specifies the inflate listener to be notified after this ViewStub successfully
324 * inflated its layout resource.
325 *
326 * @param inflateListener The OnInflateListener to notify of successful inflation.
327 *
328 * @see android.view.ViewStub.OnInflateListener
329 */
330 public void setOnInflateListener(OnInflateListener inflateListener) {
331 mInflateListener = inflateListener;
332 }
333
334 /**
335 * Listener used to receive a notification after a ViewStub has successfully
336 * inflated its layout resource.
337 *
338 * @see android.view.ViewStub#setOnInflateListener(android.view.ViewStub.OnInflateListener)
339 */
340 public static interface OnInflateListener {
341 /**
342 * Invoked after a ViewStub successfully inflated its layout resource.
343 * This method is invoked after the inflated view was added to the
344 * hierarchy but before the layout pass.
345 *
346 * @param stub The ViewStub that initiated the inflation.
347 * @param inflated The inflated View.
348 */
349 void onInflate(ViewStub stub, View inflated);
350 }
Sunny Goyal7b0e2c72016-11-03 14:48:05 -0700351
352 /** @hide **/
353 public class ViewReplaceRunnable implements Runnable {
354 public final View view;
355
356 ViewReplaceRunnable(View view) {
357 this.view = view;
358 }
359
360 @Override
361 public void run() {
362 replaceSelfWithView(view, (ViewGroup) getParent());
363 }
364 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800365}