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  *     &lt;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" /&gt;
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