/* * Copyright (C) 2014 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.support.v7.internal.widget; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.support.v7.appcompat.R; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import java.lang.ref.WeakReference; /** * Backport of {@link android.view.ViewStub} so that we can set the * {@link android.view.LayoutInflater} on devices before Jelly Bean. * * @hide */ public final class ViewStubCompat extends View { private int mLayoutResource = 0; private int mInflatedId; private WeakReference mInflatedViewRef; private LayoutInflater mInflater; private OnInflateListener mInflateListener; public ViewStubCompat(Context context, AttributeSet attrs) { this(context, attrs, 0); } public ViewStubCompat(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewStubCompat, defStyle, 0); mInflatedId = a.getResourceId(R.styleable.ViewStubCompat_android_inflatedId, NO_ID); mLayoutResource = a.getResourceId(R.styleable.ViewStubCompat_android_layout, 0); setId(a.getResourceId(R.styleable.ViewStubCompat_android_id, NO_ID)); a.recycle(); 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 */ 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 */ 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 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); 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(ViewStubCompat stub, View inflated); } }