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 
     24 import com.android.internal.R;
     25 
     26 import 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  */
     69 public 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 {
    211             super.setVisibility(visibility);
    212             if (visibility == VISIBLE || visibility == INVISIBLE) {
    213                 inflate();
    214             }
    215         }
    216     }
    217 
    218     /**
    219      * Inflates the layout resource identified by {@link #getLayoutResource()}
    220      * and replaces this StubbedView in its parent by the inflated layout resource.
    221      *
    222      * @return The inflated layout resource.
    223      *
    224      */
    225     public View inflate() {
    226         final ViewParent viewParent = getParent();
    227 
    228         if (viewParent != null && viewParent instanceof ViewGroup) {
    229             if (mLayoutResource != 0) {
    230                 final ViewGroup parent = (ViewGroup) viewParent;
    231                 final LayoutInflater factory = LayoutInflater.from(mContext);
    232                 final View view = factory.inflate(mLayoutResource, parent,
    233                         false);
    234 
    235                 if (mInflatedId != NO_ID) {
    236                     view.setId(mInflatedId);
    237                 }
    238 
    239                 final int index = parent.indexOfChild(this);
    240                 parent.removeViewInLayout(this);
    241 
    242                 final ViewGroup.LayoutParams layoutParams = getLayoutParams();
    243                 if (layoutParams != null) {
    244                     parent.addView(view, index, layoutParams);
    245                 } else {
    246                     parent.addView(view, index);
    247                 }
    248 
    249                 mInflatedViewRef = new WeakReference<View>(view);
    250 
    251                 if (mInflateListener != null) {
    252                     mInflateListener.onInflate(this, view);
    253                 }
    254 
    255                 return view;
    256             } else {
    257                 throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
    258             }
    259         } else {
    260             throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
    261         }
    262     }
    263 
    264     /**
    265      * Specifies the inflate listener to be notified after this ViewStub successfully
    266      * inflated its layout resource.
    267      *
    268      * @param inflateListener The OnInflateListener to notify of successful inflation.
    269      *
    270      * @see android.view.ViewStub.OnInflateListener
    271      */
    272     public void setOnInflateListener(OnInflateListener inflateListener) {
    273         mInflateListener = inflateListener;
    274     }
    275 
    276     /**
    277      * Listener used to receive a notification after a ViewStub has successfully
    278      * inflated its layout resource.
    279      *
    280      * @see android.view.ViewStub#setOnInflateListener(android.view.ViewStub.OnInflateListener)
    281      */
    282     public static interface OnInflateListener {
    283         /**
    284          * Invoked after a ViewStub successfully inflated its layout resource.
    285          * This method is invoked after the inflated view was added to the
    286          * hierarchy but before the layout pass.
    287          *
    288          * @param stub The ViewStub that initiated the inflation.
    289          * @param inflated The inflated View.
    290          */
    291         void onInflate(ViewStub stub, View inflated);
    292     }
    293 }
    294