diff options
author | Alan Viverette <alanv@google.com> | 2015-01-14 10:43:31 -0800 |
---|---|---|
committer | Alan Viverette <alanv@google.com> | 2015-01-14 10:43:31 -0800 |
commit | b9ead4a91599ca63e947f74f83b67a58bda64a82 (patch) | |
tree | e402c3db4afcf539fce6c379782732aeed19069f /core/java/android/widget | |
parent | dbf22ccd92dc95e7c93c0485be4bcfb5c966578a (diff) | |
download | frameworks_base-b9ead4a91599ca63e947f74f83b67a58bda64a82.zip frameworks_base-b9ead4a91599ca63e947f74f83b67a58bda64a82.tar.gz frameworks_base-b9ead4a91599ca63e947f74f83b67a58bda64a82.tar.bz2 |
Add popup theme for Spinner, use for actionBarPopupTheme default
Also adds methods for setting the context used to inflate drop-down
views in several adapters.
Bug: 17625714
Change-Id: Id267afa4901c1d46ceb3bc3b10fc642cea1799fe
Diffstat (limited to 'core/java/android/widget')
-rw-r--r-- | core/java/android/widget/ArrayAdapter.java | 71 | ||||
-rw-r--r-- | core/java/android/widget/CursorAdapter.java | 46 | ||||
-rw-r--r-- | core/java/android/widget/ResourceCursorAdapter.java | 41 | ||||
-rw-r--r-- | core/java/android/widget/SimpleAdapter.java | 55 | ||||
-rw-r--r-- | core/java/android/widget/Spinner.java | 116 | ||||
-rw-r--r-- | core/java/android/widget/SpinnerAdapter.java | 13 | ||||
-rw-r--r-- | core/java/android/widget/SuggestionsAdapter.java | 39 |
7 files changed, 307 insertions, 74 deletions
diff --git a/core/java/android/widget/ArrayAdapter.java b/core/java/android/widget/ArrayAdapter.java index 97926a7..aff5e29 100644 --- a/core/java/android/widget/ArrayAdapter.java +++ b/core/java/android/widget/ArrayAdapter.java @@ -17,7 +17,9 @@ package android.widget; import android.content.Context; +import android.content.res.Resources; import android.util.Log; +import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -44,7 +46,8 @@ import java.util.List; * or to have some of data besides toString() results fill the views, * override {@link #getView(int, View, ViewGroup)} to return the type of view you want. */ -public class ArrayAdapter<T> extends BaseAdapter implements Filterable { +public class ArrayAdapter<T> extends BaseAdapter implements Filterable, + Spinner.ThemedSpinnerAdapter { /** * Contains the list of objects that represent the data of this ArrayAdapter. * The content of this list is referred to as "the array" in the documentation. @@ -93,6 +96,9 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable { private LayoutInflater mInflater; + /** Layout inflater used for {@link #getDropDownView(int, View, ViewGroup)}. */ + private LayoutInflater mDropDownInflater; + /** * Constructor * @@ -101,7 +107,7 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable { * instantiating views. */ public ArrayAdapter(Context context, int resource) { - init(context, resource, 0, new ArrayList<T>()); + this(context, resource, 0, new ArrayList<T>()); } /** @@ -113,7 +119,7 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable { * @param textViewResourceId The id of the TextView within the layout resource to be populated */ public ArrayAdapter(Context context, int resource, int textViewResourceId) { - init(context, resource, textViewResourceId, new ArrayList<T>()); + this(context, resource, textViewResourceId, new ArrayList<T>()); } /** @@ -125,7 +131,7 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable { * @param objects The objects to represent in the ListView. */ public ArrayAdapter(Context context, int resource, T[] objects) { - init(context, resource, 0, Arrays.asList(objects)); + this(context, resource, 0, Arrays.asList(objects)); } /** @@ -138,7 +144,7 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable { * @param objects The objects to represent in the ListView. */ public ArrayAdapter(Context context, int resource, int textViewResourceId, T[] objects) { - init(context, resource, textViewResourceId, Arrays.asList(objects)); + this(context, resource, textViewResourceId, Arrays.asList(objects)); } /** @@ -150,7 +156,7 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable { * @param objects The objects to represent in the ListView. */ public ArrayAdapter(Context context, int resource, List<T> objects) { - init(context, resource, 0, objects); + this(context, resource, 0, objects); } /** @@ -163,7 +169,11 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable { * @param objects The objects to represent in the ListView. */ public ArrayAdapter(Context context, int resource, int textViewResourceId, List<T> objects) { - init(context, resource, textViewResourceId, objects); + mContext = context; + mInflater = LayoutInflater.from(context); + mResource = mDropDownResource = resource; + mObjects = objects; + mFieldId = textViewResourceId; } /** @@ -305,14 +315,6 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable { mNotifyOnChange = notifyOnChange; } - private void init(Context context, int resource, int textViewResourceId, List<T> objects) { - mContext = context; - mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - mResource = mDropDownResource = resource; - mObjects = objects; - mFieldId = textViewResourceId; - } - /** * Returns the context associated with this array adapter. The context is used * to create views from the resource passed to the constructor. @@ -359,16 +361,16 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable { * {@inheritDoc} */ public View getView(int position, View convertView, ViewGroup parent) { - return createViewFromResource(position, convertView, parent, mResource); + return createViewFromResource(mInflater, position, convertView, parent, mResource); } - private View createViewFromResource(int position, View convertView, ViewGroup parent, - int resource) { + private View createViewFromResource(LayoutInflater inflater, int position, View convertView, + ViewGroup parent, int resource) { View view; TextView text; if (convertView == null) { - view = mInflater.inflate(resource, parent, false); + view = inflater.inflate(resource, parent, false); } else { view = convertView; } @@ -408,11 +410,40 @@ public class ArrayAdapter<T> extends BaseAdapter implements Filterable { } /** + * Sets the {@link Resources.Theme} against which drop-down views are + * inflated. + * <p> + * By default, drop-down views are inflated against the theme of the + * {@link Context} passed to the adapter's constructor. + * + * @param theme the theme against which to inflate drop-down views or + * {@code null} to use the theme from the adapter's context + * @see #getDropDownView(int, View, ViewGroup) + */ + @Override + public void setDropDownViewTheme(Resources.Theme theme) { + if (theme == null) { + mDropDownInflater = null; + } else if (theme == mInflater.getContext().getTheme()) { + mDropDownInflater = mInflater; + } else { + final Context context = new ContextThemeWrapper(mContext, theme); + mDropDownInflater = LayoutInflater.from(context); + } + } + + @Override + public Resources.Theme getDropDownViewTheme() { + return mDropDownInflater == null ? null : mDropDownInflater.getContext().getTheme(); + } + + /** * {@inheritDoc} */ @Override public View getDropDownView(int position, View convertView, ViewGroup parent) { - return createViewFromResource(position, convertView, parent, mDropDownResource); + final LayoutInflater inflater = mDropDownInflater == null ? mInflater : mDropDownInflater; + return createViewFromResource(inflater, position, convertView, parent, mDropDownResource); } /** diff --git a/core/java/android/widget/CursorAdapter.java b/core/java/android/widget/CursorAdapter.java index d4c5be0..8e318fd 100644 --- a/core/java/android/widget/CursorAdapter.java +++ b/core/java/android/widget/CursorAdapter.java @@ -17,11 +17,14 @@ package android.widget; import android.content.Context; +import android.content.res.Resources; import android.database.ContentObserver; import android.database.Cursor; import android.database.DataSetObserver; import android.os.Handler; import android.util.Log; +import android.view.ContextThemeWrapper; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -35,7 +38,7 @@ import android.view.ViewGroup; * columns. */ public abstract class CursorAdapter extends BaseAdapter implements Filterable, - CursorFilter.CursorFilterClient { + CursorFilter.CursorFilterClient, Spinner.ThemedSpinnerAdapter { /** * This field should be made private, so it is hidden from the SDK. * {@hide} @@ -57,6 +60,11 @@ public abstract class CursorAdapter extends BaseAdapter implements Filterable, */ protected Context mContext; /** + * Context used for {@link #getDropDownView(int, View, ViewGroup)}. + * {@hide} + */ + protected Context mDropDownContext; + /** * This field should be made private, so it is hidden from the SDK. * {@hide} */ @@ -185,6 +193,33 @@ public abstract class CursorAdapter extends BaseAdapter implements Filterable, } /** + * Sets the {@link Resources.Theme} against which drop-down views are + * inflated. + * <p> + * By default, drop-down views are inflated against the theme of the + * {@link Context} passed to the adapter's constructor. + * + * @param theme the theme against which to inflate drop-down views or + * {@code null} to use the theme from the adapter's context + * @see #newDropDownView(Context, Cursor, ViewGroup) + */ + @Override + public void setDropDownViewTheme(Resources.Theme theme) { + if (theme == null) { + mDropDownContext = null; + } else if (theme == mContext.getTheme()) { + mDropDownContext = mContext; + } else { + mDropDownContext = new ContextThemeWrapper(mContext, theme); + } + } + + @Override + public Resources.Theme getDropDownViewTheme() { + return mDropDownContext == null ? null : mDropDownContext.getTheme(); + } + + /** * Returns the cursor. * @return the cursor. */ @@ -258,20 +293,21 @@ public abstract class CursorAdapter extends BaseAdapter implements Filterable, @Override public View getDropDownView(int position, View convertView, ViewGroup parent) { if (mDataValid) { + final Context context = mDropDownContext == null ? mContext : mDropDownContext; mCursor.moveToPosition(position); - View v; + final View v; if (convertView == null) { - v = newDropDownView(mContext, mCursor, parent); + v = newDropDownView(context, mCursor, parent); } else { v = convertView; } - bindView(v, mContext, mCursor); + bindView(v, context, mCursor); return v; } else { return null; } } - + /** * Makes a new view to hold the data pointed to by cursor. * @param context Interface to application's global information diff --git a/core/java/android/widget/ResourceCursorAdapter.java b/core/java/android/widget/ResourceCursorAdapter.java index 7341c2c..100f919 100644 --- a/core/java/android/widget/ResourceCursorAdapter.java +++ b/core/java/android/widget/ResourceCursorAdapter.java @@ -17,7 +17,9 @@ package android.widget; import android.content.Context; +import android.content.res.Resources; import android.database.Cursor; +import android.view.ContextThemeWrapper; import android.view.View; import android.view.ViewGroup; import android.view.LayoutInflater; @@ -31,9 +33,10 @@ public abstract class ResourceCursorAdapter extends CursorAdapter { private int mLayout; private int mDropDownLayout; - + private LayoutInflater mInflater; - + private LayoutInflater mDropDownInflater; + /** * Constructor the enables auto-requery. * @@ -52,8 +55,9 @@ public abstract class ResourceCursorAdapter extends CursorAdapter { super(context, c); mLayout = mDropDownLayout = layout; mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mDropDownInflater = mInflater; } - + /** * Constructor with default behavior as per * {@link CursorAdapter#CursorAdapter(Context, Cursor, boolean)}; it is recommended @@ -74,6 +78,7 @@ public abstract class ResourceCursorAdapter extends CursorAdapter { super(context, c, autoRequery); mLayout = mDropDownLayout = layout; mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mDropDownInflater = mInflater; } /** @@ -91,11 +96,37 @@ public abstract class ResourceCursorAdapter extends CursorAdapter { super(context, c, flags); mLayout = mDropDownLayout = layout; mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mDropDownInflater = mInflater; + } + + /** + * Sets the {@link android.content.res.Resources.Theme} against which drop-down views are + * inflated. + * <p> + * By default, drop-down views are inflated against the theme of the + * {@link Context} passed to the adapter's constructor. + * + * @param theme the theme against which to inflate drop-down views or + * {@code null} to use the theme from the adapter's context + * @see #newDropDownView(Context, Cursor, ViewGroup) + */ + @Override + public void setDropDownViewTheme(Resources.Theme theme) { + super.setDropDownViewTheme(theme); + + if (theme == null) { + mDropDownInflater = null; + } else if (theme == mInflater.getContext().getTheme()) { + mDropDownInflater = mInflater; + } else { + final Context context = new ContextThemeWrapper(mContext, theme); + mDropDownInflater = LayoutInflater.from(context); + } } /** * Inflates view(s) from the specified XML file. - * + * * @see android.widget.CursorAdapter#newView(android.content.Context, * android.database.Cursor, ViewGroup) */ @@ -106,7 +137,7 @@ public abstract class ResourceCursorAdapter extends CursorAdapter { @Override public View newDropDownView(Context context, Cursor cursor, ViewGroup parent) { - return mInflater.inflate(mDropDownLayout, parent, false); + return mDropDownInflater.inflate(mDropDownLayout, parent, false); } /** diff --git a/core/java/android/widget/SimpleAdapter.java b/core/java/android/widget/SimpleAdapter.java index 98bcfff..a656712 100644 --- a/core/java/android/widget/SimpleAdapter.java +++ b/core/java/android/widget/SimpleAdapter.java @@ -17,6 +17,8 @@ package android.widget; import android.content.Context; +import android.content.res.Resources; +import android.view.ContextThemeWrapper; import android.view.View; import android.view.ViewGroup; import android.view.LayoutInflater; @@ -40,14 +42,14 @@ import java.util.Map; * If the returned value is false, the following views are then tried in order: * <ul> * <li> A view that implements Checkable (e.g. CheckBox). The expected bind value is a boolean. - * <li> TextView. The expected bind value is a string and {@link #setViewText(TextView, String)} + * <li> TextView. The expected bind value is a string and {@link #setViewText(TextView, String)} * is invoked. - * <li> ImageView. The expected bind value is a resource id or a string and - * {@link #setViewImage(ImageView, int)} or {@link #setViewImage(ImageView, String)} is invoked. + * <li> ImageView. The expected bind value is a resource id or a string and + * {@link #setViewImage(ImageView, int)} or {@link #setViewImage(ImageView, String)} is invoked. * </ul> * If no appropriate binding can be found, an {@link IllegalStateException} is thrown. */ -public class SimpleAdapter extends BaseAdapter implements Filterable { +public class SimpleAdapter extends BaseAdapter implements Filterable, Spinner.ThemedSpinnerAdapter { private int[] mTo; private String[] mFrom; private ViewBinder mViewBinder; @@ -58,12 +60,15 @@ public class SimpleAdapter extends BaseAdapter implements Filterable { private int mDropDownResource; private LayoutInflater mInflater; + /** Layout inflater used for {@link #getDropDownView(int, View, ViewGroup)}. */ + private LayoutInflater mDropDownInflater; + private SimpleFilter mFilter; private ArrayList<Map<String, ?>> mUnfilteredData; /** * Constructor - * + * * @param context The context where the View associated with this SimpleAdapter is running * @param data A List of Maps. Each entry in the List corresponds to one row in the list. The * Maps contain the data for each row, and should include all the entries specified in @@ -85,7 +90,6 @@ public class SimpleAdapter extends BaseAdapter implements Filterable { mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } - /** * @see android.widget.Adapter#getCount() */ @@ -111,14 +115,14 @@ public class SimpleAdapter extends BaseAdapter implements Filterable { * @see android.widget.Adapter#getView(int, View, ViewGroup) */ public View getView(int position, View convertView, ViewGroup parent) { - return createViewFromResource(position, convertView, parent, mResource); + return createViewFromResource(mInflater, position, convertView, parent, mResource); } - private View createViewFromResource(int position, View convertView, + private View createViewFromResource(LayoutInflater inflater, int position, View convertView, ViewGroup parent, int resource) { View v; if (convertView == null) { - v = mInflater.inflate(resource, parent, false); + v = inflater.inflate(resource, parent, false); } else { v = convertView; } @@ -135,12 +139,41 @@ public class SimpleAdapter extends BaseAdapter implements Filterable { * @see #getDropDownView(int, android.view.View, android.view.ViewGroup) */ public void setDropDownViewResource(int resource) { - this.mDropDownResource = resource; + mDropDownResource = resource; + } + + /** + * Sets the {@link android.content.res.Resources.Theme} against which drop-down views are + * inflated. + * <p> + * By default, drop-down views are inflated against the theme of the + * {@link Context} passed to the adapter's constructor. + * + * @param theme the theme against which to inflate drop-down views or + * {@code null} to use the theme from the adapter's context + * @see #getDropDownView(int, View, ViewGroup) + */ + @Override + public void setDropDownViewTheme(Resources.Theme theme) { + if (theme == null) { + mDropDownInflater = null; + } else if (theme == mInflater.getContext().getTheme()) { + mDropDownInflater = mInflater; + } else { + final Context context = new ContextThemeWrapper(mInflater.getContext(), theme); + mDropDownInflater = LayoutInflater.from(context); + } + } + + @Override + public Resources.Theme getDropDownViewTheme() { + return mDropDownInflater == null ? null : mDropDownInflater.getContext().getTheme(); } @Override public View getDropDownView(int position, View convertView, ViewGroup parent) { - return createViewFromResource(position, convertView, parent, mDropDownResource); + return createViewFromResource( + mDropDownInflater, position, convertView, parent, mDropDownResource); } private void bindView(int position, View view) { diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java index 5c93b60..192b057 100644 --- a/core/java/android/widget/Spinner.java +++ b/core/java/android/widget/Spinner.java @@ -16,11 +16,15 @@ package android.widget; +import com.android.internal.R; + +import android.annotation.Nullable; import android.annotation.Widget; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; +import android.content.res.Resources; import android.content.res.TypedArray; import android.database.DataSetObserver; import android.graphics.Rect; @@ -30,6 +34,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; +import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; @@ -74,12 +79,15 @@ public class Spinner extends AbsSpinner implements OnClickListener { * Use a dropdown anchored to the Spinner for selecting spinner options. */ public static final int MODE_DROPDOWN = 1; - + /** * Use the theme-supplied value to select the dropdown mode. */ private static final int MODE_THEME = -1; + /** Context used to inflate the popup window or dialog. */ + private Context mPopupContext; + /** Forwarding listener used to implement drag-to-open. */ private ForwardingListener mForwardingListener; @@ -191,10 +199,18 @@ public class Spinner extends AbsSpinner implements OnClickListener { final TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.Spinner, defStyleAttr, defStyleRes); + final int popupThemeResId = a.getResourceId( + com.android.internal.R.styleable.Spinner_popupTheme, 0); + if (popupThemeResId != 0) { + mPopupContext = new ContextThemeWrapper(context, popupThemeResId); + } else { + mPopupContext = context; + } + if (mode == MODE_THEME) { mode = a.getInt(com.android.internal.R.styleable.Spinner_spinnerMode, MODE_DIALOG); } - + switch (mode) { case MODE_DIALOG: { mPopup = new DialogPopup(); @@ -202,13 +218,15 @@ public class Spinner extends AbsSpinner implements OnClickListener { } case MODE_DROPDOWN: { - final DropdownPopup popup = new DropdownPopup(context, attrs, defStyleAttr, defStyleRes); + final DropdownPopup popup = new DropdownPopup( + mPopupContext, attrs, defStyleAttr, defStyleRes); - mDropDownWidth = a.getLayoutDimension( - com.android.internal.R.styleable.Spinner_dropDownWidth, + final TypedArray pa = mPopupContext.obtainStyledAttributes( + attrs, R.styleable.Spinner, defStyleAttr, defStyleRes); + mDropDownWidth = pa.getLayoutDimension(R.styleable.Spinner_dropDownWidth, ViewGroup.LayoutParams.WRAP_CONTENT); - popup.setBackgroundDrawable(a.getDrawable( - com.android.internal.R.styleable.Spinner_popupBackground)); + popup.setBackgroundDrawable(pa.getDrawable(R.styleable.Spinner_popupBackground)); + pa.recycle(); mPopup = popup; mForwardingListener = new ForwardingListener(this) { @@ -247,6 +265,27 @@ public class Spinner extends AbsSpinner implements OnClickListener { } /** + * Sets the context against which the Spinner's popup or dialog window is + * inflated. + * <p> + * This method must be called before the popup or dialog window has been + * displayed. + * + * @param popupContext context used to inflate the Spinner's popup or + * dialog window + */ + public void setPopupContext(Context popupContext) { + mPopupContext = popupContext; + } + + /** + * @return the context used to inflate the Spinner's popup or dialog window + */ + public Context getPopupContext() { + return mPopupContext; + } + + /** * Set the background drawable for the spinner's popup window of choices. * Only valid in {@link #MODE_DROPDOWN}; this method is a no-op in other modes. * @@ -259,7 +298,7 @@ public class Spinner extends AbsSpinner implements OnClickListener { Log.e(TAG, "setPopupBackgroundDrawable: incompatible spinner mode; ignoring..."); return; } - ((DropdownPopup) mPopup).setBackgroundDrawable(background); + mPopup.setBackgroundDrawable(background); } /** @@ -271,7 +310,7 @@ public class Spinner extends AbsSpinner implements OnClickListener { * @attr ref android.R.styleable#Spinner_popupBackground */ public void setPopupBackgroundResource(int resId) { - setPopupBackgroundDrawable(getContext().getDrawable(resId)); + setPopupBackgroundDrawable(getPopupContext().getDrawable(resId)); } /** @@ -410,9 +449,17 @@ public class Spinner extends AbsSpinner implements OnClickListener { } /** - * Sets the Adapter used to provide the data which backs this Spinner. + * Sets the {@link SpinnerAdapter} used to provide the data which backs + * this Spinner. + * <p> + * If this Spinner has a popup theme set in XML via the + * {@link android.R.styleable#Spinner_popupTheme popupTheme} attribute, the + * adapter should inflate drop-down views using the same theme. The easiest + * way to achieve this is by using {@link #getPopupContext()} to obtain a + * layout inflater for use in + * {@link SpinnerAdapter#getDropDownView(int, View, ViewGroup)}. * <p> - * Note that Spinner overrides {@link Adapter#getViewTypeCount()} on the + * Spinner overrides {@link Adapter#getViewTypeCount()} on the * Adapter associated with this view. Calling * {@link Adapter#getItemViewType(int) getItemViewType(int)} on the object * returned from {@link #getAdapter()} will always return 0. Calling @@ -440,9 +487,9 @@ public class Spinner extends AbsSpinner implements OnClickListener { } if (mPopup != null) { - mPopup.setAdapter(new DropDownAdapter(adapter)); + mPopup.setAdapter(new DropDownAdapter(adapter, mPopupContext.getTheme())); } else { - mTempAdapter = new DropDownAdapter(adapter); + mTempAdapter = new DropDownAdapter(adapter, mPopupContext.getTheme()); } } @@ -849,14 +896,26 @@ public class Spinner extends AbsSpinner implements OnClickListener { private ListAdapter mListAdapter; /** - * <p>Creates a new ListAdapter wrapper for the specified adapter.</p> + * Creates a new ListAdapter wrapper for the specified adapter. * - * @param adapter the Adapter to transform into a ListAdapter + * @param adapter the SpinnerAdapter to transform into a ListAdapter + * @param dropDownTheme the theme against which to inflate drop-down + * views, may be {@null} to use default theme */ - public DropDownAdapter(SpinnerAdapter adapter) { - this.mAdapter = adapter; + public DropDownAdapter(@Nullable SpinnerAdapter adapter, + @Nullable Resources.Theme dropDownTheme) { + mAdapter = adapter; + if (adapter instanceof ListAdapter) { - this.mListAdapter = (ListAdapter) adapter; + mListAdapter = (ListAdapter) adapter; + } + + if (dropDownTheme != null && adapter instanceof Spinner.ThemedSpinnerAdapter) { + final Spinner.ThemedSpinnerAdapter themedAdapter = + (Spinner.ThemedSpinnerAdapter) adapter; + if (themedAdapter.getDropDownViewTheme() == null) { + themedAdapter.setDropDownViewTheme(dropDownTheme); + } } } @@ -996,7 +1055,7 @@ public class Spinner extends AbsSpinner implements OnClickListener { public void setPromptText(CharSequence hintText) { mPrompt = hintText; } - + public CharSequence getHintText() { return mPrompt; } @@ -1005,7 +1064,7 @@ public class Spinner extends AbsSpinner implements OnClickListener { if (mListAdapter == null) { return; } - AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + AlertDialog.Builder builder = new AlertDialog.Builder(getPopupContext()); if (mPrompt != null) { builder.setTitle(mPrompt); } @@ -1181,4 +1240,21 @@ public class Spinner extends AbsSpinner implements OnClickListener { } } } + + public interface ThemedSpinnerAdapter { + /** + * Sets the {@link Resources.Theme} against which drop-down views are + * inflated. + * + * @param theme the context against which to inflate drop-down views + * @see SpinnerAdapter#getDropDownView(int, View, ViewGroup) + */ + public void setDropDownViewTheme(Resources.Theme theme); + + /** + * @return The {@link Resources.Theme} against which drop-down views are + * inflated. + */ + public Resources.Theme getDropDownViewTheme(); + } } diff --git a/core/java/android/widget/SpinnerAdapter.java b/core/java/android/widget/SpinnerAdapter.java index 91504cf..f99f45b 100644 --- a/core/java/android/widget/SpinnerAdapter.java +++ b/core/java/android/widget/SpinnerAdapter.java @@ -22,16 +22,17 @@ import android.view.ViewGroup; /** * Extended {@link Adapter} that is the bridge between a * {@link android.widget.Spinner} and its data. A spinner adapter allows to - * define two different views: one that shows the data in the spinner itself and - * one that shows the data in the drop down list when the spinner is pressed.</p> + * define two different views: one that shows the data in the spinner itself + * and one that shows the data in the drop down list when the spinner is + * pressed. */ public interface SpinnerAdapter extends Adapter { /** - * <p>Get a {@link android.view.View} that displays in the drop down popup - * the data at the specified position in the data set.</p> + * Gets a {@link android.view.View} that displays in the drop down popup + * the data at the specified position in the data set. * - * @param position index of the item whose view we want. - * @param convertView the old view to reuse, if possible. Note: You should + * @param position index of the item whose view we want. + * @param convertView the old view to reuse, if possible. Note: You should * check that this view is non-null and of an appropriate type before * using. If it is not possible to convert this view to display the * correct data, this method can create a new view. diff --git a/core/java/android/widget/SuggestionsAdapter.java b/core/java/android/widget/SuggestionsAdapter.java index 6349347..4323851 100644 --- a/core/java/android/widget/SuggestionsAdapter.java +++ b/core/java/android/widget/SuggestionsAdapter.java @@ -145,8 +145,9 @@ class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListene * copied to the query text field. * <p> * - * @param refine which queries to refine. Possible values are {@link #REFINE_NONE}, - * {@link #REFINE_BY_ENTRY}, and {@link #REFINE_ALL}. + * @param refineWhat which queries to refine. Possible values are + * {@link #REFINE_NONE}, {@link #REFINE_BY_ENTRY}, and + * {@link #REFINE_ALL}. */ public void setQueryRefinement(int refineWhat) { mQueryRefinement = refineWhat; @@ -327,7 +328,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListene // First check TEXT_2_URL CharSequence text2 = getStringOrNull(cursor, mText2UrlCol); if (text2 != null) { - text2 = formatUrl(text2); + text2 = formatUrl(context, text2); } else { text2 = getStringOrNull(cursor, mText2Col); } @@ -372,12 +373,12 @@ class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListene } } - private CharSequence formatUrl(CharSequence url) { + private CharSequence formatUrl(Context context, CharSequence url) { if (mUrlColor == null) { // Lazily get the URL color from the current theme. TypedValue colorValue = new TypedValue(); - mContext.getTheme().resolveAttribute(R.attr.textColorSearchUrl, colorValue, true); - mUrlColor = mContext.getResources().getColorStateList(colorValue.resourceId); + context.getTheme().resolveAttribute(R.attr.textColorSearchUrl, colorValue, true); + mUrlColor = context.getResources().getColorStateList(colorValue.resourceId); } SpannableString text = new SpannableString(url); @@ -502,6 +503,30 @@ class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListene } /** + * This method is overridden purely to provide a bit of protection against + * flaky content providers. + * + * @see android.widget.CursorAdapter#getDropDownView(int, View, ViewGroup) + */ + @Override + public View getDropDownView(int position, View convertView, ViewGroup parent) { + try { + return super.getDropDownView(position, convertView, parent); + } catch (RuntimeException e) { + Log.w(LOG_TAG, "Search suggestions cursor threw exception.", e); + // Put exception string in item title + final Context context = mDropDownContext == null ? mContext : mDropDownContext; + final View v = newDropDownView(context, mCursor, parent); + if (v != null) { + final ChildViewCache views = (ChildViewCache) v.getTag(); + final TextView tv = views.mText1; + tv.setText(e.toString()); + } + return v; + } + } + + /** * Gets a drawable given a value provided by a suggestion provider. * * This value could be just the string value of a resource id @@ -570,7 +595,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListene OpenResourceIdResult r = mProviderContext.getContentResolver().getResourceId(uri); try { - return r.r.getDrawable(r.id, mContext.getTheme()); + return r.r.getDrawable(r.id, mProviderContext.getTheme()); } catch (Resources.NotFoundException ex) { throw new FileNotFoundException("Resource does not exist: " + uri); } |