/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.widget; import java.lang.ref.WeakReference; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import android.widget.RemoteViews.RemoteView; /** * 改编自 android.view.ViewStub -- HouKangxi <br/> * A ViewStub is an invisible, zero-sized View that can be used to lazily * inflate layout resources at runtime. * * When a ViewStub is made visible, or when {@link #inflate()} is invoked, the * layout resource is inflated. The ViewStub then replaces itself in its parent * with the inflated View or Views. Therefore, the ViewStub exists in the view * hierarchy until {@link #setVisibility(int)} or {@link #inflate()} is invoked. * * The inflated View is added to the ViewStub's parent with the ViewStub's * layout parameters. Similarly, you can define/override the inflate View's id * by using the ViewStub's inflatedId property. For instance: * * <pre> * <ViewStub android:id="@+id/stub" * android:inflatedId="@+id/subTree" * android:layout="@layout/mySubTree" * android:layout_width="120dip" * android:layout_height="40dip" /> * </pre> * * The ViewStub thus defined can be found using the id "stub." After inflation * of the layout resource "mySubTree," the ViewStub is removed from its parent. * The View created by inflating the layout resource "mySubTree" can be found * using the id "subTree," specified by the inflatedId property. The inflated * View is finally assigned a width of 120dip and a height of 40dip. * * The preferred way to perform the inflation of the layout resource is the * following: * * <pre> * ViewStub stub = (ViewStub) findViewById(R.id.stub); * View inflated = stub.inflate(); * </pre> * * When {@link #inflate()} is invoked, the ViewStub is replaced by the inflated * View and the inflated View is returned. This lets applications get a * reference to the inflated View without executing an extra findViewById(). * * @attr ref android.R.styleable#ViewStub_inflatedId * @attr ref android.R.styleable#ViewStub_layout */ @RemoteView public final class ViewStub extends View { private int mLayoutResource = 0; private int mInflatedId; private WeakReference<View> mInflatedViewRef; private LayoutInflater mInflater; private OnInflateListener mInflateListener; public ViewStub(Context context) { super(context); initialize(context); } /** * Creates a new ViewStub with the specified layout resource. * * @param context * The application's environment. * @param layoutResource * The reference to a layout resource that will be inflated. */ public ViewStub(Context context, int layoutResource) { super(context); mLayoutResource = layoutResource; initialize(context); } public ViewStub(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ViewStub(Context context, AttributeSet attrs, int defStyle) { super(context); int[] attrsArr = null; int index = 0; int layoutId = 0; int[] attrViewArr = null; int viewId = 0; try { Class<?> styleableClass = Class .forName("com.android.internal.R$styleable"); attrsArr = (int[]) styleableClass.getField("ViewStub").get(null); index = styleableClass.getField("ViewStub_inflatedId").getInt(null); layoutId = styleableClass.getField("ViewStub_layout").getInt(null); attrViewArr = (int[]) styleableClass.getField("View").get(null); viewId = styleableClass.getField("View_id").getInt(null); } catch (Exception e) { e.printStackTrace(); } TypedArray a = context.obtainStyledAttributes(attrs, attrsArr, defStyle, 0); mInflatedId = a.getResourceId(index, NO_ID); mLayoutResource = a.getResourceId(layoutId, 0); a.recycle(); a = context.obtainStyledAttributes(attrs, attrViewArr, defStyle, 0); setId(a.getResourceId(viewId, NO_ID)); a.recycle(); initialize(context); } private void initialize(Context context) { setVisibility(GONE); setWillNotDraw(true); } /** * Returns the id taken by the inflated view. If the inflated id is * {@link View#NO_ID}, the inflated view keeps its original id. * * @return A positive integer used to identify the inflated view or * {@link #NO_ID} if the inflated view should keep its id. * * @see #setInflatedId(int) * @attr ref android.R.styleable#ViewStub_inflatedId */ public int getInflatedId() { return mInflatedId; } /** * Defines the id taken by the inflated view. If the inflated id is * {@link View#NO_ID}, the inflated view keeps its original id. * * @param inflatedId * A positive integer used to identify the inflated view or * {@link #NO_ID} if the inflated view should keep its id. * * @see #getInflatedId() * @attr ref android.R.styleable#ViewStub_inflatedId */ // @android.view.RemotableViewMethod public void setInflatedId(int inflatedId) { mInflatedId = inflatedId; } /** * Returns the layout resource that will be used by * {@link #setVisibility(int)} or {@link #inflate()} to replace this * StubbedView in its parent by another view. * * @return The layout resource identifier used to inflate the new View. * * @see #setLayoutResource(int) * @see #setVisibility(int) * @see #inflate() * @attr ref android.R.styleable#ViewStub_layout */ public int getLayoutResource() { return mLayoutResource; } /** * Specifies the layout resource to inflate when this StubbedView becomes * visible or invisible or when {@link #inflate()} is invoked. The View * created by inflating the layout resource is used to replace this * StubbedView in its parent. * * @param layoutResource * A valid layout resource identifier (different from 0.) * * @see #getLayoutResource() * @see #setVisibility(int) * @see #inflate() * @attr ref android.R.styleable#ViewStub_layout */ // @android.view.RemotableViewMethod public void setLayoutResource(int layoutResource) { mLayoutResource = layoutResource; } /** * Set {@link LayoutInflater} to use in {@link #inflate()}, or {@code null} * to use the default. */ public void setLayoutInflater(LayoutInflater inflater) { mInflater = inflater; } /** * Get current {@link LayoutInflater} used in {@link #inflate()}. */ public LayoutInflater getLayoutInflater() { return mInflater; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(0, 0); } @Override public void draw(Canvas canvas) { } @Override protected void dispatchDraw(Canvas canvas) { } /** * When visibility is set to {@link #VISIBLE} or {@link #INVISIBLE}, * {@link #inflate()} is invoked and this StubbedView is replaced in its * parent by the inflated layout resource. After that calls to this function * are passed through to the inflated view. * * @param visibility * One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}. * * @see #inflate() */ @Override // @android.view.RemotableViewMethod public void setVisibility(int visibility) { if (mInflatedViewRef != null) { View view = mInflatedViewRef.get(); if (view != null) { view.setVisibility(visibility); } else { throw new IllegalStateException( "setVisibility called on un-referenced view"); } } else { super.setVisibility(visibility); if (visibility == VISIBLE || visibility == INVISIBLE) { inflate(); } } } /** * Inflates the layout resource identified by {@link #getLayoutResource()} * and replaces this StubbedView in its parent by the inflated layout * resource. * * @return The inflated layout resource. * */ public View inflate() { final ViewParent viewParent = getParent(); if (viewParent != null && viewParent instanceof ViewGroup) { if (mLayoutResource != 0) { final ViewGroup parent = (ViewGroup) viewParent; final LayoutInflater factory; if (mInflater != null) { factory = mInflater; } else { factory = LayoutInflater.from(getContext()); } final View view = factory.inflate(mLayoutResource, parent, false); if (mInflatedId != NO_ID) { view.setId(mInflatedId); } final int index = parent.indexOfChild(this); parent.removeViewInLayout(this); final ViewGroup.LayoutParams layoutParams = getLayoutParams(); if (layoutParams != null) { parent.addView(view, index, layoutParams); } else { parent.addView(view, index); } mInflatedViewRef = new WeakReference<View>(view); if (mInflateListener != null) { mInflateListener.onInflate(this, view); } return view; } else { throw new IllegalArgumentException( "ViewStub must have a valid layoutResource"); } } else { throw new IllegalStateException( "ViewStub must have a non-null ViewGroup viewParent"); } } /** * Specifies the inflate listener to be notified after this ViewStub * successfully inflated its layout resource. * * @param inflateListener * The OnInflateListener to notify of successful inflation. * * @see android.view.ViewStub.OnInflateListener */ public void setOnInflateListener(OnInflateListener inflateListener) { mInflateListener = inflateListener; } /** * Listener used to receive a notification after a ViewStub has successfully * inflated its layout resource. * * @see android.view.ViewStub#setOnInflateListener(android.view.ViewStub.OnInflateListener) */ public static interface OnInflateListener { /** * Invoked after a ViewStub successfully inflated its layout resource. * This method is invoked after the inflated view was added to the * hierarchy but before the layout pass. * * @param stub * The ViewStub that initiated the inflation. * @param inflated * The inflated View. */ void onInflate(ViewStub stub, View inflated); } }