Spinner.java revision 95148495fc3d33e4ad4dcbdee83317422db94dbf
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.widget; 18 19import android.annotation.Widget; 20import android.app.AlertDialog; 21import android.content.Context; 22import android.content.DialogInterface; 23import android.content.DialogInterface.OnClickListener; 24import android.content.res.TypedArray; 25import android.database.DataSetObserver; 26import android.graphics.Rect; 27import android.graphics.drawable.Drawable; 28import android.util.AttributeSet; 29import android.util.DisplayMetrics; 30import android.view.Gravity; 31import android.view.View; 32import android.view.ViewGroup; 33 34 35/** 36 * A view that displays one child at a time and lets the user pick among them. 37 * The items in the Spinner come from the {@link Adapter} associated with 38 * this view. 39 * 40 * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-spinner.html">Spinner 41 * tutorial</a>.</p> 42 * 43 * @attr ref android.R.styleable#Spinner_prompt 44 */ 45@Widget 46public class Spinner extends AbsSpinner implements OnClickListener { 47 private static final String TAG = "Spinner"; 48 49 // Only measure this many items to get a decent max width. 50 private static final int MAX_ITEMS_MEASURED = 15; 51 52 /** 53 * Use a dialog window for selecting spinner options. 54 */ 55 public static final int MODE_DIALOG = 0; 56 57 /** 58 * Use a dropdown anchored to the Spinner for selecting spinner options. 59 */ 60 public static final int MODE_DROPDOWN = 1; 61 62 /** 63 * Use the theme-supplied value to select the dropdown mode. 64 */ 65 private static final int MODE_THEME = -1; 66 67 private SpinnerPopup mPopup; 68 private DropDownAdapter mTempAdapter; 69 int mDropDownWidth; 70 71 private int mGravity; 72 private boolean mDisableChildrenWhenDisabled; 73 74 private Rect mTempRect = new Rect(); 75 76 /** 77 * Construct a new spinner with the given context's theme. 78 * 79 * @param context The Context the view is running in, through which it can 80 * access the current theme, resources, etc. 81 */ 82 public Spinner(Context context) { 83 this(context, null); 84 } 85 86 /** 87 * Construct a new spinner with the given context's theme and the supplied 88 * mode of displaying choices. <code>mode</code> may be one of 89 * {@link #MODE_DIALOG} or {@link #MODE_DROPDOWN}. 90 * 91 * @param context The Context the view is running in, through which it can 92 * access the current theme, resources, etc. 93 * @param mode Constant describing how the user will select choices from the spinner. 94 * 95 * @see #MODE_DIALOG 96 * @see #MODE_DROPDOWN 97 */ 98 public Spinner(Context context, int mode) { 99 this(context, null, com.android.internal.R.attr.spinnerStyle, mode); 100 } 101 102 /** 103 * Construct a new spinner with the given context's theme and the supplied attribute set. 104 * 105 * @param context The Context the view is running in, through which it can 106 * access the current theme, resources, etc. 107 * @param attrs The attributes of the XML tag that is inflating the view. 108 */ 109 public Spinner(Context context, AttributeSet attrs) { 110 this(context, attrs, com.android.internal.R.attr.spinnerStyle); 111 } 112 113 /** 114 * Construct a new spinner with the given context's theme, the supplied attribute set, 115 * and default style. 116 * 117 * @param context The Context the view is running in, through which it can 118 * access the current theme, resources, etc. 119 * @param attrs The attributes of the XML tag that is inflating the view. 120 * @param defStyle The default style to apply to this view. If 0, no style 121 * will be applied (beyond what is included in the theme). This may 122 * either be an attribute resource, whose value will be retrieved 123 * from the current theme, or an explicit style resource. 124 */ 125 public Spinner(Context context, AttributeSet attrs, int defStyle) { 126 this(context, attrs, defStyle, MODE_THEME); 127 } 128 129 /** 130 * Construct a new spinner with the given context's theme, the supplied attribute set, 131 * and default style. <code>mode</code> may be one of {@link #MODE_DIALOG} or 132 * {@link #MODE_DROPDOWN} and determines how the user will select choices from the spinner. 133 * 134 * @param context The Context the view is running in, through which it can 135 * access the current theme, resources, etc. 136 * @param attrs The attributes of the XML tag that is inflating the view. 137 * @param defStyle The default style to apply to this view. If 0, no style 138 * will be applied (beyond what is included in the theme). This may 139 * either be an attribute resource, whose value will be retrieved 140 * from the current theme, or an explicit style resource. 141 * @param mode Constant describing how the user will select choices from the spinner. 142 * 143 * @see #MODE_DIALOG 144 * @see #MODE_DROPDOWN 145 */ 146 public Spinner(Context context, AttributeSet attrs, int defStyle, int mode) { 147 super(context, attrs, defStyle); 148 149 TypedArray a = context.obtainStyledAttributes(attrs, 150 com.android.internal.R.styleable.Spinner, defStyle, 0); 151 152 if (mode == MODE_THEME) { 153 mode = a.getInt(com.android.internal.R.styleable.Spinner_spinnerMode, MODE_DIALOG); 154 } 155 156 switch (mode) { 157 case MODE_DIALOG: { 158 mPopup = new DialogPopup(); 159 break; 160 } 161 162 case MODE_DROPDOWN: { 163 DropdownPopup popup = new DropdownPopup(context, attrs, defStyle); 164 165 mDropDownWidth = a.getLayoutDimension( 166 com.android.internal.R.styleable.Spinner_dropDownWidth, 167 ViewGroup.LayoutParams.WRAP_CONTENT); 168 popup.setBackgroundDrawable(a.getDrawable( 169 com.android.internal.R.styleable.Spinner_popupBackground)); 170 final int verticalOffset = a.getDimensionPixelOffset( 171 com.android.internal.R.styleable.Spinner_dropDownVerticalOffset, 0); 172 if (verticalOffset != 0) { 173 popup.setVerticalOffset(verticalOffset); 174 } 175 176 final int horizontalOffset = a.getDimensionPixelOffset( 177 com.android.internal.R.styleable.Spinner_dropDownHorizontalOffset, 0); 178 if (horizontalOffset != 0) { 179 popup.setHorizontalOffset(horizontalOffset); 180 } 181 182 mPopup = popup; 183 break; 184 } 185 } 186 187 mGravity = a.getInt(com.android.internal.R.styleable.Spinner_gravity, Gravity.CENTER); 188 189 mPopup.setPromptText(a.getString(com.android.internal.R.styleable.Spinner_prompt)); 190 191 mDisableChildrenWhenDisabled = a.getBoolean( 192 com.android.internal.R.styleable.Spinner_disableChildrenWhenDisabled, false); 193 194 a.recycle(); 195 196 // Base constructor can call setAdapter before we initialize mPopup. 197 // Finish setting things up if this happened. 198 if (mTempAdapter != null) { 199 mPopup.setAdapter(mTempAdapter); 200 mTempAdapter = null; 201 } 202 } 203 204 @Override 205 public void setEnabled(boolean enabled) { 206 super.setEnabled(enabled); 207 if (mDisableChildrenWhenDisabled) { 208 final int count = getChildCount(); 209 for (int i = 0; i < count; i++) { 210 getChildAt(i).setEnabled(enabled); 211 } 212 } 213 } 214 215 /** 216 * Describes how the selected item view is positioned. Currently only the horizontal component 217 * is used. The default is determined by the current theme. 218 * 219 * @param gravity See {@link android.view.Gravity} 220 * 221 * @attr ref android.R.styleable#Spinner_gravity 222 */ 223 public void setGravity(int gravity) { 224 if (mGravity != gravity) { 225 if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == 0) { 226 gravity |= Gravity.LEFT; 227 } 228 mGravity = gravity; 229 requestLayout(); 230 } 231 } 232 233 @Override 234 public void setAdapter(SpinnerAdapter adapter) { 235 super.setAdapter(adapter); 236 237 if (mPopup != null) { 238 mPopup.setAdapter(new DropDownAdapter(adapter)); 239 } else { 240 mTempAdapter = new DropDownAdapter(adapter); 241 } 242 } 243 244 @Override 245 public int getBaseline() { 246 View child = null; 247 248 if (getChildCount() > 0) { 249 child = getChildAt(0); 250 } else if (mAdapter != null && mAdapter.getCount() > 0) { 251 child = makeAndAddView(0); 252 mRecycler.put(0, child); 253 removeAllViewsInLayout(); 254 } 255 256 if (child != null) { 257 final int childBaseline = child.getBaseline(); 258 return childBaseline >= 0 ? child.getTop() + childBaseline : -1; 259 } else { 260 return -1; 261 } 262 } 263 264 @Override 265 protected void onDetachedFromWindow() { 266 super.onDetachedFromWindow(); 267 268 if (mPopup != null && mPopup.isShowing()) { 269 mPopup.dismiss(); 270 } 271 } 272 273 /** 274 * <p>A spinner does not support item click events. Calling this method 275 * will raise an exception.</p> 276 * 277 * @param l this listener will be ignored 278 */ 279 @Override 280 public void setOnItemClickListener(OnItemClickListener l) { 281 throw new RuntimeException("setOnItemClickListener cannot be used with a spinner."); 282 } 283 284 @Override 285 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 286 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 287 if (mPopup != null && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) { 288 final int measuredWidth = getMeasuredWidth(); 289 setMeasuredDimension(Math.min(Math.max(measuredWidth, 290 measureContentWidth(getAdapter(), getBackground())), 291 MeasureSpec.getSize(widthMeasureSpec)), 292 getMeasuredHeight()); 293 } 294 } 295 296 /** 297 * @see android.view.View#onLayout(boolean,int,int,int,int) 298 * 299 * Creates and positions all views 300 * 301 */ 302 @Override 303 protected void onLayout(boolean changed, int l, int t, int r, int b) { 304 super.onLayout(changed, l, t, r, b); 305 mInLayout = true; 306 layout(0, false); 307 mInLayout = false; 308 } 309 310 /** 311 * Creates and positions all views for this Spinner. 312 * 313 * @param delta Change in the selected position. +1 moves selection is moving to the right, 314 * so views are scrolling to the left. -1 means selection is moving to the left. 315 */ 316 @Override 317 void layout(int delta, boolean animate) { 318 int childrenLeft = mSpinnerPadding.left; 319 int childrenWidth = mRight - mLeft - mSpinnerPadding.left - mSpinnerPadding.right; 320 321 if (mDataChanged) { 322 handleDataChanged(); 323 } 324 325 // Handle the empty set by removing all views 326 if (mItemCount == 0) { 327 resetList(); 328 return; 329 } 330 331 if (mNextSelectedPosition >= 0) { 332 setSelectedPositionInt(mNextSelectedPosition); 333 } 334 335 recycleAllViews(); 336 337 // Clear out old views 338 removeAllViewsInLayout(); 339 340 // Make selected view and position it 341 mFirstPosition = mSelectedPosition; 342 View sel = makeAndAddView(mSelectedPosition); 343 int width = sel.getMeasuredWidth(); 344 int selectedOffset = childrenLeft; 345 switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { 346 case Gravity.CENTER_HORIZONTAL: 347 selectedOffset = childrenLeft + (childrenWidth / 2) - (width / 2); 348 break; 349 case Gravity.RIGHT: 350 selectedOffset = childrenLeft + childrenWidth - width; 351 break; 352 } 353 sel.offsetLeftAndRight(selectedOffset); 354 355 // Flush any cached views that did not get reused above 356 mRecycler.clear(); 357 358 invalidate(); 359 360 checkSelectionChanged(); 361 362 mDataChanged = false; 363 mNeedSync = false; 364 setNextSelectedPositionInt(mSelectedPosition); 365 } 366 367 /** 368 * Obtain a view, either by pulling an existing view from the recycler or 369 * by getting a new one from the adapter. If we are animating, make sure 370 * there is enough information in the view's layout parameters to animate 371 * from the old to new positions. 372 * 373 * @param position Position in the spinner for the view to obtain 374 * @return A view that has been added to the spinner 375 */ 376 private View makeAndAddView(int position) { 377 378 View child; 379 380 if (!mDataChanged) { 381 child = mRecycler.get(position); 382 if (child != null) { 383 // Position the view 384 setUpChild(child); 385 386 return child; 387 } 388 } 389 390 // Nothing found in the recycler -- ask the adapter for a view 391 child = mAdapter.getView(position, null, this); 392 393 // Position the view 394 setUpChild(child); 395 396 return child; 397 } 398 399 /** 400 * Helper for makeAndAddView to set the position of a view 401 * and fill out its layout paramters. 402 * 403 * @param child The view to position 404 */ 405 private void setUpChild(View child) { 406 407 // Respect layout params that are already in the view. Otherwise 408 // make some up... 409 ViewGroup.LayoutParams lp = child.getLayoutParams(); 410 if (lp == null) { 411 lp = generateDefaultLayoutParams(); 412 } 413 414 addViewInLayout(child, 0, lp); 415 416 child.setSelected(hasFocus()); 417 if (mDisableChildrenWhenDisabled) { 418 child.setEnabled(isEnabled()); 419 } 420 421 // Get measure specs 422 int childHeightSpec = ViewGroup.getChildMeasureSpec(mHeightMeasureSpec, 423 mSpinnerPadding.top + mSpinnerPadding.bottom, lp.height); 424 int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec, 425 mSpinnerPadding.left + mSpinnerPadding.right, lp.width); 426 427 // Measure child 428 child.measure(childWidthSpec, childHeightSpec); 429 430 int childLeft; 431 int childRight; 432 433 // Position vertically based on gravity setting 434 int childTop = mSpinnerPadding.top 435 + ((getMeasuredHeight() - mSpinnerPadding.bottom - 436 mSpinnerPadding.top - child.getMeasuredHeight()) / 2); 437 int childBottom = childTop + child.getMeasuredHeight(); 438 439 int width = child.getMeasuredWidth(); 440 childLeft = 0; 441 childRight = childLeft + width; 442 443 child.layout(childLeft, childTop, childRight, childBottom); 444 } 445 446 @Override 447 public boolean performClick() { 448 boolean handled = super.performClick(); 449 450 if (!handled) { 451 handled = true; 452 453 if (!mPopup.isShowing()) { 454 mPopup.show(); 455 } 456 } 457 458 return handled; 459 } 460 461 public void onClick(DialogInterface dialog, int which) { 462 setSelection(which); 463 dialog.dismiss(); 464 } 465 466 /** 467 * Sets the prompt to display when the dialog is shown. 468 * @param prompt the prompt to set 469 */ 470 public void setPrompt(CharSequence prompt) { 471 mPopup.setPromptText(prompt); 472 } 473 474 /** 475 * Sets the prompt to display when the dialog is shown. 476 * @param promptId the resource ID of the prompt to display when the dialog is shown 477 */ 478 public void setPromptId(int promptId) { 479 setPrompt(getContext().getText(promptId)); 480 } 481 482 /** 483 * @return The prompt to display when the dialog is shown 484 */ 485 public CharSequence getPrompt() { 486 return mPopup.getHintText(); 487 } 488 489 int measureContentWidth(SpinnerAdapter adapter, Drawable background) { 490 if (adapter == null) { 491 return 0; 492 } 493 494 int width = 0; 495 View itemView = null; 496 int itemType = 0; 497 final int widthMeasureSpec = 498 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 499 final int heightMeasureSpec = 500 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); 501 502 // Make sure the number of items we'll measure is capped. If it's a huge data set 503 // with wildly varying sizes, oh well. 504 int start = Math.max(0, getSelectedItemPosition()); 505 final int end = Math.min(adapter.getCount(), start + MAX_ITEMS_MEASURED); 506 final int count = end - start; 507 start = Math.max(0, start - (MAX_ITEMS_MEASURED - count)); 508 for (int i = start; i < end; i++) { 509 final int positionType = adapter.getItemViewType(i); 510 if (positionType != itemType) { 511 itemType = positionType; 512 itemView = null; 513 } 514 itemView = adapter.getView(i, itemView, this); 515 if (itemView.getLayoutParams() == null) { 516 itemView.setLayoutParams(new ViewGroup.LayoutParams( 517 ViewGroup.LayoutParams.WRAP_CONTENT, 518 ViewGroup.LayoutParams.WRAP_CONTENT)); 519 } 520 itemView.measure(widthMeasureSpec, heightMeasureSpec); 521 width = Math.max(width, itemView.getMeasuredWidth()); 522 } 523 524 // Add background padding to measured width 525 if (background != null) { 526 background.getPadding(mTempRect); 527 width += mTempRect.left + mTempRect.right; 528 } 529 530 return width; 531 } 532 533 /** 534 * <p>Wrapper class for an Adapter. Transforms the embedded Adapter instance 535 * into a ListAdapter.</p> 536 */ 537 private static class DropDownAdapter implements ListAdapter, SpinnerAdapter { 538 private SpinnerAdapter mAdapter; 539 private ListAdapter mListAdapter; 540 541 /** 542 * <p>Creates a new ListAdapter wrapper for the specified adapter.</p> 543 * 544 * @param adapter the Adapter to transform into a ListAdapter 545 */ 546 public DropDownAdapter(SpinnerAdapter adapter) { 547 this.mAdapter = adapter; 548 if (adapter instanceof ListAdapter) { 549 this.mListAdapter = (ListAdapter) adapter; 550 } 551 } 552 553 public int getCount() { 554 return mAdapter == null ? 0 : mAdapter.getCount(); 555 } 556 557 public Object getItem(int position) { 558 return mAdapter == null ? null : mAdapter.getItem(position); 559 } 560 561 public long getItemId(int position) { 562 return mAdapter == null ? -1 : mAdapter.getItemId(position); 563 } 564 565 public View getView(int position, View convertView, ViewGroup parent) { 566 return getDropDownView(position, convertView, parent); 567 } 568 569 public View getDropDownView(int position, View convertView, ViewGroup parent) { 570 return mAdapter == null ? null : 571 mAdapter.getDropDownView(position, convertView, parent); 572 } 573 574 public boolean hasStableIds() { 575 return mAdapter != null && mAdapter.hasStableIds(); 576 } 577 578 public void registerDataSetObserver(DataSetObserver observer) { 579 if (mAdapter != null) { 580 mAdapter.registerDataSetObserver(observer); 581 } 582 } 583 584 public void unregisterDataSetObserver(DataSetObserver observer) { 585 if (mAdapter != null) { 586 mAdapter.unregisterDataSetObserver(observer); 587 } 588 } 589 590 /** 591 * If the wrapped SpinnerAdapter is also a ListAdapter, delegate this call. 592 * Otherwise, return true. 593 */ 594 public boolean areAllItemsEnabled() { 595 final ListAdapter adapter = mListAdapter; 596 if (adapter != null) { 597 return adapter.areAllItemsEnabled(); 598 } else { 599 return true; 600 } 601 } 602 603 /** 604 * If the wrapped SpinnerAdapter is also a ListAdapter, delegate this call. 605 * Otherwise, return true. 606 */ 607 public boolean isEnabled(int position) { 608 final ListAdapter adapter = mListAdapter; 609 if (adapter != null) { 610 return adapter.isEnabled(position); 611 } else { 612 return true; 613 } 614 } 615 616 public int getItemViewType(int position) { 617 return 0; 618 } 619 620 public int getViewTypeCount() { 621 return 1; 622 } 623 624 public boolean isEmpty() { 625 return getCount() == 0; 626 } 627 } 628 629 /** 630 * Implements some sort of popup selection interface for selecting a spinner option. 631 * Allows for different spinner modes. 632 */ 633 private interface SpinnerPopup { 634 public void setAdapter(ListAdapter adapter); 635 636 /** 637 * Show the popup 638 */ 639 public void show(); 640 641 /** 642 * Dismiss the popup 643 */ 644 public void dismiss(); 645 646 /** 647 * @return true if the popup is showing, false otherwise. 648 */ 649 public boolean isShowing(); 650 651 /** 652 * Set hint text to be displayed to the user. This should provide 653 * a description of the choice being made. 654 * @param hintText Hint text to set. 655 */ 656 public void setPromptText(CharSequence hintText); 657 public CharSequence getHintText(); 658 } 659 660 private class DialogPopup implements SpinnerPopup, DialogInterface.OnClickListener { 661 private AlertDialog mPopup; 662 private ListAdapter mListAdapter; 663 private CharSequence mPrompt; 664 665 public void dismiss() { 666 mPopup.dismiss(); 667 mPopup = null; 668 } 669 670 public boolean isShowing() { 671 return mPopup != null ? mPopup.isShowing() : false; 672 } 673 674 public void setAdapter(ListAdapter adapter) { 675 mListAdapter = adapter; 676 } 677 678 public void setPromptText(CharSequence hintText) { 679 mPrompt = hintText; 680 } 681 682 public CharSequence getHintText() { 683 return mPrompt; 684 } 685 686 public void show() { 687 AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); 688 if (mPrompt != null) { 689 builder.setTitle(mPrompt); 690 } 691 mPopup = builder.setSingleChoiceItems(mListAdapter, 692 getSelectedItemPosition(), this).show(); 693 } 694 695 public void onClick(DialogInterface dialog, int which) { 696 setSelection(which); 697 dismiss(); 698 } 699 } 700 701 private class DropdownPopup extends ListPopupWindow implements SpinnerPopup { 702 private CharSequence mHintText; 703 private ListAdapter mAdapter; 704 705 public DropdownPopup(Context context, AttributeSet attrs, int defStyleRes) { 706 super(context, attrs, 0, defStyleRes); 707 708 setAnchorView(Spinner.this); 709 setModal(true); 710 setPromptPosition(POSITION_PROMPT_ABOVE); 711 setOnItemClickListener(new OnItemClickListener() { 712 public void onItemClick(AdapterView parent, View v, int position, long id) { 713 Spinner.this.setSelection(position); 714 dismiss(); 715 } 716 }); 717 } 718 719 @Override 720 public void setAdapter(ListAdapter adapter) { 721 super.setAdapter(adapter); 722 mAdapter = adapter; 723 } 724 725 public CharSequence getHintText() { 726 return mHintText; 727 } 728 729 public void setPromptText(CharSequence hintText) { 730 // Hint text is ignored for dropdowns, but maintain it here. 731 mHintText = hintText; 732 } 733 734 @Override 735 public void show() { 736 final Drawable background = getBackground(); 737 int bgOffset = 0; 738 if (background != null) { 739 background.getPadding(mTempRect); 740 bgOffset = -mTempRect.left; 741 } else { 742 mTempRect.left = mTempRect.right = 0; 743 } 744 745 final int spinnerPaddingLeft = Spinner.this.getPaddingLeft(); 746 if (mDropDownWidth == WRAP_CONTENT) { 747 final int spinnerWidth = Spinner.this.getWidth(); 748 final int spinnerPaddingRight = Spinner.this.getPaddingRight(); 749 750 int contentWidth = measureContentWidth( 751 (SpinnerAdapter) mAdapter, getBackground()); 752 final int contentWidthLimit = mContext.getResources() 753 .getDisplayMetrics().widthPixels - mTempRect.left - mTempRect.right; 754 if (contentWidth > contentWidthLimit) { 755 contentWidth = contentWidthLimit; 756 } 757 758 setContentWidth(Math.max( 759 contentWidth, spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight)); 760 } else if (mDropDownWidth == MATCH_PARENT) { 761 final int spinnerWidth = Spinner.this.getWidth(); 762 final int spinnerPaddingRight = Spinner.this.getPaddingRight(); 763 setContentWidth(spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight); 764 } else { 765 setContentWidth(mDropDownWidth); 766 } 767 setHorizontalOffset(bgOffset + spinnerPaddingLeft); 768 setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED); 769 super.show(); 770 getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); 771 setSelection(Spinner.this.getSelectedItemPosition()); 772 } 773 } 774} 775