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