Ensure spinner variables are initialized correctly

Removes the popup context setter, since this property cannot change
after inflation without losing data from the AttributeSet.

Bug: 19046761
Change-Id: I622f50d1752446a5b5793706c388e67a46ad06da
This commit is contained in:
Alan Viverette
2015-01-16 14:40:04 -08:00
parent c30e076875
commit 3f221cf325
3 changed files with 97 additions and 72 deletions

View File

@@ -38843,6 +38843,7 @@ package android.widget {
ctor public Spinner(android.content.Context, android.util.AttributeSet, int);
ctor public Spinner(android.content.Context, android.util.AttributeSet, int, int);
ctor public Spinner(android.content.Context, android.util.AttributeSet, int, int, int);
ctor public Spinner(android.content.Context, android.util.AttributeSet, int, int, int, android.content.Context);
method public int getDropDownHorizontalOffset();
method public int getDropDownVerticalOffset();
method public int getDropDownWidth();
@@ -38857,7 +38858,6 @@ package android.widget {
method public void setGravity(int);
method public void setPopupBackgroundDrawable(android.graphics.drawable.Drawable);
method public void setPopupBackgroundResource(int);
method public void setPopupContext(android.content.Context);
method public void setPrompt(java.lang.CharSequence);
method public void setPromptId(int);
field public static final int MODE_DIALOG = 0; // 0x0

View File

@@ -73,13 +73,12 @@ public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> {
initAbsSpinner();
final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.AbsSpinner, defStyleAttr, defStyleRes);
attrs, R.styleable.AbsSpinner, defStyleAttr, defStyleRes);
CharSequence[] entries = a.getTextArray(R.styleable.AbsSpinner_entries);
final CharSequence[] entries = a.getTextArray(R.styleable.AbsSpinner_entries);
if (entries != null) {
ArrayAdapter<CharSequence> adapter =
new ArrayAdapter<CharSequence>(context,
R.layout.simple_spinner_item, entries);
final ArrayAdapter<CharSequence> adapter = new ArrayAdapter<CharSequence>(
context, R.layout.simple_spinner_item, entries);
adapter.setDropDownViewResource(R.layout.simple_spinner_dropdown_item);
setAdapter(adapter);
}

View File

@@ -91,8 +91,10 @@ public class Spinner extends AbsSpinner implements OnClickListener {
/** Forwarding listener used to implement drag-to-open. */
private ForwardingListener mForwardingListener;
/** Temporary holder for setAdapter() calls from the super constructor. */
private SpinnerAdapter mTempAdapter;
private SpinnerPopup mPopup;
private DropDownAdapter mTempAdapter;
int mDropDownWidth;
private int mGravity;
@@ -192,92 +194,112 @@ public class Spinner extends AbsSpinner implements OnClickListener {
* @see #MODE_DIALOG
* @see #MODE_DROPDOWN
*/
public Spinner(
Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes, int mode) {
public Spinner(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes,
int mode) {
this(context, attrs, defStyleAttr, defStyleRes, mode, null);
}
/**
* Constructs a new spinner with the given context's theme, the supplied
* attribute set, default styles, popup mode (one of {@link #MODE_DIALOG}
* or {@link #MODE_DROPDOWN}), and the context against which the popup
* should be inflated.
*
* @param context The context against which the view is inflated, which
* provides access to the current theme, resources, etc.
* @param attrs The attributes of the XML tag that is inflating the view.
* @param defStyleAttr An attribute in the current theme that contains a
* reference to a style resource that supplies default
* values for the view. Can be 0 to not look for
* defaults.
* @param defStyleRes A resource identifier of a style resource that
* supplies default values for the view, used only if
* defStyleAttr is 0 or can not be found in the theme.
* Can be 0 to not look for defaults.
* @param mode Constant describing how the user will select choices from
* the spinner.
* @param popupContext The context against which the dialog or dropdown
* popup will be inflated. Can be null to use the view
* context. If set, this will override any value
* specified by
* {@link android.R.styleable#Spinner_popupTheme}.
*
* @see #MODE_DIALOG
* @see #MODE_DROPDOWN
*/
public Spinner(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes, int mode,
Context popupContext) {
super(context, attrs, defStyleAttr, defStyleRes);
final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.Spinner, defStyleAttr, defStyleRes);
attrs, 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);
if (popupContext != null) {
mPopupContext = popupContext;
} else {
mPopupContext = context;
final int popupThemeResId = a.getResourceId(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);
mode = a.getInt(R.styleable.Spinner_spinnerMode, MODE_DIALOG);
}
switch (mode) {
case MODE_DIALOG: {
mPopup = new DialogPopup();
break;
}
case MODE_DIALOG: {
mPopup = new DialogPopup();
mPopup.setPromptText(a.getString(R.styleable.Spinner_prompt));
break;
}
case MODE_DROPDOWN: {
final DropdownPopup popup = new DropdownPopup(
mPopupContext, attrs, defStyleAttr, defStyleRes);
case MODE_DROPDOWN: {
final DropdownPopup popup = new DropdownPopup(
mPopupContext, attrs, defStyleAttr, defStyleRes);
final TypedArray pa = mPopupContext.obtainStyledAttributes(
attrs, R.styleable.Spinner, defStyleAttr, defStyleRes);
mDropDownWidth = pa.getLayoutDimension(R.styleable.Spinner_dropDownWidth,
ViewGroup.LayoutParams.WRAP_CONTENT);
popup.setBackgroundDrawable(pa.getDrawable(R.styleable.Spinner_popupBackground));
popup.setPromptText(a.getString(R.styleable.Spinner_prompt));
pa.recycle();
final TypedArray pa = mPopupContext.obtainStyledAttributes(
attrs, R.styleable.Spinner, defStyleAttr, defStyleRes);
mDropDownWidth = pa.getLayoutDimension(R.styleable.Spinner_dropDownWidth,
ViewGroup.LayoutParams.WRAP_CONTENT);
popup.setBackgroundDrawable(pa.getDrawable(R.styleable.Spinner_popupBackground));
pa.recycle();
mPopup = popup;
mForwardingListener = new ForwardingListener(this) {
@Override
public ListPopupWindow getPopup() {
return popup;
}
@Override
public boolean onForwardingStarted() {
if (!mPopup.isShowing()) {
mPopup.show(getTextDirection(), getTextAlignment());
mPopup = popup;
mForwardingListener = new ForwardingListener(this) {
@Override
public ListPopupWindow getPopup() {
return popup;
}
return true;
}
};
break;
}
@Override
public boolean onForwardingStarted() {
if (!mPopup.isShowing()) {
mPopup.show(getTextDirection(), getTextAlignment());
}
return true;
}
};
break;
}
}
mGravity = a.getInt(com.android.internal.R.styleable.Spinner_gravity, Gravity.CENTER);
mPopup.setPromptText(a.getString(com.android.internal.R.styleable.Spinner_prompt));
mGravity = a.getInt(R.styleable.Spinner_gravity, Gravity.CENTER);
mDisableChildrenWhenDisabled = a.getBoolean(
com.android.internal.R.styleable.Spinner_disableChildrenWhenDisabled, false);
R.styleable.Spinner_disableChildrenWhenDisabled, false);
a.recycle();
// Base constructor can call setAdapter before we initialize mPopup.
// Finish setting things up if this happened.
if (mTempAdapter != null) {
mPopup.setAdapter(mTempAdapter);
setAdapter(mTempAdapter);
mTempAdapter = null;
}
}
/**
* 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
*/
@@ -476,6 +498,13 @@ public class Spinner extends AbsSpinner implements OnClickListener {
*/
@Override
public void setAdapter(SpinnerAdapter adapter) {
// The super constructor may call setAdapter before we're prepared.
// Postpone doing anything until we've finished construction.
if (mPopup == null) {
mTempAdapter = adapter;
return;
}
super.setAdapter(adapter);
mRecycler.clear();
@@ -486,11 +515,8 @@ public class Spinner extends AbsSpinner implements OnClickListener {
throw new IllegalArgumentException("Spinner adapter view type count must be 1");
}
if (mPopup != null) {
mPopup.setAdapter(new DropDownAdapter(adapter, mPopupContext.getTheme()));
} else {
mTempAdapter = new DropDownAdapter(adapter, mPopupContext.getTheme());
}
final Context popupContext = mPopupContext == null ? mContext : mPopupContext;
mPopup.setAdapter(new DropDownAdapter(adapter, popupContext.getTheme()));
}
@Override
@@ -1031,7 +1057,7 @@ public class Spinner extends AbsSpinner implements OnClickListener {
public int getVerticalOffset();
public int getHorizontalOffset();
}
private class DialogPopup implements SpinnerPopup, DialogInterface.OnClickListener {
private AlertDialog mPopup;
private ListAdapter mListAdapter;