Home | History | Annotate | Download | only in view
      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.annotation.IdRes;
     20 import android.annotation.LayoutRes;
     21 import android.content.Context;
     22 import android.content.res.TypedArray;
     23 import android.graphics.Canvas;
     24 import android.util.AttributeSet;
     25 import android.widget.RemoteViews.RemoteView;
     26 
     27 import com.android.internal.R;
     28 
     29 import java.lang.ref.WeakReference;
     30 
     31 /**
     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>
     61  *     ViewStub stub = (ViewStub) findViewById(R.id.stub);
     62  *     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  */
     72 @RemoteView
     73 public final class ViewStub extends View {
     74     private int mInflatedId;
     75     private int mLayoutResource;
     76 
     77     private WeakReference<View> mInflatedViewRef;
     78 
     79     private LayoutInflater mInflater;
     80     private OnInflateListener mInflateListener;
     81 
     82     public ViewStub(Context context) {
     83         this(context, 0);
     84     }
     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      */
     92     public ViewStub(Context context, @LayoutRes int layoutResource) {
     93         this(context, null);
     94 
     95         mLayoutResource = layoutResource;
     96     }
     97 
     98     public ViewStub(Context context, AttributeSet attrs) {
     99         this(context, attrs, 0);
    100     }
    101 
    102     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) {
    107         super(context);
    108 
    109         final TypedArray a = context.obtainStyledAttributes(attrs,
    110                 R.styleable.ViewStub, defStyleAttr, defStyleRes);
    111         mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
    112         mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);
    113         mID = a.getResourceId(R.styleable.ViewStub_id, NO_ID);
    114         a.recycle();
    115 
    116         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      */
    130     @IdRes
    131     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      */
    145     @android.view.RemotableViewMethod
    146     public void setInflatedId(@IdRes int inflatedId) {
    147         mInflatedId = inflatedId;
    148     }
    149 
    150     /**
    151      * Returns the layout resource that will be used by {@link #setVisibility(int)} or
    152      * {@link #inflate()} to replace this StubbedView
    153      * in its parent by another view.
    154      *
    155      * @return The layout resource identifier used to inflate the new View.
    156      *
    157      * @see #setLayoutResource(int)
    158      * @see #setVisibility(int)
    159      * @see #inflate()
    160      * @attr ref android.R.styleable#ViewStub_layout
    161      */
    162     @LayoutRes
    163     public int getLayoutResource() {
    164         return mLayoutResource;
    165     }
    166 
    167     /**
    168      * Specifies the layout resource to inflate when this StubbedView becomes visible or invisible
    169      * or when {@link #inflate()} is invoked. The View created by inflating the layout resource is
    170      * used to replace this StubbedView in its parent.
    171      *
    172      * @param layoutResource A valid layout resource identifier (different from 0.)
    173      *
    174      * @see #getLayoutResource()
    175      * @see #setVisibility(int)
    176      * @see #inflate()
    177      * @attr ref android.R.styleable#ViewStub_layout
    178      */
    179     @android.view.RemotableViewMethod
    180     public void setLayoutResource(@LayoutRes int layoutResource) {
    181         mLayoutResource = layoutResource;
    182     }
    183 
    184     /**
    185      * Set {@link LayoutInflater} to use in {@link #inflate()}, or {@code null}
    186      * to use the default.
    187      */
    188     public void setLayoutInflater(LayoutInflater inflater) {
    189         mInflater = inflater;
    190     }
    191 
    192     /**
    193      * Get current {@link LayoutInflater} used in {@link #inflate()}.
    194      */
    195     public LayoutInflater getLayoutInflater() {
    196         return mInflater;
    197     }
    198 
    199     @Override
    200     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    201         setMeasuredDimension(0, 0);
    202     }
    203 
    204     @Override
    205     public void draw(Canvas canvas) {
    206     }
    207 
    208     @Override
    209     protected void dispatchDraw(Canvas canvas) {
    210     }
    211 
    212     /**
    213      * When visibility is set to {@link #VISIBLE} or {@link #INVISIBLE},
    214      * {@link #inflate()} is invoked and this StubbedView is replaced in its parent
    215      * by the inflated layout resource. After that calls to this function are passed
    216      * through to the inflated view.
    217      *
    218      * @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
    219      *
    220      * @see #inflate()
    221      */
    222     @Override
    223     @android.view.RemotableViewMethod
    224     public void setVisibility(int visibility) {
    225         if (mInflatedViewRef != null) {
    226             View view = mInflatedViewRef.get();
    227             if (view != null) {
    228                 view.setVisibility(visibility);
    229             } else {
    230                 throw new IllegalStateException("setVisibility called on un-referenced view");
    231             }
    232         } else {
    233             super.setVisibility(visibility);
    234             if (visibility == VISIBLE || visibility == INVISIBLE) {
    235                 inflate();
    236             }
    237         }
    238     }
    239 
    240     /**
    241      * Inflates the layout resource identified by {@link #getLayoutResource()}
    242      * and replaces this StubbedView in its parent by the inflated layout resource.
    243      *
    244      * @return The inflated layout resource.
    245      *
    246      */
    247     public View inflate() {
    248         final ViewParent viewParent = getParent();
    249 
    250         if (viewParent != null && viewParent instanceof ViewGroup) {
    251             if (mLayoutResource != 0) {
    252                 final ViewGroup parent = (ViewGroup) viewParent;
    253                 final LayoutInflater factory;
    254                 if (mInflater != null) {
    255                     factory = mInflater;
    256                 } else {
    257                     factory = LayoutInflater.from(mContext);
    258                 }
    259                 final View view = factory.inflate(mLayoutResource, parent,
    260                         false);
    261 
    262                 if (mInflatedId != NO_ID) {
    263                     view.setId(mInflatedId);
    264                 }
    265 
    266                 final int index = parent.indexOfChild(this);
    267                 parent.removeViewInLayout(this);
    268 
    269                 final ViewGroup.LayoutParams layoutParams = getLayoutParams();
    270                 if (layoutParams != null) {
    271                     parent.addView(view, index, layoutParams);
    272                 } else {
    273                     parent.addView(view, index);
    274                 }
    275 
    276                 mInflatedViewRef = new WeakReference<View>(view);
    277 
    278                 if (mInflateListener != null) {
    279                     mInflateListener.onInflate(this, view);
    280                 }
    281 
    282                 return view;
    283             } else {
    284                 throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
    285             }
    286         } else {
    287             throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
    288         }
    289     }
    290 
    291     /**
    292      * Specifies the inflate listener to be notified after this ViewStub successfully
    293      * inflated its layout resource.
    294      *
    295      * @param inflateListener The OnInflateListener to notify of successful inflation.
    296      *
    297      * @see android.view.ViewStub.OnInflateListener
    298      */
    299     public void setOnInflateListener(OnInflateListener inflateListener) {
    300         mInflateListener = inflateListener;
    301     }
    302 
    303     /**
    304      * Listener used to receive a notification after a ViewStub has successfully
    305      * inflated its layout resource.
    306      *
    307      * @see android.view.ViewStub#setOnInflateListener(android.view.ViewStub.OnInflateListener)
    308      */
    309     public static interface OnInflateListener {
    310         /**
    311          * Invoked after a ViewStub successfully inflated its layout resource.
    312          * This method is invoked after the inflated view was added to the
    313          * hierarchy but before the layout pass.
    314          *
    315          * @param stub The ViewStub that initiated the inflation.
    316          * @param inflated The inflated View.
    317          */
    318         void onInflate(ViewStub stub, View inflated);
    319     }
    320 }
    321