Home | History | Annotate | Download | only in widget
      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 
     52     public ViewStubCompat(Context context, AttributeSet attrs) {
     53         this(context, attrs, 0);
     54     }
     55 
     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      */
     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      */
     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      */
    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      */
    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      */
    136     public void setLayoutInflater(LayoutInflater inflater) {
    137         mInflater = inflater;
    138     }
    139 
    140     /**
    141      * Get current {@link LayoutInflater} used in {@link #inflate()}.
    142      */
    143     public LayoutInflater getLayoutInflater() {
    144         return mInflater;
    145     }
    146 
    147     @Override
    148     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    149         setMeasuredDimension(0, 0);
    150     }
    151 
    152     @SuppressLint("MissingSuperCall") // Intentionally not calling super method.
    153     @Override
    154     public void draw(Canvas canvas) {
    155     }
    156 
    157     @Override
    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
    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      */
    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      */
    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          */
    266         void onInflate(ViewStubCompat stub, View inflated);
    267     }
    268 }