1/* 2 * Copyright (C) 2014 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 17package android.support.v7.internal.widget; 18 19import android.content.Context; 20import android.content.res.TypedArray; 21import android.graphics.Canvas; 22import android.support.v7.appcompat.R; 23import android.util.AttributeSet; 24import android.view.LayoutInflater; 25import android.view.View; 26import android.view.ViewGroup; 27import android.view.ViewParent; 28 29 30import java.lang.ref.WeakReference; 31 32/** 33 * Backport of {@link android.view.ViewStub} so that we can set the 34 * {@link android.view.LayoutInflater} on devices before Jelly Bean. 35 * 36 * @hide 37 */ 38public final class ViewStubCompat extends View { 39 private int mLayoutResource = 0; 40 private int mInflatedId; 41 42 private WeakReference<View> mInflatedViewRef; 43 44 private LayoutInflater mInflater; 45 private OnInflateListener mInflateListener; 46 47 public ViewStubCompat(Context context, AttributeSet attrs) { 48 this(context, attrs, 0); 49 } 50 51 public ViewStubCompat(Context context, AttributeSet attrs, int defStyle) { 52 super(context, attrs, defStyle); 53 54 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ViewStubCompat, 55 defStyle, 0); 56 57 mInflatedId = a.getResourceId(R.styleable.ViewStubCompat_android_inflatedId, NO_ID); 58 mLayoutResource = a.getResourceId(R.styleable.ViewStubCompat_android_layout, 0); 59 60 setId(a.getResourceId(R.styleable.ViewStubCompat_android_id, NO_ID)); 61 a.recycle(); 62 63 setVisibility(GONE); 64 setWillNotDraw(true); 65 } 66 67 /** 68 * Returns the id taken by the inflated view. If the inflated id is 69 * {@link View#NO_ID}, the inflated view keeps its original id. 70 * 71 * @return A positive integer used to identify the inflated view or 72 * {@link #NO_ID} if the inflated view should keep its id. 73 * 74 * @see #setInflatedId(int) 75 * @attr ref android.R.styleable#ViewStub_inflatedId 76 */ 77 public int getInflatedId() { 78 return mInflatedId; 79 } 80 81 /** 82 * Defines the id taken by the inflated view. If the inflated id is 83 * {@link View#NO_ID}, the inflated view keeps its original id. 84 * 85 * @param inflatedId A positive integer used to identify the inflated view or 86 * {@link #NO_ID} if the inflated view should keep its id. 87 * 88 * @see #getInflatedId() 89 * @attr ref android.R.styleable#ViewStub_inflatedId 90 */ 91 public void setInflatedId(int inflatedId) { 92 mInflatedId = inflatedId; 93 } 94 95 /** 96 * Returns the layout resource that will be used by {@link #setVisibility(int)} or 97 * {@link #inflate()} to replace this StubbedView 98 * in its parent by another view. 99 * 100 * @return The layout resource identifier used to inflate the new View. 101 * 102 * @see #setLayoutResource(int) 103 * @see #setVisibility(int) 104 * @see #inflate() 105 * @attr ref android.R.styleable#ViewStub_layout 106 */ 107 public int getLayoutResource() { 108 return mLayoutResource; 109 } 110 111 /** 112 * Specifies the layout resource to inflate when this StubbedView becomes visible or invisible 113 * or when {@link #inflate()} is invoked. The View created by inflating the layout resource is 114 * used to replace this StubbedView in its parent. 115 * 116 * @param layoutResource A valid layout resource identifier (different from 0.) 117 * 118 * @see #getLayoutResource() 119 * @see #setVisibility(int) 120 * @see #inflate() 121 * @attr ref android.R.styleable#ViewStub_layout 122 */ 123 public void setLayoutResource(int layoutResource) { 124 mLayoutResource = layoutResource; 125 } 126 127 /** 128 * Set {@link LayoutInflater} to use in {@link #inflate()}, or {@code null} 129 * to use the default. 130 */ 131 public void setLayoutInflater(LayoutInflater inflater) { 132 mInflater = inflater; 133 } 134 135 /** 136 * Get current {@link LayoutInflater} used in {@link #inflate()}. 137 */ 138 public LayoutInflater getLayoutInflater() { 139 return mInflater; 140 } 141 142 @Override 143 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 144 setMeasuredDimension(0, 0); 145 } 146 147 @Override 148 public void draw(Canvas canvas) { 149 } 150 151 @Override 152 protected void dispatchDraw(Canvas canvas) { 153 } 154 155 /** 156 * When visibility is set to {@link #VISIBLE} or {@link #INVISIBLE}, 157 * {@link #inflate()} is invoked and this StubbedView is replaced in its parent 158 * by the inflated layout resource. After that calls to this function are passed 159 * through to the inflated view. 160 * 161 * @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}. 162 * 163 * @see #inflate() 164 */ 165 @Override 166 public void setVisibility(int visibility) { 167 if (mInflatedViewRef != null) { 168 View view = mInflatedViewRef.get(); 169 if (view != null) { 170 view.setVisibility(visibility); 171 } else { 172 throw new IllegalStateException("setVisibility called on un-referenced view"); 173 } 174 } else { 175 super.setVisibility(visibility); 176 if (visibility == VISIBLE || visibility == INVISIBLE) { 177 inflate(); 178 } 179 } 180 } 181 182 /** 183 * Inflates the layout resource identified by {@link #getLayoutResource()} 184 * and replaces this StubbedView in its parent by the inflated layout resource. 185 * 186 * @return The inflated layout resource. 187 * 188 */ 189 public View inflate() { 190 final ViewParent viewParent = getParent(); 191 192 if (viewParent != null && viewParent instanceof ViewGroup) { 193 if (mLayoutResource != 0) { 194 final ViewGroup parent = (ViewGroup) viewParent; 195 final LayoutInflater factory; 196 if (mInflater != null) { 197 factory = mInflater; 198 } else { 199 factory = LayoutInflater.from(getContext()); 200 } 201 final View view = factory.inflate(mLayoutResource, parent, 202 false); 203 204 if (mInflatedId != NO_ID) { 205 view.setId(mInflatedId); 206 } 207 208 final int index = parent.indexOfChild(this); 209 parent.removeViewInLayout(this); 210 211 final ViewGroup.LayoutParams layoutParams = getLayoutParams(); 212 if (layoutParams != null) { 213 parent.addView(view, index, layoutParams); 214 } else { 215 parent.addView(view, index); 216 } 217 218 mInflatedViewRef = new WeakReference<View>(view); 219 220 if (mInflateListener != null) { 221 mInflateListener.onInflate(this, view); 222 } 223 224 return view; 225 } else { 226 throw new IllegalArgumentException("ViewStub must have a valid layoutResource"); 227 } 228 } else { 229 throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent"); 230 } 231 } 232 233 /** 234 * Specifies the inflate listener to be notified after this ViewStub successfully 235 * inflated its layout resource. 236 * 237 * @param inflateListener The OnInflateListener to notify of successful inflation. 238 * 239 * @see android.view.ViewStub.OnInflateListener 240 */ 241 public void setOnInflateListener(OnInflateListener inflateListener) { 242 mInflateListener = inflateListener; 243 } 244 245 /** 246 * Listener used to receive a notification after a ViewStub has successfully 247 * inflated its layout resource. 248 * 249 * @see android.view.ViewStub#setOnInflateListener(android.view.ViewStub.OnInflateListener) 250 */ 251 public static interface OnInflateListener { 252 /** 253 * Invoked after a ViewStub successfully inflated its layout resource. 254 * This method is invoked after the inflated view was added to the 255 * hierarchy but before the layout pass. 256 * 257 * @param stub The ViewStub that initiated the inflation. 258 * @param inflated The inflated View. 259 */ 260 void onInflate(ViewStubCompat stub, View inflated); 261 } 262}