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 defStyleAttr) {
    101         this(context, attrs, defStyleAttr, 0);
    102     }
    103 
    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 
    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      */
    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
    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      */
    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
    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      */
    193     public void setLayoutInflater(LayoutInflater inflater) {
    194         mInflater = inflater;
    195     }
    196 
    197     /**
    198      * Get current {@link LayoutInflater} used in {@link #inflate()}.
    199      */
    200     public LayoutInflater getLayoutInflater() {
    201         return mInflater;
    202     }
    203 
    204     @Override
    205     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    206         setMeasuredDimension(0, 0);
    207     }
    208 
    209     @Override
    210     public void draw(Canvas canvas) {
    211     }
    212 
    213     @Override
    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
    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      */
    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      */
    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          */
    323         void onInflate(ViewStub stub, View inflated);
    324     }
    325 }
    326