Fix Holo Spinners
Fix bug 3321329 - Spinner dropdown does not match widget width
Fix a bug in ListPopupWindow where setContentWidth was misbehaving
Add gravity setting to Spinner. This controls the positioning of the
selected item view within the widget. Holo default is left, legacy
default is center.
In dropdown mode, Spinners now WRAP_CONTENT width based on a limited
set of dropdown content. This means the dropdown can display a
reasonable amount of its content without clipping, while matching
width with the Spinner widget itself.
Change-Id: Ia17fd5f71526548408f4ad3b16bde536b0d3b207
diff --git a/api/current.xml b/api/current.xml
index fe5ea76..6d0253d 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -253039,6 +253039,19 @@
<parameter name="which" type="int">
</parameter>
</method>
+<method name="setGravity"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="gravity" type="int">
+</parameter>
+</method>
<method name="setPrompt"
return="void"
abstract="false"
@@ -257911,7 +257924,7 @@
deprecated="not deprecated"
visibility="public"
>
-<parameter name="t" type="T">
+<parameter name="arg0" type="T">
</parameter>
</method>
</interface>
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index 9c483ee..3bba816 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -440,7 +440,8 @@
public void setContentWidth(int width) {
Drawable popupBackground = mPopup.getBackground();
if (popupBackground != null) {
- mDropDownWidth = popupBackground.getIntrinsicWidth() + width;
+ popupBackground.getPadding(mTempRect);
+ mDropDownWidth = mTempRect.left + mTempRect.right + width;
}
}
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index bdf24e0..5a08442 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -25,7 +25,8 @@
import android.database.DataSetObserver;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
-import android.view.LayoutInflater;
+import android.util.Log;
+import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
@@ -65,6 +66,8 @@
private SpinnerPopup mPopup;
private DropDownAdapter mTempAdapter;
+ private int mGravity;
+
/**
* Construct a new spinner with the given context's theme.
*
@@ -152,10 +155,7 @@
}
case MODE_DROPDOWN: {
- final int hintResource = a.getResourceId(
- com.android.internal.R.styleable.Spinner_popupPromptView, 0);
-
- DropdownPopup popup = new DropdownPopup(context, attrs, defStyle, hintResource);
+ DropdownPopup popup = new DropdownPopup(context, attrs, defStyle);
popup.setWidth(a.getLayoutDimension(
com.android.internal.R.styleable.Spinner_dropDownWidth,
@@ -172,6 +172,8 @@
}
}
+ mGravity = a.getInt(com.android.internal.R.styleable.Spinner_gravity, Gravity.CENTER);
+
mPopup.setPromptText(a.getString(com.android.internal.R.styleable.Spinner_prompt));
a.recycle();
@@ -183,7 +185,25 @@
mTempAdapter = null;
}
}
-
+
+ /**
+ * Describes how the selected item view is positioned. Currently only the horizontal component
+ * is used. The default is determined by the current theme.
+ *
+ * @param gravity See {@link android.view.Gravity}
+ *
+ * @attr ref android.R.styleable#Spinner_gravity
+ */
+ public void setGravity(int gravity) {
+ if (mGravity != gravity) {
+ if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == 0) {
+ gravity |= Gravity.LEFT;
+ }
+ mGravity = gravity;
+ requestLayout();
+ }
+ }
+
@Override
public void setAdapter(SpinnerAdapter adapter) {
super.setAdapter(adapter);
@@ -234,6 +254,18 @@
throw new RuntimeException("setOnItemClickListener cannot be used with a spinner.");
}
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (mPopup != null && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) {
+ final int measuredWidth = getMeasuredWidth();
+ setMeasuredDimension(Math.min(Math.max(measuredWidth, mPopup.measureContentWidth()),
+ MeasureSpec.getSize(widthMeasureSpec)),
+ getMeasuredHeight());
+ Log.d(TAG, "onMeasure - old measured width " + measuredWidth + " new " + getMeasuredWidth());
+ }
+ }
+
/**
* @see android.view.View#onLayout(boolean,int,int,int,int)
*
@@ -278,11 +310,19 @@
// Clear out old views
removeAllViewsInLayout();
- // Make selected view and center it
+ // Make selected view and position it
mFirstPosition = mSelectedPosition;
View sel = makeAndAddView(mSelectedPosition);
int width = sel.getMeasuredWidth();
- int selectedOffset = childrenLeft + (childrenWidth / 2) - (width / 2);
+ int selectedOffset = childrenLeft;
+ switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+ case Gravity.CENTER_HORIZONTAL:
+ selectedOffset = childrenLeft + (childrenWidth / 2) - (width / 2);
+ break;
+ case Gravity.RIGHT:
+ selectedOffset = childrenLeft + childrenWidth - width;
+ break;
+ }
sel.offsetLeftAndRight(selectedOffset);
// Flush any cached views that did not get reused above
@@ -541,6 +581,8 @@
*/
public void setPromptText(CharSequence hintText);
public CharSequence getHintText();
+
+ public int measureContentWidth();
}
private class DialogPopup implements SpinnerPopup, DialogInterface.OnClickListener {
@@ -582,20 +624,20 @@
setSelection(which);
dismiss();
}
+
+ public int measureContentWidth() {
+ // Doesn't matter for dialog mode
+ return 0;
+ }
}
private class DropdownPopup extends ListPopupWindow implements SpinnerPopup {
private CharSequence mHintText;
- private TextView mHintView;
- private int mHintResource;
private int mPopupMaxWidth;
- public DropdownPopup(Context context, AttributeSet attrs,
- int defStyleRes, int hintResource) {
+ public DropdownPopup(Context context, AttributeSet attrs, int defStyleRes) {
super(context, attrs, 0, defStyleRes);
- mHintResource = hintResource;
-
final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
mPopupMaxWidth = metrics.widthPixels / 2;
@@ -615,30 +657,21 @@
}
public void setPromptText(CharSequence hintText) {
+ // Hint text is ignored for dropdowns, but maintain it here.
mHintText = hintText;
- if (mHintView != null) {
- mHintView.setText(hintText);
- }
}
@Override
public void show() {
- if (mHintView == null) {
- final TextView textView = (TextView) LayoutInflater.from(getContext()).inflate(
- mHintResource, null).findViewById(com.android.internal.R.id.text1);
- textView.setText(mHintText);
- setPromptView(textView);
- mHintView = textView;
- }
- setContentWidth(Math.min(
- Math.max(measureContentWidth(getAdapter()), Spinner.this.getWidth()),
- mPopupMaxWidth));
+ setWidth(Spinner.this.getWidth());
super.show();
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
setSelection(Spinner.this.getSelectedItemPosition());
}
- private int measureContentWidth(SpinnerAdapter adapter) {
+ @Override
+ public int measureContentWidth() {
+ final SpinnerAdapter adapter = getAdapter();
int width = 0;
View itemView = null;
int itemType = 0;
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 2d798c9..14c8f46 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2822,6 +2822,8 @@
spinnerMode="dropdown". This layout must contain a TextView with the id
@android:id/text1 to be populated with the prompt text. -->
<attr name="popupPromptView" format="reference" />
+ <!-- Gravity setting for positioning the currently selected item. -->
+ <attr name="gravity" />
</declare-styleable>
<declare-styleable name="DatePicker">
<!-- The first year (inclusive), for example "1940". -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 8a86676..470fb36 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -530,6 +530,7 @@
<item name="android:dropDownHorizontalOffset">0dip</item>
<item name="android:dropDownWidth">wrap_content</item>
<item name="android:popupPromptView">@android:layout/simple_dropdown_hint</item>
+ <item name="android:gravity">center</item>
</style>
<style name="Widget.Spinner.DropDown">
@@ -1570,6 +1571,7 @@
<item name="android:dropDownHorizontalOffset">0dip</item>
<item name="android:dropDownWidth">wrap_content</item>
<item name="android:popupPromptView">@android:layout/simple_dropdown_hint</item>
+ <item name="android:gravity">left|center_vertical</item>
</style>
<style name="Widget.Holo.Spinner.DropDown">