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.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 
     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      */
     90     public ViewStub(Context context, int layoutResource) {
     91         mLayoutResource = layoutResource;
     92         initialize(context);
     93     }
     94 
     95     public ViewStub(Context context, AttributeSet attrs) {
     96         this(context, attrs, 0);
     97     }
     98 
     99     @SuppressWarnings({"UnusedDeclaration"})
    100     public ViewStub(Context context, AttributeSet attrs, int defStyle) {
    101         TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.ViewStub,
    102                 defStyle, 0);
    103 
    104         mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
    105         mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);
    106 
    107         a.recycle();
    108 
    109         a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.View, defStyle, 0);
    110         mID = a.getResourceId(R.styleable.View_id, NO_ID);
    111         a.recycle();
    112 
    113         initialize(context);
    114     }
    115 
    116     private void initialize(Context context) {
    117         mContext = context;
    118         setVisibility(GONE);
    119         setWillNotDraw(true);
    120     }
    121 
    122     /**
    123      * Returns the id taken by the inflated view. If the inflated id is
    124      * {@link View#NO_ID}, the inflated view keeps its original id.
    125      *
    126      * @return A positive integer used to identify the inflated view or
    127      *         {@link #NO_ID} if the inflated view should keep its id.
    128      *
    129      * @see #setInflatedId(int)
    130      * @attr ref android.R.styleable#ViewStub_inflatedId
    131      */
    132     public int getInflatedId() {
    133         return mInflatedId;
    134     }
    135 
    136     /**
    137      * Defines the id taken by the inflated view. If the inflated id is
    138      * {@link View#NO_ID}, the inflated view keeps its original id.
    139      *
    140      * @param inflatedId A positive integer used to identify the inflated view or
    141      *                   {@link #NO_ID} if the inflated view should keep its id.
    142      *
    143      * @see #getInflatedId()
    144      * @attr ref android.R.styleable#ViewStub_inflatedId
    145      */
    146     @android.view.RemotableViewMethod
    147     public void setInflatedId(int inflatedId) {
    148         mInflatedId = inflatedId;
    149     }
    150 
    151     /**
    152      * Returns the layout resource that will be used by {@link #setVisibility(int)} or
    153      * {@link #inflate()} to replace this StubbedView
    154      * in its parent by another view.
    155      *
    156      * @return The layout resource identifier used to inflate the new View.
    157      *
    158      * @see #setLayoutResource(int)
    159      * @see #setVisibility(int)
    160      * @see #inflate()
    161      * @attr ref android.R.styleable#ViewStub_layout
    162      */
    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(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