ViewStub.java revision b6af533114cfa5a4547990e79e96bd0402c47e72
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
17package android.view;
18
19import android.content.Context;
20import android.content.res.TypedArray;
21import android.graphics.Canvas;
22import android.util.AttributeSet;
23
24import com.android.internal.R;
25
26import 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 *     &lt;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" /&gt;
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 */
69public 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
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     */
87    public ViewStub(Context context, int layoutResource) {
88        mLayoutResource = layoutResource;
89        initialize(context);
90    }
91
92    public ViewStub(Context context, AttributeSet attrs) {
93        this(context, attrs, 0);
94    }
95
96    @SuppressWarnings({"UnusedDeclaration"})
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
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     */
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     */
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     */
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     */
175    public void setLayoutResource(int layoutResource) {
176        mLayoutResource = layoutResource;
177    }
178
179    @Override
180    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
181        setMeasuredDimension(0, 0);
182    }
183
184    @Override
185    public void draw(Canvas canvas) {
186    }
187
188    @Override
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
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 if (visibility == VISIBLE || visibility == INVISIBLE) {
211            super.setVisibility(visibility);
212            inflate();
213        }
214    }
215
216    /**
217     * Inflates the layout resource identified by {@link #getLayoutResource()}
218     * and replaces this StubbedView in its parent by the inflated layout resource.
219     *
220     * @return The inflated layout resource.
221     *
222     */
223    public View inflate() {
224        final ViewParent viewParent = getParent();
225
226        if (viewParent != null && viewParent instanceof ViewGroup) {
227            if (mLayoutResource != 0) {
228                final ViewGroup parent = (ViewGroup) viewParent;
229                final LayoutInflater factory = LayoutInflater.from(mContext);
230                final View view = factory.inflate(mLayoutResource, parent,
231                        false);
232
233                if (mInflatedId != NO_ID) {
234                    view.setId(mInflatedId);
235                }
236
237                final int index = parent.indexOfChild(this);
238                parent.removeViewInLayout(this);
239
240                final ViewGroup.LayoutParams layoutParams = getLayoutParams();
241                if (layoutParams != null) {
242                    parent.addView(view, index, layoutParams);
243                } else {
244                    parent.addView(view, index);
245                }
246
247                mInflatedViewRef = new WeakReference(view);
248
249                if (mInflateListener != null) {
250                    mInflateListener.onInflate(this, view);
251                }
252
253                return view;
254            } else {
255                throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
256            }
257        } else {
258            throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
259        }
260    }
261
262    /**
263     * Specifies the inflate listener to be notified after this ViewStub successfully
264     * inflated its layout resource.
265     *
266     * @param inflateListener The OnInflateListener to notify of successful inflation.
267     *
268     * @see android.view.ViewStub.OnInflateListener
269     */
270    public void setOnInflateListener(OnInflateListener inflateListener) {
271        mInflateListener = inflateListener;
272    }
273
274    /**
275     * Listener used to receive a notification after a ViewStub has successfully
276     * inflated its layout resource.
277     *
278     * @see android.view.ViewStub#setOnInflateListener(android.view.ViewStub.OnInflateListener)
279     */
280    public static interface OnInflateListener {
281        /**
282         * Invoked after a ViewStub successfully inflated its layout resource.
283         * This method is invoked after the inflated view was added to the
284         * hierarchy but before the layout pass.
285         *
286         * @param stub The ViewStub that initiated the inflation.
287         * @param inflated The inflated View.
288         */
289        void onInflate(ViewStub stub, View inflated);
290    }
291}
292