summaryrefslogtreecommitdiffstats
path: root/core/java/android/widget
diff options
context:
space:
mode:
authorAlan Viverette <alanv@google.com>2015-01-14 10:43:31 -0800
committerAlan Viverette <alanv@google.com>2015-01-14 10:43:31 -0800
commitb9ead4a91599ca63e947f74f83b67a58bda64a82 (patch)
treee402c3db4afcf539fce6c379782732aeed19069f /core/java/android/widget
parentdbf22ccd92dc95e7c93c0485be4bcfb5c966578a (diff)
downloadframeworks_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.java71
-rw-r--r--core/java/android/widget/CursorAdapter.java46
-rw-r--r--core/java/android/widget/ResourceCursorAdapter.java41
-rw-r--r--core/java/android/widget/SimpleAdapter.java55
-rw-r--r--core/java/android/widget/Spinner.java116
-rw-r--r--core/java/android/widget/SpinnerAdapter.java13
-rw-r--r--core/java/android/widget/SuggestionsAdapter.java39
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);
}