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