1415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes/* 2415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * Copyright (C) 2014 The Android Open Source Project 3415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * 4415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * Licensed under the Apache License, Version 2.0 (the "License"); 5415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * you may not use this file except in compliance with the License. 6415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * You may obtain a copy of the License at 7415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * 8415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * http://www.apache.org/licenses/LICENSE-2.0 9415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * 10415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * Unless required by applicable law or agreed to in writing, software 11415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * distributed under the License is distributed on an "AS IS" BASIS, 12415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * See the License for the specific language governing permissions and 14415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes * limitations under the License. 15415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes */ 16415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes 172cccf609662389d6a23dbc0711d5fb2e826e8c63Chris Banespackage android.support.v7.widget; 18415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes 19415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.content.Context; 20a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banesimport android.content.res.ColorStateList; 21f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.content.res.Resources; 22f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.content.res.TypedArray; 23f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.database.DataSetObserver; 24a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banesimport android.graphics.PorterDuff; 25f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.graphics.Rect; 260332c97857f4aa1071e2e01875129ca3bea6c052Chris Banesimport android.graphics.drawable.Drawable; 27415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.os.Build; 28091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banesimport android.support.annotation.DrawableRes; 29a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banesimport android.support.annotation.Nullable; 30c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viveretteimport android.support.annotation.RestrictTo; 31a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banesimport android.support.v4.view.TintableBackgroundView; 32f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.support.v4.view.ViewCompat; 332cccf609662389d6a23dbc0711d5fb2e826e8c63Chris Banesimport android.support.v7.appcompat.R; 344c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banesimport android.support.v7.content.res.AppCompatResources; 3566698bb15ba0f873aa1c2290cc50d6bb839a474aChris Banesimport android.support.v7.view.ContextThemeWrapper; 366142a54baae3289f734947c6b5375b12eb0fb722Chris Banesimport android.support.v7.view.menu.ShowableListMenu; 37415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.util.AttributeSet; 38f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.util.Log; 39f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.view.MotionEvent; 40f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.view.View; 41f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.view.ViewGroup; 42f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.view.ViewTreeObserver; 43f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.widget.AdapterView; 4498e3c72d70ea6461e7b0e4375699c75e84b05d60Chris Banesimport android.widget.ArrayAdapter; 45f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.widget.ListAdapter; 46f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.widget.ListView; 47f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.widget.PopupWindow; 48415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banesimport android.widget.Spinner; 49f933672168f6deff5e3636584bec735416b5a93bChris Banesimport android.widget.SpinnerAdapter; 50415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes 51c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viveretteimport static android.support.annotation.RestrictTo.Scope.GROUP_ID; 52c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viverette 530332c97857f4aa1071e2e01875129ca3bea6c052Chris Banes 54415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes/** 551d6e3840486930e276d142861afb6c7e72d5ce72Kirill Grouchnikov * A {@link Spinner} which supports compatible features on older versions of the platform, 56091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * including: 57091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * <ul> 581d6e3840486930e276d142861afb6c7e72d5ce72Kirill Grouchnikov * <li>Dynamic tinting of the background via the background tint methods in 59f933672168f6deff5e3636584bec735416b5a93bChris Banes * {@link android.support.v4.view.ViewCompat}.</li> 601d6e3840486930e276d142861afb6c7e72d5ce72Kirill Grouchnikov * <li>Configuring the background tint using {@link R.attr#backgroundTint} and 61f933672168f6deff5e3636584bec735416b5a93bChris Banes * {@link R.attr#backgroundTintMode}.</li> 621d6e3840486930e276d142861afb6c7e72d5ce72Kirill Grouchnikov * <li>Setting the popup theme using {@link R.attr#popupTheme}.</li> 63091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * </ul> 64091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * 65091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * <p>This will automatically be used when you use {@link Spinner} in your layouts. 66091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes * You should only need to manually use this class when writing custom views.</p> 67415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes */ 682cccf609662389d6a23dbc0711d5fb2e826e8c63Chris Banespublic class AppCompatSpinner extends Spinner implements TintableBackgroundView { 69415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes 702c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas static final boolean IS_AT_LEAST_M = Build.VERSION.SDK_INT >= 23; 71f933672168f6deff5e3636584bec735416b5a93bChris Banes private static final boolean IS_AT_LEAST_JB = Build.VERSION.SDK_INT >= 16; 72f933672168f6deff5e3636584bec735416b5a93bChris Banes 73f933672168f6deff5e3636584bec735416b5a93bChris Banes private static final int[] ATTRS_ANDROID_SPINNERMODE = {android.R.attr.spinnerMode}; 74f933672168f6deff5e3636584bec735416b5a93bChris Banes 75f933672168f6deff5e3636584bec735416b5a93bChris Banes private static final int MAX_ITEMS_MEASURED = 15; 76f933672168f6deff5e3636584bec735416b5a93bChris Banes 77f933672168f6deff5e3636584bec735416b5a93bChris Banes private static final String TAG = "AppCompatSpinner"; 78f933672168f6deff5e3636584bec735416b5a93bChris Banes 79f933672168f6deff5e3636584bec735416b5a93bChris Banes private static final int MODE_DIALOG = 0; 80f933672168f6deff5e3636584bec735416b5a93bChris Banes private static final int MODE_DROPDOWN = 1; 81f933672168f6deff5e3636584bec735416b5a93bChris Banes private static final int MODE_THEME = -1; 82415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes 83091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes private AppCompatBackgroundHelper mBackgroundTintHelper; 84a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes 85f933672168f6deff5e3636584bec735416b5a93bChris Banes /** Context used to inflate the popup window or dialog. */ 86f933672168f6deff5e3636584bec735416b5a93bChris Banes private Context mPopupContext; 87f933672168f6deff5e3636584bec735416b5a93bChris Banes 88f933672168f6deff5e3636584bec735416b5a93bChris Banes /** Forwarding listener used to implement drag-to-open. */ 896142a54baae3289f734947c6b5375b12eb0fb722Chris Banes private ForwardingListener mForwardingListener; 90f933672168f6deff5e3636584bec735416b5a93bChris Banes 91f933672168f6deff5e3636584bec735416b5a93bChris Banes /** Temporary holder for setAdapter() calls from the super constructor. */ 92f933672168f6deff5e3636584bec735416b5a93bChris Banes private SpinnerAdapter mTempAdapter; 93f933672168f6deff5e3636584bec735416b5a93bChris Banes 94f933672168f6deff5e3636584bec735416b5a93bChris Banes private boolean mPopupSet; 95f933672168f6deff5e3636584bec735416b5a93bChris Banes 962c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas DropdownPopup mPopup; 97f933672168f6deff5e3636584bec735416b5a93bChris Banes 982c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas int mDropDownWidth; 99f933672168f6deff5e3636584bec735416b5a93bChris Banes 1002c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas final Rect mTempRect = new Rect(); 101f933672168f6deff5e3636584bec735416b5a93bChris Banes 102f933672168f6deff5e3636584bec735416b5a93bChris Banes /** 103f933672168f6deff5e3636584bec735416b5a93bChris Banes * Construct a new spinner with the given context's theme. 104f933672168f6deff5e3636584bec735416b5a93bChris Banes * 105f933672168f6deff5e3636584bec735416b5a93bChris Banes * @param context The Context the view is running in, through which it can 106f933672168f6deff5e3636584bec735416b5a93bChris Banes * access the current theme, resources, etc. 107f933672168f6deff5e3636584bec735416b5a93bChris Banes */ 1082cccf609662389d6a23dbc0711d5fb2e826e8c63Chris Banes public AppCompatSpinner(Context context) { 109415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes this(context, null); 110415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes } 111415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes 112f933672168f6deff5e3636584bec735416b5a93bChris Banes /** 113f933672168f6deff5e3636584bec735416b5a93bChris Banes * Construct a new spinner with the given context's theme and the supplied 114f933672168f6deff5e3636584bec735416b5a93bChris Banes * mode of displaying choices. <code>mode</code> may be one of 115f933672168f6deff5e3636584bec735416b5a93bChris Banes * {@link #MODE_DIALOG} or {@link #MODE_DROPDOWN}. 116f933672168f6deff5e3636584bec735416b5a93bChris Banes * 117f933672168f6deff5e3636584bec735416b5a93bChris Banes * @param context The Context the view is running in, through which it can 118f933672168f6deff5e3636584bec735416b5a93bChris Banes * access the current theme, resources, etc. 119f933672168f6deff5e3636584bec735416b5a93bChris Banes * @param mode Constant describing how the user will select choices from the spinner. 120f933672168f6deff5e3636584bec735416b5a93bChris Banes * @see #MODE_DIALOG 121f933672168f6deff5e3636584bec735416b5a93bChris Banes * @see #MODE_DROPDOWN 122f933672168f6deff5e3636584bec735416b5a93bChris Banes */ 123f933672168f6deff5e3636584bec735416b5a93bChris Banes public AppCompatSpinner(Context context, int mode) { 124f933672168f6deff5e3636584bec735416b5a93bChris Banes this(context, null, R.attr.spinnerStyle, mode); 125f933672168f6deff5e3636584bec735416b5a93bChris Banes } 126f933672168f6deff5e3636584bec735416b5a93bChris Banes 127f933672168f6deff5e3636584bec735416b5a93bChris Banes /** 128f933672168f6deff5e3636584bec735416b5a93bChris Banes * Construct a new spinner with the given context's theme and the supplied attribute set. 129f933672168f6deff5e3636584bec735416b5a93bChris Banes * 130f933672168f6deff5e3636584bec735416b5a93bChris Banes * @param context The Context the view is running in, through which it can 131f933672168f6deff5e3636584bec735416b5a93bChris Banes * access the current theme, resources, etc. 132f933672168f6deff5e3636584bec735416b5a93bChris Banes * @param attrs The attributes of the XML tag that is inflating the view. 133f933672168f6deff5e3636584bec735416b5a93bChris Banes */ 1342cccf609662389d6a23dbc0711d5fb2e826e8c63Chris Banes public AppCompatSpinner(Context context, AttributeSet attrs) { 1352cccf609662389d6a23dbc0711d5fb2e826e8c63Chris Banes this(context, attrs, R.attr.spinnerStyle); 136415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes } 137415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes 138f933672168f6deff5e3636584bec735416b5a93bChris Banes /** 139f933672168f6deff5e3636584bec735416b5a93bChris Banes * Construct a new spinner with the given context's theme, the supplied attribute set, 140f933672168f6deff5e3636584bec735416b5a93bChris Banes * and default style attribute. 141f933672168f6deff5e3636584bec735416b5a93bChris Banes * 142f933672168f6deff5e3636584bec735416b5a93bChris Banes * @param context The Context the view is running in, through which it can 143f933672168f6deff5e3636584bec735416b5a93bChris Banes * access the current theme, resources, etc. 144f933672168f6deff5e3636584bec735416b5a93bChris Banes * @param attrs The attributes of the XML tag that is inflating the view. 145f933672168f6deff5e3636584bec735416b5a93bChris Banes * @param defStyleAttr An attribute in the current theme that contains a 146f933672168f6deff5e3636584bec735416b5a93bChris Banes * reference to a style resource that supplies default values for 147f933672168f6deff5e3636584bec735416b5a93bChris Banes * the view. Can be 0 to not look for defaults. 148f933672168f6deff5e3636584bec735416b5a93bChris Banes */ 1492cccf609662389d6a23dbc0711d5fb2e826e8c63Chris Banes public AppCompatSpinner(Context context, AttributeSet attrs, int defStyleAttr) { 150f933672168f6deff5e3636584bec735416b5a93bChris Banes this(context, attrs, defStyleAttr, MODE_THEME); 151f933672168f6deff5e3636584bec735416b5a93bChris Banes } 152f933672168f6deff5e3636584bec735416b5a93bChris Banes 153f933672168f6deff5e3636584bec735416b5a93bChris Banes /** 154f933672168f6deff5e3636584bec735416b5a93bChris Banes * Construct a new spinner with the given context's theme, the supplied attribute set, 155f933672168f6deff5e3636584bec735416b5a93bChris Banes * and default style. <code>mode</code> may be one of {@link #MODE_DIALOG} or 156f933672168f6deff5e3636584bec735416b5a93bChris Banes * {@link #MODE_DROPDOWN} and determines how the user will select choices from the spinner. 157f933672168f6deff5e3636584bec735416b5a93bChris Banes * 158f933672168f6deff5e3636584bec735416b5a93bChris Banes * @param context The Context the view is running in, through which it can 159f933672168f6deff5e3636584bec735416b5a93bChris Banes * access the current theme, resources, etc. 160f933672168f6deff5e3636584bec735416b5a93bChris Banes * @param attrs The attributes of the XML tag that is inflating the view. 161f933672168f6deff5e3636584bec735416b5a93bChris Banes * @param defStyleAttr An attribute in the current theme that contains a 162f933672168f6deff5e3636584bec735416b5a93bChris Banes * reference to a style resource that supplies default values for 163f933672168f6deff5e3636584bec735416b5a93bChris Banes * the view. Can be 0 to not look for defaults. 164f933672168f6deff5e3636584bec735416b5a93bChris Banes * @param mode Constant describing how the user will select choices from the spinner. 165f933672168f6deff5e3636584bec735416b5a93bChris Banes * @see #MODE_DIALOG 166f933672168f6deff5e3636584bec735416b5a93bChris Banes * @see #MODE_DROPDOWN 167f933672168f6deff5e3636584bec735416b5a93bChris Banes */ 168bf61606ac234432b2d7f98a8219a59cc8c07cf70Chris Banes public AppCompatSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode) { 169bf61606ac234432b2d7f98a8219a59cc8c07cf70Chris Banes this(context, attrs, defStyleAttr, mode, null); 170f933672168f6deff5e3636584bec735416b5a93bChris Banes } 171f933672168f6deff5e3636584bec735416b5a93bChris Banes 172f933672168f6deff5e3636584bec735416b5a93bChris Banes 173f933672168f6deff5e3636584bec735416b5a93bChris Banes /** 174f933672168f6deff5e3636584bec735416b5a93bChris Banes * Constructs a new spinner with the given context's theme, the supplied 175f933672168f6deff5e3636584bec735416b5a93bChris Banes * attribute set, default styles, popup mode (one of {@link #MODE_DIALOG} 176f933672168f6deff5e3636584bec735416b5a93bChris Banes * or {@link #MODE_DROPDOWN}), and the context against which the popup 177f933672168f6deff5e3636584bec735416b5a93bChris Banes * should be inflated. 178f933672168f6deff5e3636584bec735416b5a93bChris Banes * 179f933672168f6deff5e3636584bec735416b5a93bChris Banes * @param context The context against which the view is inflated, which 180f933672168f6deff5e3636584bec735416b5a93bChris Banes * provides access to the current theme, resources, etc. 181f933672168f6deff5e3636584bec735416b5a93bChris Banes * @param attrs The attributes of the XML tag that is inflating the view. 182f933672168f6deff5e3636584bec735416b5a93bChris Banes * @param defStyleAttr An attribute in the current theme that contains a 183f933672168f6deff5e3636584bec735416b5a93bChris Banes * reference to a style resource that supplies default 184f933672168f6deff5e3636584bec735416b5a93bChris Banes * values for the view. Can be 0 to not look for 185f933672168f6deff5e3636584bec735416b5a93bChris Banes * defaults. 186f933672168f6deff5e3636584bec735416b5a93bChris Banes * @param mode Constant describing how the user will select choices from 187f933672168f6deff5e3636584bec735416b5a93bChris Banes * the spinner. 188f933672168f6deff5e3636584bec735416b5a93bChris Banes * @param popupTheme The theme against which the dialog or dropdown popup 189f933672168f6deff5e3636584bec735416b5a93bChris Banes * should be inflated. May be {@code null} to use the 190f933672168f6deff5e3636584bec735416b5a93bChris Banes * view theme. If set, this will override any value 191f933672168f6deff5e3636584bec735416b5a93bChris Banes * specified by 192bf61606ac234432b2d7f98a8219a59cc8c07cf70Chris Banes * {@link R.styleable#Spinner_popupTheme}. 193f933672168f6deff5e3636584bec735416b5a93bChris Banes * @see #MODE_DIALOG 194f933672168f6deff5e3636584bec735416b5a93bChris Banes * @see #MODE_DROPDOWN 195f933672168f6deff5e3636584bec735416b5a93bChris Banes */ 196f933672168f6deff5e3636584bec735416b5a93bChris Banes public AppCompatSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode, 197f933672168f6deff5e3636584bec735416b5a93bChris Banes Resources.Theme popupTheme) { 198415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes super(context, attrs, defStyleAttr); 199415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes 200f933672168f6deff5e3636584bec735416b5a93bChris Banes TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, 201f933672168f6deff5e3636584bec735416b5a93bChris Banes R.styleable.Spinner, defStyleAttr, 0); 202f933672168f6deff5e3636584bec735416b5a93bChris Banes 2034c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banes mBackgroundTintHelper = new AppCompatBackgroundHelper(this); 204091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes 205f933672168f6deff5e3636584bec735416b5a93bChris Banes if (popupTheme != null) { 206f933672168f6deff5e3636584bec735416b5a93bChris Banes mPopupContext = new ContextThemeWrapper(context, popupTheme); 207f933672168f6deff5e3636584bec735416b5a93bChris Banes } else { 208f933672168f6deff5e3636584bec735416b5a93bChris Banes final int popupThemeResId = a.getResourceId(R.styleable.Spinner_popupTheme, 0); 209f933672168f6deff5e3636584bec735416b5a93bChris Banes if (popupThemeResId != 0) { 210f933672168f6deff5e3636584bec735416b5a93bChris Banes mPopupContext = new ContextThemeWrapper(context, popupThemeResId); 211f933672168f6deff5e3636584bec735416b5a93bChris Banes } else { 212f933672168f6deff5e3636584bec735416b5a93bChris Banes // If we're running on a < M device, we'll use the current context and still handle 213f933672168f6deff5e3636584bec735416b5a93bChris Banes // any dropdown popup 214f933672168f6deff5e3636584bec735416b5a93bChris Banes mPopupContext = !IS_AT_LEAST_M ? context : null; 215f933672168f6deff5e3636584bec735416b5a93bChris Banes } 216f933672168f6deff5e3636584bec735416b5a93bChris Banes } 217f933672168f6deff5e3636584bec735416b5a93bChris Banes 218f933672168f6deff5e3636584bec735416b5a93bChris Banes if (mPopupContext != null) { 219f933672168f6deff5e3636584bec735416b5a93bChris Banes if (mode == MODE_THEME) { 220f933672168f6deff5e3636584bec735416b5a93bChris Banes if (Build.VERSION.SDK_INT >= 11) { 221f933672168f6deff5e3636584bec735416b5a93bChris Banes // If we're running on API v11+ we will try and read android:spinnerMode 222f933672168f6deff5e3636584bec735416b5a93bChris Banes TypedArray aa = null; 223f933672168f6deff5e3636584bec735416b5a93bChris Banes try { 224f933672168f6deff5e3636584bec735416b5a93bChris Banes aa = context.obtainStyledAttributes(attrs, ATTRS_ANDROID_SPINNERMODE, 225f933672168f6deff5e3636584bec735416b5a93bChris Banes defStyleAttr, 0); 226f933672168f6deff5e3636584bec735416b5a93bChris Banes if (aa.hasValue(0)) { 227f933672168f6deff5e3636584bec735416b5a93bChris Banes mode = aa.getInt(0, MODE_DIALOG); 228f933672168f6deff5e3636584bec735416b5a93bChris Banes } 229f933672168f6deff5e3636584bec735416b5a93bChris Banes } catch (Exception e) { 230f933672168f6deff5e3636584bec735416b5a93bChris Banes Log.i(TAG, "Could not read android:spinnerMode", e); 231f933672168f6deff5e3636584bec735416b5a93bChris Banes } finally { 232f933672168f6deff5e3636584bec735416b5a93bChris Banes if (aa != null) { 233f933672168f6deff5e3636584bec735416b5a93bChris Banes aa.recycle(); 234f933672168f6deff5e3636584bec735416b5a93bChris Banes } 235f933672168f6deff5e3636584bec735416b5a93bChris Banes } 236f933672168f6deff5e3636584bec735416b5a93bChris Banes } else { 237f933672168f6deff5e3636584bec735416b5a93bChris Banes // Else, we use a default mode of dropdown 238f933672168f6deff5e3636584bec735416b5a93bChris Banes mode = MODE_DROPDOWN; 239f933672168f6deff5e3636584bec735416b5a93bChris Banes } 240f933672168f6deff5e3636584bec735416b5a93bChris Banes } 241f933672168f6deff5e3636584bec735416b5a93bChris Banes 242f933672168f6deff5e3636584bec735416b5a93bChris Banes if (mode == MODE_DROPDOWN) { 243f933672168f6deff5e3636584bec735416b5a93bChris Banes final DropdownPopup popup = new DropdownPopup(mPopupContext, attrs, defStyleAttr); 244f933672168f6deff5e3636584bec735416b5a93bChris Banes final TintTypedArray pa = TintTypedArray.obtainStyledAttributes( 245f933672168f6deff5e3636584bec735416b5a93bChris Banes mPopupContext, attrs, R.styleable.Spinner, defStyleAttr, 0); 246f933672168f6deff5e3636584bec735416b5a93bChris Banes mDropDownWidth = pa.getLayoutDimension(R.styleable.Spinner_android_dropDownWidth, 247f933672168f6deff5e3636584bec735416b5a93bChris Banes LayoutParams.WRAP_CONTENT); 248f933672168f6deff5e3636584bec735416b5a93bChris Banes popup.setBackgroundDrawable( 249f933672168f6deff5e3636584bec735416b5a93bChris Banes pa.getDrawable(R.styleable.Spinner_android_popupBackground)); 250f933672168f6deff5e3636584bec735416b5a93bChris Banes popup.setPromptText(a.getString(R.styleable.Spinner_android_prompt)); 251f933672168f6deff5e3636584bec735416b5a93bChris Banes pa.recycle(); 252f933672168f6deff5e3636584bec735416b5a93bChris Banes 253f933672168f6deff5e3636584bec735416b5a93bChris Banes mPopup = popup; 2546142a54baae3289f734947c6b5375b12eb0fb722Chris Banes mForwardingListener = new ForwardingListener(this) { 255f933672168f6deff5e3636584bec735416b5a93bChris Banes @Override 2566142a54baae3289f734947c6b5375b12eb0fb722Chris Banes public ShowableListMenu getPopup() { 257f933672168f6deff5e3636584bec735416b5a93bChris Banes return popup; 258f933672168f6deff5e3636584bec735416b5a93bChris Banes } 259f933672168f6deff5e3636584bec735416b5a93bChris Banes 260f933672168f6deff5e3636584bec735416b5a93bChris Banes @Override 261f933672168f6deff5e3636584bec735416b5a93bChris Banes public boolean onForwardingStarted() { 262f933672168f6deff5e3636584bec735416b5a93bChris Banes if (!mPopup.isShowing()) { 263f933672168f6deff5e3636584bec735416b5a93bChris Banes mPopup.show(); 264f933672168f6deff5e3636584bec735416b5a93bChris Banes } 265f933672168f6deff5e3636584bec735416b5a93bChris Banes return true; 266f933672168f6deff5e3636584bec735416b5a93bChris Banes } 267f933672168f6deff5e3636584bec735416b5a93bChris Banes }; 268fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes } 269fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes } 27098e3c72d70ea6461e7b0e4375699c75e84b05d60Chris Banes 27198e3c72d70ea6461e7b0e4375699c75e84b05d60Chris Banes final CharSequence[] entries = a.getTextArray(R.styleable.Spinner_android_entries); 27298e3c72d70ea6461e7b0e4375699c75e84b05d60Chris Banes if (entries != null) { 273c9668b98d83e8a8696685c6c36be7643ab1e25e0Chris Banes final ArrayAdapter<CharSequence> adapter = new ArrayAdapter<>( 274c9668b98d83e8a8696685c6c36be7643ab1e25e0Chris Banes context, android.R.layout.simple_spinner_item, entries); 27598e3c72d70ea6461e7b0e4375699c75e84b05d60Chris Banes adapter.setDropDownViewResource(R.layout.support_simple_spinner_dropdown_item); 27698e3c72d70ea6461e7b0e4375699c75e84b05d60Chris Banes setAdapter(adapter); 27798e3c72d70ea6461e7b0e4375699c75e84b05d60Chris Banes } 27898e3c72d70ea6461e7b0e4375699c75e84b05d60Chris Banes 279091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes a.recycle(); 280415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes 281f933672168f6deff5e3636584bec735416b5a93bChris Banes mPopupSet = true; 282f933672168f6deff5e3636584bec735416b5a93bChris Banes 283f933672168f6deff5e3636584bec735416b5a93bChris Banes // Base constructors can call setAdapter before we initialize mPopup. 284f933672168f6deff5e3636584bec735416b5a93bChris Banes // Finish setting things up if this happened. 285f933672168f6deff5e3636584bec735416b5a93bChris Banes if (mTempAdapter != null) { 286f933672168f6deff5e3636584bec735416b5a93bChris Banes setAdapter(mTempAdapter); 287f933672168f6deff5e3636584bec735416b5a93bChris Banes mTempAdapter = null; 288f933672168f6deff5e3636584bec735416b5a93bChris Banes } 289f933672168f6deff5e3636584bec735416b5a93bChris Banes 290091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes mBackgroundTintHelper.loadFromAttributes(attrs, defStyleAttr); 291f933672168f6deff5e3636584bec735416b5a93bChris Banes } 292f933672168f6deff5e3636584bec735416b5a93bChris Banes 293f933672168f6deff5e3636584bec735416b5a93bChris Banes /** 294f933672168f6deff5e3636584bec735416b5a93bChris Banes * @return the context used to inflate the Spinner's popup or dialog window 295f933672168f6deff5e3636584bec735416b5a93bChris Banes */ 296f933672168f6deff5e3636584bec735416b5a93bChris Banes public Context getPopupContext() { 297f933672168f6deff5e3636584bec735416b5a93bChris Banes if (mPopup != null) { 298f933672168f6deff5e3636584bec735416b5a93bChris Banes return mPopupContext; 299f933672168f6deff5e3636584bec735416b5a93bChris Banes } else if (IS_AT_LEAST_M) { 300f933672168f6deff5e3636584bec735416b5a93bChris Banes return super.getPopupContext(); 301f933672168f6deff5e3636584bec735416b5a93bChris Banes } 302f933672168f6deff5e3636584bec735416b5a93bChris Banes return null; 303f933672168f6deff5e3636584bec735416b5a93bChris Banes } 304684dbfaf7105761b5a19a82e8273ed79d3b0641eChris Banes 305f933672168f6deff5e3636584bec735416b5a93bChris Banes public void setPopupBackgroundDrawable(Drawable background) { 306f933672168f6deff5e3636584bec735416b5a93bChris Banes if (mPopup != null) { 307f933672168f6deff5e3636584bec735416b5a93bChris Banes mPopup.setBackgroundDrawable(background); 308f933672168f6deff5e3636584bec735416b5a93bChris Banes } else if (IS_AT_LEAST_JB) { 309f933672168f6deff5e3636584bec735416b5a93bChris Banes super.setPopupBackgroundDrawable(background); 310f933672168f6deff5e3636584bec735416b5a93bChris Banes } 311684dbfaf7105761b5a19a82e8273ed79d3b0641eChris Banes } 312684dbfaf7105761b5a19a82e8273ed79d3b0641eChris Banes 313f933672168f6deff5e3636584bec735416b5a93bChris Banes public void setPopupBackgroundResource(@DrawableRes int resId) { 3144c99f0e29b0926d8e5de44b7e3980d47f052f04cChris Banes setPopupBackgroundDrawable(AppCompatResources.getDrawable(getPopupContext(), resId)); 315f933672168f6deff5e3636584bec735416b5a93bChris Banes } 3160332c97857f4aa1071e2e01875129ca3bea6c052Chris Banes 317f933672168f6deff5e3636584bec735416b5a93bChris Banes public Drawable getPopupBackground() { 318f933672168f6deff5e3636584bec735416b5a93bChris Banes if (mPopup != null) { 319f933672168f6deff5e3636584bec735416b5a93bChris Banes return mPopup.getBackground(); 320f933672168f6deff5e3636584bec735416b5a93bChris Banes } else if (IS_AT_LEAST_JB) { 321f933672168f6deff5e3636584bec735416b5a93bChris Banes return super.getPopupBackground(); 322f933672168f6deff5e3636584bec735416b5a93bChris Banes } 323f933672168f6deff5e3636584bec735416b5a93bChris Banes return null; 324f933672168f6deff5e3636584bec735416b5a93bChris Banes } 3250332c97857f4aa1071e2e01875129ca3bea6c052Chris Banes 326f933672168f6deff5e3636584bec735416b5a93bChris Banes public void setDropDownVerticalOffset(int pixels) { 327f933672168f6deff5e3636584bec735416b5a93bChris Banes if (mPopup != null) { 328f933672168f6deff5e3636584bec735416b5a93bChris Banes mPopup.setVerticalOffset(pixels); 329f933672168f6deff5e3636584bec735416b5a93bChris Banes } else if (IS_AT_LEAST_JB) { 330f933672168f6deff5e3636584bec735416b5a93bChris Banes super.setDropDownVerticalOffset(pixels); 331f933672168f6deff5e3636584bec735416b5a93bChris Banes } 332f933672168f6deff5e3636584bec735416b5a93bChris Banes } 333f933672168f6deff5e3636584bec735416b5a93bChris Banes 334f933672168f6deff5e3636584bec735416b5a93bChris Banes public int getDropDownVerticalOffset() { 335f933672168f6deff5e3636584bec735416b5a93bChris Banes if (mPopup != null) { 336f933672168f6deff5e3636584bec735416b5a93bChris Banes return mPopup.getVerticalOffset(); 337f933672168f6deff5e3636584bec735416b5a93bChris Banes } else if (IS_AT_LEAST_JB) { 338f933672168f6deff5e3636584bec735416b5a93bChris Banes return super.getDropDownVerticalOffset(); 339f933672168f6deff5e3636584bec735416b5a93bChris Banes } 340f933672168f6deff5e3636584bec735416b5a93bChris Banes return 0; 341f933672168f6deff5e3636584bec735416b5a93bChris Banes } 342f933672168f6deff5e3636584bec735416b5a93bChris Banes 343f933672168f6deff5e3636584bec735416b5a93bChris Banes public void setDropDownHorizontalOffset(int pixels) { 344f933672168f6deff5e3636584bec735416b5a93bChris Banes if (mPopup != null) { 345f933672168f6deff5e3636584bec735416b5a93bChris Banes mPopup.setHorizontalOffset(pixels); 346f933672168f6deff5e3636584bec735416b5a93bChris Banes } else if (IS_AT_LEAST_JB) { 347f933672168f6deff5e3636584bec735416b5a93bChris Banes super.setDropDownHorizontalOffset(pixels); 348f933672168f6deff5e3636584bec735416b5a93bChris Banes } 349f933672168f6deff5e3636584bec735416b5a93bChris Banes } 350f933672168f6deff5e3636584bec735416b5a93bChris Banes 351f933672168f6deff5e3636584bec735416b5a93bChris Banes /** 352f933672168f6deff5e3636584bec735416b5a93bChris Banes * Get the configured horizontal offset in pixels for the spinner's popup window of choices. 353f933672168f6deff5e3636584bec735416b5a93bChris Banes * Only valid in {@link #MODE_DROPDOWN}; other modes will return 0. 354f933672168f6deff5e3636584bec735416b5a93bChris Banes * 355f933672168f6deff5e3636584bec735416b5a93bChris Banes * @return Horizontal offset in pixels 356f933672168f6deff5e3636584bec735416b5a93bChris Banes */ 357f933672168f6deff5e3636584bec735416b5a93bChris Banes public int getDropDownHorizontalOffset() { 358f933672168f6deff5e3636584bec735416b5a93bChris Banes if (mPopup != null) { 359f933672168f6deff5e3636584bec735416b5a93bChris Banes return mPopup.getHorizontalOffset(); 360f933672168f6deff5e3636584bec735416b5a93bChris Banes } else if (IS_AT_LEAST_JB) { 361f933672168f6deff5e3636584bec735416b5a93bChris Banes return super.getDropDownHorizontalOffset(); 362f933672168f6deff5e3636584bec735416b5a93bChris Banes } 363f933672168f6deff5e3636584bec735416b5a93bChris Banes return 0; 364f933672168f6deff5e3636584bec735416b5a93bChris Banes } 365f933672168f6deff5e3636584bec735416b5a93bChris Banes 366f933672168f6deff5e3636584bec735416b5a93bChris Banes public void setDropDownWidth(int pixels) { 367f933672168f6deff5e3636584bec735416b5a93bChris Banes if (mPopup != null) { 368f933672168f6deff5e3636584bec735416b5a93bChris Banes mDropDownWidth = pixels; 369f933672168f6deff5e3636584bec735416b5a93bChris Banes } else if (IS_AT_LEAST_JB) { 370f933672168f6deff5e3636584bec735416b5a93bChris Banes super.setDropDownWidth(pixels); 371f933672168f6deff5e3636584bec735416b5a93bChris Banes } 372f933672168f6deff5e3636584bec735416b5a93bChris Banes } 373f933672168f6deff5e3636584bec735416b5a93bChris Banes 374f933672168f6deff5e3636584bec735416b5a93bChris Banes public int getDropDownWidth() { 375f933672168f6deff5e3636584bec735416b5a93bChris Banes if (mPopup != null) { 376f933672168f6deff5e3636584bec735416b5a93bChris Banes return mDropDownWidth; 377f933672168f6deff5e3636584bec735416b5a93bChris Banes } else if (IS_AT_LEAST_JB) { 378f933672168f6deff5e3636584bec735416b5a93bChris Banes return super.getDropDownWidth(); 379f933672168f6deff5e3636584bec735416b5a93bChris Banes } 380f933672168f6deff5e3636584bec735416b5a93bChris Banes return 0; 381f933672168f6deff5e3636584bec735416b5a93bChris Banes } 382f933672168f6deff5e3636584bec735416b5a93bChris Banes 383f933672168f6deff5e3636584bec735416b5a93bChris Banes @Override 384f933672168f6deff5e3636584bec735416b5a93bChris Banes public void setAdapter(SpinnerAdapter adapter) { 385f933672168f6deff5e3636584bec735416b5a93bChris Banes // The super constructor may call setAdapter before we're prepared. 386f933672168f6deff5e3636584bec735416b5a93bChris Banes // Postpone doing anything until we've finished construction. 387f933672168f6deff5e3636584bec735416b5a93bChris Banes if (!mPopupSet) { 388f933672168f6deff5e3636584bec735416b5a93bChris Banes mTempAdapter = adapter; 389f933672168f6deff5e3636584bec735416b5a93bChris Banes return; 390f933672168f6deff5e3636584bec735416b5a93bChris Banes } 391f933672168f6deff5e3636584bec735416b5a93bChris Banes 392f933672168f6deff5e3636584bec735416b5a93bChris Banes super.setAdapter(adapter); 393f933672168f6deff5e3636584bec735416b5a93bChris Banes 394f933672168f6deff5e3636584bec735416b5a93bChris Banes if (mPopup != null) { 395f933672168f6deff5e3636584bec735416b5a93bChris Banes final Context popupContext = mPopupContext == null ? getContext() : mPopupContext; 396f933672168f6deff5e3636584bec735416b5a93bChris Banes mPopup.setAdapter(new DropDownAdapter(adapter, popupContext.getTheme())); 3970332c97857f4aa1071e2e01875129ca3bea6c052Chris Banes } 3980332c97857f4aa1071e2e01875129ca3bea6c052Chris Banes } 3990332c97857f4aa1071e2e01875129ca3bea6c052Chris Banes 400091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes @Override 401f933672168f6deff5e3636584bec735416b5a93bChris Banes protected void onDetachedFromWindow() { 402f933672168f6deff5e3636584bec735416b5a93bChris Banes super.onDetachedFromWindow(); 403f933672168f6deff5e3636584bec735416b5a93bChris Banes 404f933672168f6deff5e3636584bec735416b5a93bChris Banes if (mPopup != null && mPopup.isShowing()) { 405f933672168f6deff5e3636584bec735416b5a93bChris Banes mPopup.dismiss(); 406f933672168f6deff5e3636584bec735416b5a93bChris Banes } 407f933672168f6deff5e3636584bec735416b5a93bChris Banes } 408f933672168f6deff5e3636584bec735416b5a93bChris Banes 409f933672168f6deff5e3636584bec735416b5a93bChris Banes @Override 410f933672168f6deff5e3636584bec735416b5a93bChris Banes public boolean onTouchEvent(MotionEvent event) { 411f933672168f6deff5e3636584bec735416b5a93bChris Banes if (mForwardingListener != null && mForwardingListener.onTouch(this, event)) { 412f933672168f6deff5e3636584bec735416b5a93bChris Banes return true; 413f933672168f6deff5e3636584bec735416b5a93bChris Banes } 414f933672168f6deff5e3636584bec735416b5a93bChris Banes return super.onTouchEvent(event); 415f933672168f6deff5e3636584bec735416b5a93bChris Banes } 416f933672168f6deff5e3636584bec735416b5a93bChris Banes 417f933672168f6deff5e3636584bec735416b5a93bChris Banes @Override 418f933672168f6deff5e3636584bec735416b5a93bChris Banes protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 419f933672168f6deff5e3636584bec735416b5a93bChris Banes super.onMeasure(widthMeasureSpec, heightMeasureSpec); 420f933672168f6deff5e3636584bec735416b5a93bChris Banes 421f933672168f6deff5e3636584bec735416b5a93bChris Banes if (mPopup != null && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) { 422f933672168f6deff5e3636584bec735416b5a93bChris Banes final int measuredWidth = getMeasuredWidth(); 423f933672168f6deff5e3636584bec735416b5a93bChris Banes setMeasuredDimension(Math.min(Math.max(measuredWidth, 424f933672168f6deff5e3636584bec735416b5a93bChris Banes compatMeasureContentWidth(getAdapter(), getBackground())), 425f933672168f6deff5e3636584bec735416b5a93bChris Banes MeasureSpec.getSize(widthMeasureSpec)), 426f933672168f6deff5e3636584bec735416b5a93bChris Banes getMeasuredHeight()); 427f933672168f6deff5e3636584bec735416b5a93bChris Banes } 428f933672168f6deff5e3636584bec735416b5a93bChris Banes } 429f933672168f6deff5e3636584bec735416b5a93bChris Banes 430f933672168f6deff5e3636584bec735416b5a93bChris Banes @Override 431f933672168f6deff5e3636584bec735416b5a93bChris Banes public boolean performClick() { 43269a24fe6852ac5c6a0de42a1c5a10cbd08dfcce1Chris Banes if (mPopup != null) { 43369a24fe6852ac5c6a0de42a1c5a10cbd08dfcce1Chris Banes // If we have a popup, show it if needed, or just consume the click... 43469a24fe6852ac5c6a0de42a1c5a10cbd08dfcce1Chris Banes if (!mPopup.isShowing()) { 43569a24fe6852ac5c6a0de42a1c5a10cbd08dfcce1Chris Banes mPopup.show(); 43669a24fe6852ac5c6a0de42a1c5a10cbd08dfcce1Chris Banes } 437f933672168f6deff5e3636584bec735416b5a93bChris Banes return true; 438f933672168f6deff5e3636584bec735416b5a93bChris Banes } 43969a24fe6852ac5c6a0de42a1c5a10cbd08dfcce1Chris Banes 44069a24fe6852ac5c6a0de42a1c5a10cbd08dfcce1Chris Banes // Else let the platform handle the click 441f933672168f6deff5e3636584bec735416b5a93bChris Banes return super.performClick(); 442f933672168f6deff5e3636584bec735416b5a93bChris Banes } 443f933672168f6deff5e3636584bec735416b5a93bChris Banes 444f933672168f6deff5e3636584bec735416b5a93bChris Banes @Override 445f933672168f6deff5e3636584bec735416b5a93bChris Banes public void setPrompt(CharSequence prompt) { 446f933672168f6deff5e3636584bec735416b5a93bChris Banes if (mPopup != null) { 447f933672168f6deff5e3636584bec735416b5a93bChris Banes mPopup.setPromptText(prompt); 448f933672168f6deff5e3636584bec735416b5a93bChris Banes } else { 449f933672168f6deff5e3636584bec735416b5a93bChris Banes super.setPrompt(prompt); 450f933672168f6deff5e3636584bec735416b5a93bChris Banes } 451f933672168f6deff5e3636584bec735416b5a93bChris Banes } 452f933672168f6deff5e3636584bec735416b5a93bChris Banes 453f933672168f6deff5e3636584bec735416b5a93bChris Banes @Override 454f933672168f6deff5e3636584bec735416b5a93bChris Banes public CharSequence getPrompt() { 455f933672168f6deff5e3636584bec735416b5a93bChris Banes return mPopup != null ? mPopup.getHintText() : super.getPrompt(); 456f933672168f6deff5e3636584bec735416b5a93bChris Banes } 457f933672168f6deff5e3636584bec735416b5a93bChris Banes 458f933672168f6deff5e3636584bec735416b5a93bChris Banes @Override 459091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes public void setBackgroundResource(@DrawableRes int resId) { 460091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes super.setBackgroundResource(resId); 461091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes if (mBackgroundTintHelper != null) { 462091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes mBackgroundTintHelper.onSetBackgroundResource(resId); 463091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes } 464091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes } 465091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes 466091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes @Override 467091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes public void setBackgroundDrawable(Drawable background) { 468091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes super.setBackgroundDrawable(background); 469091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes if (mBackgroundTintHelper != null) { 470091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes mBackgroundTintHelper.onSetBackgroundDrawable(background); 471091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes } 472091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes } 473091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes 474a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes /** 475a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes * This should be accessed via 476f933672168f6deff5e3636584bec735416b5a93bChris Banes * {@link android.support.v4.view.ViewCompat#setBackgroundTintList(android.view.View, 477f933672168f6deff5e3636584bec735416b5a93bChris Banes * ColorStateList)} 478a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes * 479a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes * @hide 480a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes */ 481c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viverette @RestrictTo(GROUP_ID) 482a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes @Override 483a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes public void setSupportBackgroundTintList(@Nullable ColorStateList tint) { 484091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes if (mBackgroundTintHelper != null) { 485091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes mBackgroundTintHelper.setSupportBackgroundTintList(tint); 486a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes } 487a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes } 488a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes 489a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes /** 490a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes * This should be accessed via 491a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes * {@link android.support.v4.view.ViewCompat#getBackgroundTintList(android.view.View)} 492a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes * 493a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes * @hide 494a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes */ 495c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viverette @RestrictTo(GROUP_ID) 496a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes @Override 497a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes @Nullable 498a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes public ColorStateList getSupportBackgroundTintList() { 499091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes return mBackgroundTintHelper != null 500091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes ? mBackgroundTintHelper.getSupportBackgroundTintList() : null; 501a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes } 502a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes 503a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes /** 504a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes * This should be accessed via 505f933672168f6deff5e3636584bec735416b5a93bChris Banes * {@link android.support.v4.view.ViewCompat#setBackgroundTintMode(android.view.View, 506f933672168f6deff5e3636584bec735416b5a93bChris Banes * PorterDuff.Mode)} 507a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes * 508a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes * @hide 509a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes */ 510c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viverette @RestrictTo(GROUP_ID) 511a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes @Override 512a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes public void setSupportBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) { 513091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes if (mBackgroundTintHelper != null) { 514091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes mBackgroundTintHelper.setSupportBackgroundTintMode(tintMode); 515a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes } 516a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes } 517a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes 518a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes /** 519a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes * This should be accessed via 520a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes * {@link android.support.v4.view.ViewCompat#getBackgroundTintMode(android.view.View)} 521a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes * 522a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes * @hide 523a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes */ 524c39d9c75590eca86a5e7e32a8824ba04a0d42e9bAlan Viverette @RestrictTo(GROUP_ID) 525a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes @Override 526a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes @Nullable 527a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes public PorterDuff.Mode getSupportBackgroundTintMode() { 528091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes return mBackgroundTintHelper != null 529091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes ? mBackgroundTintHelper.getSupportBackgroundTintMode() : null; 530a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes } 531a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes 532a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes @Override 533a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes protected void drawableStateChanged() { 534a5f106fbd09335ae504c39b1ee1e0caa3f1238e3Chris Banes super.drawableStateChanged(); 535091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes if (mBackgroundTintHelper != null) { 536091b0f935e68ce9bfecc2422e60eada33fa3b09cChris Banes mBackgroundTintHelper.applySupportBackgroundTint(); 537684dbfaf7105761b5a19a82e8273ed79d3b0641eChris Banes } 538684dbfaf7105761b5a19a82e8273ed79d3b0641eChris Banes } 539f933672168f6deff5e3636584bec735416b5a93bChris Banes 5402c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas int compatMeasureContentWidth(SpinnerAdapter adapter, Drawable background) { 541f933672168f6deff5e3636584bec735416b5a93bChris Banes if (adapter == null) { 542f933672168f6deff5e3636584bec735416b5a93bChris Banes return 0; 543f933672168f6deff5e3636584bec735416b5a93bChris Banes } 544f933672168f6deff5e3636584bec735416b5a93bChris Banes 545f933672168f6deff5e3636584bec735416b5a93bChris Banes int width = 0; 546f933672168f6deff5e3636584bec735416b5a93bChris Banes View itemView = null; 547f933672168f6deff5e3636584bec735416b5a93bChris Banes int itemType = 0; 548f933672168f6deff5e3636584bec735416b5a93bChris Banes final int widthMeasureSpec = 549f933672168f6deff5e3636584bec735416b5a93bChris Banes MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.UNSPECIFIED); 550f933672168f6deff5e3636584bec735416b5a93bChris Banes final int heightMeasureSpec = 551f933672168f6deff5e3636584bec735416b5a93bChris Banes MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.UNSPECIFIED); 552f933672168f6deff5e3636584bec735416b5a93bChris Banes 553f933672168f6deff5e3636584bec735416b5a93bChris Banes // Make sure the number of items we'll measure is capped. If it's a huge data set 554f933672168f6deff5e3636584bec735416b5a93bChris Banes // with wildly varying sizes, oh well. 555f933672168f6deff5e3636584bec735416b5a93bChris Banes int start = Math.max(0, getSelectedItemPosition()); 556f933672168f6deff5e3636584bec735416b5a93bChris Banes final int end = Math.min(adapter.getCount(), start + MAX_ITEMS_MEASURED); 557f933672168f6deff5e3636584bec735416b5a93bChris Banes final int count = end - start; 558f933672168f6deff5e3636584bec735416b5a93bChris Banes start = Math.max(0, start - (MAX_ITEMS_MEASURED - count)); 559f933672168f6deff5e3636584bec735416b5a93bChris Banes for (int i = start; i < end; i++) { 560f933672168f6deff5e3636584bec735416b5a93bChris Banes final int positionType = adapter.getItemViewType(i); 561f933672168f6deff5e3636584bec735416b5a93bChris Banes if (positionType != itemType) { 562f933672168f6deff5e3636584bec735416b5a93bChris Banes itemType = positionType; 563f933672168f6deff5e3636584bec735416b5a93bChris Banes itemView = null; 564f933672168f6deff5e3636584bec735416b5a93bChris Banes } 565f933672168f6deff5e3636584bec735416b5a93bChris Banes itemView = adapter.getView(i, itemView, this); 566f933672168f6deff5e3636584bec735416b5a93bChris Banes if (itemView.getLayoutParams() == null) { 567f933672168f6deff5e3636584bec735416b5a93bChris Banes itemView.setLayoutParams(new LayoutParams( 568f933672168f6deff5e3636584bec735416b5a93bChris Banes LayoutParams.WRAP_CONTENT, 569f933672168f6deff5e3636584bec735416b5a93bChris Banes LayoutParams.WRAP_CONTENT)); 570f933672168f6deff5e3636584bec735416b5a93bChris Banes } 571f933672168f6deff5e3636584bec735416b5a93bChris Banes itemView.measure(widthMeasureSpec, heightMeasureSpec); 572f933672168f6deff5e3636584bec735416b5a93bChris Banes width = Math.max(width, itemView.getMeasuredWidth()); 573f933672168f6deff5e3636584bec735416b5a93bChris Banes } 574f933672168f6deff5e3636584bec735416b5a93bChris Banes 575f933672168f6deff5e3636584bec735416b5a93bChris Banes // Add background padding to measured width 576f933672168f6deff5e3636584bec735416b5a93bChris Banes if (background != null) { 577f933672168f6deff5e3636584bec735416b5a93bChris Banes background.getPadding(mTempRect); 578f933672168f6deff5e3636584bec735416b5a93bChris Banes width += mTempRect.left + mTempRect.right; 579f933672168f6deff5e3636584bec735416b5a93bChris Banes } 580f933672168f6deff5e3636584bec735416b5a93bChris Banes 581f933672168f6deff5e3636584bec735416b5a93bChris Banes return width; 582f933672168f6deff5e3636584bec735416b5a93bChris Banes } 583f933672168f6deff5e3636584bec735416b5a93bChris Banes 584f933672168f6deff5e3636584bec735416b5a93bChris Banes /** 585f933672168f6deff5e3636584bec735416b5a93bChris Banes * <p>Wrapper class for an Adapter. Transforms the embedded Adapter instance 586f933672168f6deff5e3636584bec735416b5a93bChris Banes * into a ListAdapter.</p> 587f933672168f6deff5e3636584bec735416b5a93bChris Banes */ 588f933672168f6deff5e3636584bec735416b5a93bChris Banes private static class DropDownAdapter implements ListAdapter, SpinnerAdapter { 589f933672168f6deff5e3636584bec735416b5a93bChris Banes 590f933672168f6deff5e3636584bec735416b5a93bChris Banes private SpinnerAdapter mAdapter; 591f933672168f6deff5e3636584bec735416b5a93bChris Banes 592f933672168f6deff5e3636584bec735416b5a93bChris Banes private ListAdapter mListAdapter; 593f933672168f6deff5e3636584bec735416b5a93bChris Banes 594f933672168f6deff5e3636584bec735416b5a93bChris Banes /** 595f933672168f6deff5e3636584bec735416b5a93bChris Banes * Creates a new ListAdapter wrapper for the specified adapter. 596f933672168f6deff5e3636584bec735416b5a93bChris Banes * 597f933672168f6deff5e3636584bec735416b5a93bChris Banes * @param adapter the SpinnerAdapter to transform into a ListAdapter 598f933672168f6deff5e3636584bec735416b5a93bChris Banes * @param dropDownTheme the theme against which to inflate drop-down 599f933672168f6deff5e3636584bec735416b5a93bChris Banes * views, may be {@null} to use default theme 600f933672168f6deff5e3636584bec735416b5a93bChris Banes */ 601f933672168f6deff5e3636584bec735416b5a93bChris Banes public DropDownAdapter(@Nullable SpinnerAdapter adapter, 602f933672168f6deff5e3636584bec735416b5a93bChris Banes @Nullable Resources.Theme dropDownTheme) { 603f933672168f6deff5e3636584bec735416b5a93bChris Banes mAdapter = adapter; 604f933672168f6deff5e3636584bec735416b5a93bChris Banes 605f933672168f6deff5e3636584bec735416b5a93bChris Banes if (adapter instanceof ListAdapter) { 606f933672168f6deff5e3636584bec735416b5a93bChris Banes mListAdapter = (ListAdapter) adapter; 607f933672168f6deff5e3636584bec735416b5a93bChris Banes } 608f933672168f6deff5e3636584bec735416b5a93bChris Banes 609f933672168f6deff5e3636584bec735416b5a93bChris Banes if (dropDownTheme != null) { 610f933672168f6deff5e3636584bec735416b5a93bChris Banes if (IS_AT_LEAST_M && adapter instanceof android.widget.ThemedSpinnerAdapter) { 611f933672168f6deff5e3636584bec735416b5a93bChris Banes final android.widget.ThemedSpinnerAdapter themedAdapter = 612f933672168f6deff5e3636584bec735416b5a93bChris Banes (android.widget.ThemedSpinnerAdapter) adapter; 613f933672168f6deff5e3636584bec735416b5a93bChris Banes if (themedAdapter.getDropDownViewTheme() != dropDownTheme) { 614f933672168f6deff5e3636584bec735416b5a93bChris Banes themedAdapter.setDropDownViewTheme(dropDownTheme); 615f933672168f6deff5e3636584bec735416b5a93bChris Banes } 616f933672168f6deff5e3636584bec735416b5a93bChris Banes } else if (adapter instanceof ThemedSpinnerAdapter) { 617f933672168f6deff5e3636584bec735416b5a93bChris Banes final ThemedSpinnerAdapter themedAdapter = (ThemedSpinnerAdapter) adapter; 618f933672168f6deff5e3636584bec735416b5a93bChris Banes if (themedAdapter.getDropDownViewTheme() == null) { 619f933672168f6deff5e3636584bec735416b5a93bChris Banes themedAdapter.setDropDownViewTheme(dropDownTheme); 620f933672168f6deff5e3636584bec735416b5a93bChris Banes } 621f933672168f6deff5e3636584bec735416b5a93bChris Banes } 622f933672168f6deff5e3636584bec735416b5a93bChris Banes } 623f933672168f6deff5e3636584bec735416b5a93bChris Banes } 624f933672168f6deff5e3636584bec735416b5a93bChris Banes 625f933672168f6deff5e3636584bec735416b5a93bChris Banes public int getCount() { 626f933672168f6deff5e3636584bec735416b5a93bChris Banes return mAdapter == null ? 0 : mAdapter.getCount(); 627f933672168f6deff5e3636584bec735416b5a93bChris Banes } 628f933672168f6deff5e3636584bec735416b5a93bChris Banes 629f933672168f6deff5e3636584bec735416b5a93bChris Banes public Object getItem(int position) { 630f933672168f6deff5e3636584bec735416b5a93bChris Banes return mAdapter == null ? null : mAdapter.getItem(position); 631f933672168f6deff5e3636584bec735416b5a93bChris Banes } 632f933672168f6deff5e3636584bec735416b5a93bChris Banes 633f933672168f6deff5e3636584bec735416b5a93bChris Banes public long getItemId(int position) { 634f933672168f6deff5e3636584bec735416b5a93bChris Banes return mAdapter == null ? -1 : mAdapter.getItemId(position); 635f933672168f6deff5e3636584bec735416b5a93bChris Banes } 636f933672168f6deff5e3636584bec735416b5a93bChris Banes 637f933672168f6deff5e3636584bec735416b5a93bChris Banes public View getView(int position, View convertView, ViewGroup parent) { 638f933672168f6deff5e3636584bec735416b5a93bChris Banes return getDropDownView(position, convertView, parent); 639f933672168f6deff5e3636584bec735416b5a93bChris Banes } 640f933672168f6deff5e3636584bec735416b5a93bChris Banes 641f933672168f6deff5e3636584bec735416b5a93bChris Banes public View getDropDownView(int position, View convertView, ViewGroup parent) { 642f933672168f6deff5e3636584bec735416b5a93bChris Banes return (mAdapter == null) ? null 643f933672168f6deff5e3636584bec735416b5a93bChris Banes : mAdapter.getDropDownView(position, convertView, parent); 644f933672168f6deff5e3636584bec735416b5a93bChris Banes } 645f933672168f6deff5e3636584bec735416b5a93bChris Banes 646f933672168f6deff5e3636584bec735416b5a93bChris Banes public boolean hasStableIds() { 647f933672168f6deff5e3636584bec735416b5a93bChris Banes return mAdapter != null && mAdapter.hasStableIds(); 648f933672168f6deff5e3636584bec735416b5a93bChris Banes } 649f933672168f6deff5e3636584bec735416b5a93bChris Banes 650f933672168f6deff5e3636584bec735416b5a93bChris Banes public void registerDataSetObserver(DataSetObserver observer) { 651f933672168f6deff5e3636584bec735416b5a93bChris Banes if (mAdapter != null) { 652f933672168f6deff5e3636584bec735416b5a93bChris Banes mAdapter.registerDataSetObserver(observer); 653f933672168f6deff5e3636584bec735416b5a93bChris Banes } 654f933672168f6deff5e3636584bec735416b5a93bChris Banes } 655f933672168f6deff5e3636584bec735416b5a93bChris Banes 656f933672168f6deff5e3636584bec735416b5a93bChris Banes public void unregisterDataSetObserver(DataSetObserver observer) { 657f933672168f6deff5e3636584bec735416b5a93bChris Banes if (mAdapter != null) { 658f933672168f6deff5e3636584bec735416b5a93bChris Banes mAdapter.unregisterDataSetObserver(observer); 659f933672168f6deff5e3636584bec735416b5a93bChris Banes } 660f933672168f6deff5e3636584bec735416b5a93bChris Banes } 661f933672168f6deff5e3636584bec735416b5a93bChris Banes 662f933672168f6deff5e3636584bec735416b5a93bChris Banes /** 663f933672168f6deff5e3636584bec735416b5a93bChris Banes * If the wrapped SpinnerAdapter is also a ListAdapter, delegate this call. 664f933672168f6deff5e3636584bec735416b5a93bChris Banes * Otherwise, return true. 665f933672168f6deff5e3636584bec735416b5a93bChris Banes */ 666f933672168f6deff5e3636584bec735416b5a93bChris Banes public boolean areAllItemsEnabled() { 667f933672168f6deff5e3636584bec735416b5a93bChris Banes final ListAdapter adapter = mListAdapter; 668f933672168f6deff5e3636584bec735416b5a93bChris Banes if (adapter != null) { 669f933672168f6deff5e3636584bec735416b5a93bChris Banes return adapter.areAllItemsEnabled(); 670f933672168f6deff5e3636584bec735416b5a93bChris Banes } else { 671f933672168f6deff5e3636584bec735416b5a93bChris Banes return true; 672f933672168f6deff5e3636584bec735416b5a93bChris Banes } 673f933672168f6deff5e3636584bec735416b5a93bChris Banes } 674f933672168f6deff5e3636584bec735416b5a93bChris Banes 675f933672168f6deff5e3636584bec735416b5a93bChris Banes /** 676f933672168f6deff5e3636584bec735416b5a93bChris Banes * If the wrapped SpinnerAdapter is also a ListAdapter, delegate this call. 677f933672168f6deff5e3636584bec735416b5a93bChris Banes * Otherwise, return true. 678f933672168f6deff5e3636584bec735416b5a93bChris Banes */ 679f933672168f6deff5e3636584bec735416b5a93bChris Banes public boolean isEnabled(int position) { 680f933672168f6deff5e3636584bec735416b5a93bChris Banes final ListAdapter adapter = mListAdapter; 681f933672168f6deff5e3636584bec735416b5a93bChris Banes if (adapter != null) { 682f933672168f6deff5e3636584bec735416b5a93bChris Banes return adapter.isEnabled(position); 683f933672168f6deff5e3636584bec735416b5a93bChris Banes } else { 684f933672168f6deff5e3636584bec735416b5a93bChris Banes return true; 685f933672168f6deff5e3636584bec735416b5a93bChris Banes } 686f933672168f6deff5e3636584bec735416b5a93bChris Banes } 687f933672168f6deff5e3636584bec735416b5a93bChris Banes 688f933672168f6deff5e3636584bec735416b5a93bChris Banes public int getItemViewType(int position) { 689f933672168f6deff5e3636584bec735416b5a93bChris Banes return 0; 690f933672168f6deff5e3636584bec735416b5a93bChris Banes } 691f933672168f6deff5e3636584bec735416b5a93bChris Banes 692f933672168f6deff5e3636584bec735416b5a93bChris Banes public int getViewTypeCount() { 693f933672168f6deff5e3636584bec735416b5a93bChris Banes return 1; 694f933672168f6deff5e3636584bec735416b5a93bChris Banes } 695f933672168f6deff5e3636584bec735416b5a93bChris Banes 696f933672168f6deff5e3636584bec735416b5a93bChris Banes public boolean isEmpty() { 697f933672168f6deff5e3636584bec735416b5a93bChris Banes return getCount() == 0; 698f933672168f6deff5e3636584bec735416b5a93bChris Banes } 699f933672168f6deff5e3636584bec735416b5a93bChris Banes } 700f933672168f6deff5e3636584bec735416b5a93bChris Banes 701f933672168f6deff5e3636584bec735416b5a93bChris Banes private class DropdownPopup extends ListPopupWindow { 702f933672168f6deff5e3636584bec735416b5a93bChris Banes private CharSequence mHintText; 7032c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas ListAdapter mAdapter; 704f933672168f6deff5e3636584bec735416b5a93bChris Banes private final Rect mVisibleRect = new Rect(); 705f933672168f6deff5e3636584bec735416b5a93bChris Banes 706f933672168f6deff5e3636584bec735416b5a93bChris Banes public DropdownPopup(Context context, AttributeSet attrs, int defStyleAttr) { 707f933672168f6deff5e3636584bec735416b5a93bChris Banes super(context, attrs, defStyleAttr); 708f933672168f6deff5e3636584bec735416b5a93bChris Banes 709f933672168f6deff5e3636584bec735416b5a93bChris Banes setAnchorView(AppCompatSpinner.this); 710f933672168f6deff5e3636584bec735416b5a93bChris Banes setModal(true); 711f933672168f6deff5e3636584bec735416b5a93bChris Banes setPromptPosition(POSITION_PROMPT_ABOVE); 712f933672168f6deff5e3636584bec735416b5a93bChris Banes 713f933672168f6deff5e3636584bec735416b5a93bChris Banes setOnItemClickListener(new AdapterView.OnItemClickListener() { 714f933672168f6deff5e3636584bec735416b5a93bChris Banes @Override 715f933672168f6deff5e3636584bec735416b5a93bChris Banes public void onItemClick(AdapterView<?> parent, View v, int position, long id) { 716f933672168f6deff5e3636584bec735416b5a93bChris Banes AppCompatSpinner.this.setSelection(position); 717f933672168f6deff5e3636584bec735416b5a93bChris Banes if (getOnItemClickListener() != null) { 718f933672168f6deff5e3636584bec735416b5a93bChris Banes AppCompatSpinner.this 719f933672168f6deff5e3636584bec735416b5a93bChris Banes .performItemClick(v, position, mAdapter.getItemId(position)); 720f933672168f6deff5e3636584bec735416b5a93bChris Banes } 721f933672168f6deff5e3636584bec735416b5a93bChris Banes dismiss(); 722f933672168f6deff5e3636584bec735416b5a93bChris Banes } 723f933672168f6deff5e3636584bec735416b5a93bChris Banes }); 724f933672168f6deff5e3636584bec735416b5a93bChris Banes } 725f933672168f6deff5e3636584bec735416b5a93bChris Banes 726f933672168f6deff5e3636584bec735416b5a93bChris Banes @Override 727f933672168f6deff5e3636584bec735416b5a93bChris Banes public void setAdapter(ListAdapter adapter) { 728f933672168f6deff5e3636584bec735416b5a93bChris Banes super.setAdapter(adapter); 729f933672168f6deff5e3636584bec735416b5a93bChris Banes mAdapter = adapter; 730f933672168f6deff5e3636584bec735416b5a93bChris Banes } 731f933672168f6deff5e3636584bec735416b5a93bChris Banes 732f933672168f6deff5e3636584bec735416b5a93bChris Banes public CharSequence getHintText() { 733f933672168f6deff5e3636584bec735416b5a93bChris Banes return mHintText; 734f933672168f6deff5e3636584bec735416b5a93bChris Banes } 735f933672168f6deff5e3636584bec735416b5a93bChris Banes 736f933672168f6deff5e3636584bec735416b5a93bChris Banes public void setPromptText(CharSequence hintText) { 737f933672168f6deff5e3636584bec735416b5a93bChris Banes // Hint text is ignored for dropdowns, but maintain it here. 738f933672168f6deff5e3636584bec735416b5a93bChris Banes mHintText = hintText; 739f933672168f6deff5e3636584bec735416b5a93bChris Banes } 740f933672168f6deff5e3636584bec735416b5a93bChris Banes 741f933672168f6deff5e3636584bec735416b5a93bChris Banes void computeContentWidth() { 742f933672168f6deff5e3636584bec735416b5a93bChris Banes final Drawable background = getBackground(); 743f933672168f6deff5e3636584bec735416b5a93bChris Banes int hOffset = 0; 744f933672168f6deff5e3636584bec735416b5a93bChris Banes if (background != null) { 745f933672168f6deff5e3636584bec735416b5a93bChris Banes background.getPadding(mTempRect); 746f933672168f6deff5e3636584bec735416b5a93bChris Banes hOffset = ViewUtils.isLayoutRtl(AppCompatSpinner.this) ? mTempRect.right 747f933672168f6deff5e3636584bec735416b5a93bChris Banes : -mTempRect.left; 748f933672168f6deff5e3636584bec735416b5a93bChris Banes } else { 749f933672168f6deff5e3636584bec735416b5a93bChris Banes mTempRect.left = mTempRect.right = 0; 750f933672168f6deff5e3636584bec735416b5a93bChris Banes } 751f933672168f6deff5e3636584bec735416b5a93bChris Banes 752f933672168f6deff5e3636584bec735416b5a93bChris Banes final int spinnerPaddingLeft = AppCompatSpinner.this.getPaddingLeft(); 753f933672168f6deff5e3636584bec735416b5a93bChris Banes final int spinnerPaddingRight = AppCompatSpinner.this.getPaddingRight(); 754f933672168f6deff5e3636584bec735416b5a93bChris Banes final int spinnerWidth = AppCompatSpinner.this.getWidth(); 755f933672168f6deff5e3636584bec735416b5a93bChris Banes if (mDropDownWidth == WRAP_CONTENT) { 756f933672168f6deff5e3636584bec735416b5a93bChris Banes int contentWidth = compatMeasureContentWidth( 757f933672168f6deff5e3636584bec735416b5a93bChris Banes (SpinnerAdapter) mAdapter, getBackground()); 758f933672168f6deff5e3636584bec735416b5a93bChris Banes final int contentWidthLimit = getContext().getResources() 759f933672168f6deff5e3636584bec735416b5a93bChris Banes .getDisplayMetrics().widthPixels - mTempRect.left - mTempRect.right; 760f933672168f6deff5e3636584bec735416b5a93bChris Banes if (contentWidth > contentWidthLimit) { 761f933672168f6deff5e3636584bec735416b5a93bChris Banes contentWidth = contentWidthLimit; 762f933672168f6deff5e3636584bec735416b5a93bChris Banes } 763f933672168f6deff5e3636584bec735416b5a93bChris Banes setContentWidth(Math.max( 764f933672168f6deff5e3636584bec735416b5a93bChris Banes contentWidth, spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight)); 765f933672168f6deff5e3636584bec735416b5a93bChris Banes } else if (mDropDownWidth == MATCH_PARENT) { 766f933672168f6deff5e3636584bec735416b5a93bChris Banes setContentWidth(spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight); 767f933672168f6deff5e3636584bec735416b5a93bChris Banes } else { 768f933672168f6deff5e3636584bec735416b5a93bChris Banes setContentWidth(mDropDownWidth); 769f933672168f6deff5e3636584bec735416b5a93bChris Banes } 770f933672168f6deff5e3636584bec735416b5a93bChris Banes if (ViewUtils.isLayoutRtl(AppCompatSpinner.this)) { 771f933672168f6deff5e3636584bec735416b5a93bChris Banes hOffset += spinnerWidth - spinnerPaddingRight - getWidth(); 772f933672168f6deff5e3636584bec735416b5a93bChris Banes } else { 773f933672168f6deff5e3636584bec735416b5a93bChris Banes hOffset += spinnerPaddingLeft; 774f933672168f6deff5e3636584bec735416b5a93bChris Banes } 775f933672168f6deff5e3636584bec735416b5a93bChris Banes setHorizontalOffset(hOffset); 776f933672168f6deff5e3636584bec735416b5a93bChris Banes } 777f933672168f6deff5e3636584bec735416b5a93bChris Banes 778f933672168f6deff5e3636584bec735416b5a93bChris Banes public void show() { 779f933672168f6deff5e3636584bec735416b5a93bChris Banes final boolean wasShowing = isShowing(); 780f933672168f6deff5e3636584bec735416b5a93bChris Banes 781f933672168f6deff5e3636584bec735416b5a93bChris Banes computeContentWidth(); 782f933672168f6deff5e3636584bec735416b5a93bChris Banes 783f933672168f6deff5e3636584bec735416b5a93bChris Banes setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED); 784f933672168f6deff5e3636584bec735416b5a93bChris Banes super.show(); 785f933672168f6deff5e3636584bec735416b5a93bChris Banes final ListView listView = getListView(); 786f933672168f6deff5e3636584bec735416b5a93bChris Banes listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); 787f933672168f6deff5e3636584bec735416b5a93bChris Banes setSelection(AppCompatSpinner.this.getSelectedItemPosition()); 788f933672168f6deff5e3636584bec735416b5a93bChris Banes 789f933672168f6deff5e3636584bec735416b5a93bChris Banes if (wasShowing) { 790f933672168f6deff5e3636584bec735416b5a93bChris Banes // Skip setting up the layout/dismiss listener below. If we were previously 791f933672168f6deff5e3636584bec735416b5a93bChris Banes // showing it will still stick around. 792f933672168f6deff5e3636584bec735416b5a93bChris Banes return; 793f933672168f6deff5e3636584bec735416b5a93bChris Banes } 794f933672168f6deff5e3636584bec735416b5a93bChris Banes 795f933672168f6deff5e3636584bec735416b5a93bChris Banes // Make sure we hide if our anchor goes away. 796f933672168f6deff5e3636584bec735416b5a93bChris Banes // TODO: This might be appropriate to push all the way down to PopupWindow, 797f933672168f6deff5e3636584bec735416b5a93bChris Banes // but it may have other side effects to investigate first. (Text editing handles, etc.) 798f933672168f6deff5e3636584bec735416b5a93bChris Banes final ViewTreeObserver vto = getViewTreeObserver(); 799f933672168f6deff5e3636584bec735416b5a93bChris Banes if (vto != null) { 800f933672168f6deff5e3636584bec735416b5a93bChris Banes final ViewTreeObserver.OnGlobalLayoutListener layoutListener 801f933672168f6deff5e3636584bec735416b5a93bChris Banes = new ViewTreeObserver.OnGlobalLayoutListener() { 802f933672168f6deff5e3636584bec735416b5a93bChris Banes @Override 803f933672168f6deff5e3636584bec735416b5a93bChris Banes public void onGlobalLayout() { 804f933672168f6deff5e3636584bec735416b5a93bChris Banes if (!isVisibleToUser(AppCompatSpinner.this)) { 805f933672168f6deff5e3636584bec735416b5a93bChris Banes dismiss(); 806f933672168f6deff5e3636584bec735416b5a93bChris Banes } else { 807f933672168f6deff5e3636584bec735416b5a93bChris Banes computeContentWidth(); 808f933672168f6deff5e3636584bec735416b5a93bChris Banes 809f933672168f6deff5e3636584bec735416b5a93bChris Banes // Use super.show here to update; we don't want to move the selected 810f933672168f6deff5e3636584bec735416b5a93bChris Banes // position or adjust other things that would be reset otherwise. 811f933672168f6deff5e3636584bec735416b5a93bChris Banes DropdownPopup.super.show(); 812f933672168f6deff5e3636584bec735416b5a93bChris Banes } 813f933672168f6deff5e3636584bec735416b5a93bChris Banes } 814f933672168f6deff5e3636584bec735416b5a93bChris Banes }; 815f933672168f6deff5e3636584bec735416b5a93bChris Banes vto.addOnGlobalLayoutListener(layoutListener); 816f933672168f6deff5e3636584bec735416b5a93bChris Banes setOnDismissListener(new PopupWindow.OnDismissListener() { 817f933672168f6deff5e3636584bec735416b5a93bChris Banes @Override 818f933672168f6deff5e3636584bec735416b5a93bChris Banes public void onDismiss() { 819f933672168f6deff5e3636584bec735416b5a93bChris Banes final ViewTreeObserver vto = getViewTreeObserver(); 820f933672168f6deff5e3636584bec735416b5a93bChris Banes if (vto != null) { 821f933672168f6deff5e3636584bec735416b5a93bChris Banes vto.removeGlobalOnLayoutListener(layoutListener); 822f933672168f6deff5e3636584bec735416b5a93bChris Banes } 823f933672168f6deff5e3636584bec735416b5a93bChris Banes } 824f933672168f6deff5e3636584bec735416b5a93bChris Banes }); 825f933672168f6deff5e3636584bec735416b5a93bChris Banes } 826f933672168f6deff5e3636584bec735416b5a93bChris Banes } 827f933672168f6deff5e3636584bec735416b5a93bChris Banes 828f933672168f6deff5e3636584bec735416b5a93bChris Banes /** 829f933672168f6deff5e3636584bec735416b5a93bChris Banes * Simplified version of the the hidden View.isVisibleToUser() 830f933672168f6deff5e3636584bec735416b5a93bChris Banes */ 8312c1bad7ecd5879bf0f29ce2ce1bc5bd67a3f4682Aurimas Liutikas boolean isVisibleToUser(View view) { 832f933672168f6deff5e3636584bec735416b5a93bChris Banes return ViewCompat.isAttachedToWindow(view) && view.getGlobalVisibleRect(mVisibleRect); 833f933672168f6deff5e3636584bec735416b5a93bChris Banes } 834f933672168f6deff5e3636584bec735416b5a93bChris Banes } 835415f740df4981ef2f5fb462a50c7cf095cc21128Chris Banes} 836