blob: b23a855f4d652929f0bdb057f6f889a27dd730e4 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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;
Adam Powell5f83a602011-01-19 17:58:04 -080026import android.graphics.Rect;
Adam Powell8db7cb12011-02-08 14:18:38 -080027import android.graphics.drawable.Drawable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.util.AttributeSet;
Adam Powella39b9872011-01-05 16:07:54 -080029import android.view.Gravity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.view.View;
31import android.view.ViewGroup;
32
33
34/**
35 * A view that displays one child at a time and lets the user pick among them.
36 * The items in the Spinner come from the {@link Adapter} associated with
37 * this view.
Scott Main41ec6532010-08-19 16:57:07 -070038 *
39 * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-spinner.html">Spinner
40 * tutorial</a>.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041 *
42 * @attr ref android.R.styleable#Spinner_prompt
43 */
44@Widget
45public class Spinner extends AbsSpinner implements OnClickListener {
Adam Powellc3fa6302010-05-18 11:36:27 -070046 private static final String TAG = "Spinner";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047
Adam Powell50f784c2010-12-19 16:12:19 -080048 // Only measure this many items to get a decent max width.
49 private static final int MAX_ITEMS_MEASURED = 15;
50
Adam Powellc3fa6302010-05-18 11:36:27 -070051 /**
52 * Use a dialog window for selecting spinner options.
53 */
54 public static final int MODE_DIALOG = 0;
55
56 /**
57 * Use a dropdown anchored to the Spinner for selecting spinner options.
58 */
59 public static final int MODE_DROPDOWN = 1;
60
Adam Powellfef364f2010-09-02 15:11:46 -070061 /**
62 * Use the theme-supplied value to select the dropdown mode.
63 */
64 private static final int MODE_THEME = -1;
65
Adam Powellc3fa6302010-05-18 11:36:27 -070066 private SpinnerPopup mPopup;
Adam Powell68464a92010-06-07 12:48:07 -070067 private DropDownAdapter mTempAdapter;
Adam Powell8db7cb12011-02-08 14:18:38 -080068 int mDropDownWidth;
Adam Powellfef364f2010-09-02 15:11:46 -070069
Adam Powella39b9872011-01-05 16:07:54 -080070 private int mGravity;
71
Adam Powell19fd1642011-02-07 19:00:11 -080072 private Rect mTempRect = new Rect();
73
Adam Powellfef364f2010-09-02 15:11:46 -070074 /**
75 * Construct a new spinner with the given context's theme.
76 *
77 * @param context The Context the view is running in, through which it can
78 * access the current theme, resources, etc.
79 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080 public Spinner(Context context) {
81 this(context, null);
82 }
83
Adam Powellfef364f2010-09-02 15:11:46 -070084 /**
85 * Construct a new spinner with the given context's theme and the supplied
86 * mode of displaying choices. <code>mode</code> may be one of
87 * {@link #MODE_DIALOG} or {@link #MODE_DROPDOWN}.
88 *
89 * @param context The Context the view is running in, through which it can
90 * access the current theme, resources, etc.
91 * @param mode Constant describing how the user will select choices from the spinner.
92 *
93 * @see #MODE_DIALOG
94 * @see #MODE_DROPDOWN
95 */
96 public Spinner(Context context, int mode) {
97 this(context, null, com.android.internal.R.attr.spinnerStyle, mode);
98 }
99
100 /**
101 * Construct a new spinner with the given context's theme and the supplied attribute set.
102 *
103 * @param context The Context the view is running in, through which it can
104 * access the current theme, resources, etc.
105 * @param attrs The attributes of the XML tag that is inflating the view.
106 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107 public Spinner(Context context, AttributeSet attrs) {
108 this(context, attrs, com.android.internal.R.attr.spinnerStyle);
109 }
110
Adam Powellfef364f2010-09-02 15:11:46 -0700111 /**
112 * Construct a new spinner with the given context's theme, the supplied attribute set,
113 * and default style.
114 *
115 * @param context The Context the view is running in, through which it can
116 * access the current theme, resources, etc.
117 * @param attrs The attributes of the XML tag that is inflating the view.
118 * @param defStyle The default style to apply to this view. If 0, no style
119 * will be applied (beyond what is included in the theme). This may
120 * either be an attribute resource, whose value will be retrieved
121 * from the current theme, or an explicit style resource.
122 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123 public Spinner(Context context, AttributeSet attrs, int defStyle) {
Adam Powellfef364f2010-09-02 15:11:46 -0700124 this(context, attrs, defStyle, MODE_THEME);
125 }
126
127 /**
128 * Construct a new spinner with the given context's theme, the supplied attribute set,
129 * and default style. <code>mode</code> may be one of {@link #MODE_DIALOG} or
130 * {@link #MODE_DROPDOWN} and determines how the user will select choices from the spinner.
131 *
132 * @param context The Context the view is running in, through which it can
133 * access the current theme, resources, etc.
134 * @param attrs The attributes of the XML tag that is inflating the view.
135 * @param defStyle The default style to apply to this view. If 0, no style
136 * will be applied (beyond what is included in the theme). This may
137 * either be an attribute resource, whose value will be retrieved
138 * from the current theme, or an explicit style resource.
139 * @param mode Constant describing how the user will select choices from the spinner.
140 *
141 * @see #MODE_DIALOG
142 * @see #MODE_DROPDOWN
143 */
144 public Spinner(Context context, AttributeSet attrs, int defStyle, int mode) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145 super(context, attrs, defStyle);
146
147 TypedArray a = context.obtainStyledAttributes(attrs,
148 com.android.internal.R.styleable.Spinner, defStyle, 0);
Adam Powellfef364f2010-09-02 15:11:46 -0700149
150 if (mode == MODE_THEME) {
151 mode = a.getInt(com.android.internal.R.styleable.Spinner_spinnerMode, MODE_DIALOG);
152 }
Adam Powellc3fa6302010-05-18 11:36:27 -0700153
154 switch (mode) {
155 case MODE_DIALOG: {
156 mPopup = new DialogPopup();
157 break;
158 }
Daisuke Miyakawa3f10b1c2010-08-28 15:59:56 -0700159
Adam Powellc3fa6302010-05-18 11:36:27 -0700160 case MODE_DROPDOWN: {
Adam Powella39b9872011-01-05 16:07:54 -0800161 DropdownPopup popup = new DropdownPopup(context, attrs, defStyle);
Adam Powellc3fa6302010-05-18 11:36:27 -0700162
Adam Powell8db7cb12011-02-08 14:18:38 -0800163 mDropDownWidth = a.getLayoutDimension(
Ben Komalo72536f72010-10-15 11:28:31 -0700164 com.android.internal.R.styleable.Spinner_dropDownWidth,
Adam Powell8db7cb12011-02-08 14:18:38 -0800165 ViewGroup.LayoutParams.WRAP_CONTENT);
Adam Powellc3fa6302010-05-18 11:36:27 -0700166 popup.setBackgroundDrawable(a.getDrawable(
167 com.android.internal.R.styleable.Spinner_popupBackground));
168 popup.setVerticalOffset(a.getDimensionPixelOffset(
169 com.android.internal.R.styleable.Spinner_dropDownVerticalOffset, 0));
170 popup.setHorizontalOffset(a.getDimensionPixelOffset(
171 com.android.internal.R.styleable.Spinner_dropDownHorizontalOffset, 0));
172
173 mPopup = popup;
174 break;
175 }
176 }
177
Adam Powella39b9872011-01-05 16:07:54 -0800178 mGravity = a.getInt(com.android.internal.R.styleable.Spinner_gravity, Gravity.CENTER);
179
Adam Powellc3fa6302010-05-18 11:36:27 -0700180 mPopup.setPromptText(a.getString(com.android.internal.R.styleable.Spinner_prompt));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181
182 a.recycle();
Adam Powell68464a92010-06-07 12:48:07 -0700183
184 // Base constructor can call setAdapter before we initialize mPopup.
185 // Finish setting things up if this happened.
186 if (mTempAdapter != null) {
187 mPopup.setAdapter(mTempAdapter);
188 mTempAdapter = null;
189 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 }
Adam Powella39b9872011-01-05 16:07:54 -0800191
192 /**
193 * Describes how the selected item view is positioned. Currently only the horizontal component
194 * is used. The default is determined by the current theme.
195 *
196 * @param gravity See {@link android.view.Gravity}
197 *
198 * @attr ref android.R.styleable#Spinner_gravity
199 */
200 public void setGravity(int gravity) {
201 if (mGravity != gravity) {
202 if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == 0) {
203 gravity |= Gravity.LEFT;
204 }
205 mGravity = gravity;
206 requestLayout();
207 }
208 }
209
Adam Powellc3fa6302010-05-18 11:36:27 -0700210 @Override
211 public void setAdapter(SpinnerAdapter adapter) {
212 super.setAdapter(adapter);
Adam Powell68464a92010-06-07 12:48:07 -0700213
214 if (mPopup != null) {
215 mPopup.setAdapter(new DropDownAdapter(adapter));
216 } else {
217 mTempAdapter = new DropDownAdapter(adapter);
218 }
Adam Powellc3fa6302010-05-18 11:36:27 -0700219 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220
221 @Override
222 public int getBaseline() {
223 View child = null;
224
225 if (getChildCount() > 0) {
226 child = getChildAt(0);
227 } else if (mAdapter != null && mAdapter.getCount() > 0) {
228 child = makeAndAddView(0);
Adam Powell22e92e52010-12-10 13:20:28 -0800229 mRecycler.put(0, child);
230 removeAllViewsInLayout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 }
232
233 if (child != null) {
234 return child.getTop() + child.getBaseline();
235 } else {
236 return -1;
237 }
238 }
239
Romain Guy5275d692009-07-15 17:00:23 -0700240 @Override
241 protected void onDetachedFromWindow() {
242 super.onDetachedFromWindow();
243
244 if (mPopup != null && mPopup.isShowing()) {
245 mPopup.dismiss();
Romain Guy5275d692009-07-15 17:00:23 -0700246 }
247 }
248
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 /**
250 * <p>A spinner does not support item click events. Calling this method
251 * will raise an exception.</p>
252 *
253 * @param l this listener will be ignored
254 */
255 @Override
256 public void setOnItemClickListener(OnItemClickListener l) {
257 throw new RuntimeException("setOnItemClickListener cannot be used with a spinner.");
258 }
259
Adam Powella39b9872011-01-05 16:07:54 -0800260 @Override
261 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
262 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
263 if (mPopup != null && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) {
264 final int measuredWidth = getMeasuredWidth();
Adam Powell19fd1642011-02-07 19:00:11 -0800265 setMeasuredDimension(Math.min(Math.max(measuredWidth,
Adam Powellb70c7272011-02-10 12:04:59 -0800266 measureContentWidth(getAdapter(), getBackground())),
267 MeasureSpec.getSize(widthMeasureSpec)),
Adam Powella39b9872011-01-05 16:07:54 -0800268 getMeasuredHeight());
Adam Powella39b9872011-01-05 16:07:54 -0800269 }
270 }
271
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272 /**
273 * @see android.view.View#onLayout(boolean,int,int,int,int)
274 *
275 * Creates and positions all views
276 *
277 */
278 @Override
279 protected void onLayout(boolean changed, int l, int t, int r, int b) {
280 super.onLayout(changed, l, t, r, b);
281 mInLayout = true;
282 layout(0, false);
283 mInLayout = false;
284 }
285
286 /**
287 * Creates and positions all views for this Spinner.
288 *
289 * @param delta Change in the selected position. +1 moves selection is moving to the right,
290 * so views are scrolling to the left. -1 means selection is moving to the left.
291 */
292 @Override
293 void layout(int delta, boolean animate) {
294 int childrenLeft = mSpinnerPadding.left;
295 int childrenWidth = mRight - mLeft - mSpinnerPadding.left - mSpinnerPadding.right;
296
297 if (mDataChanged) {
298 handleDataChanged();
299 }
300
301 // Handle the empty set by removing all views
302 if (mItemCount == 0) {
303 resetList();
304 return;
305 }
306
307 if (mNextSelectedPosition >= 0) {
308 setSelectedPositionInt(mNextSelectedPosition);
309 }
310
311 recycleAllViews();
312
313 // Clear out old views
314 removeAllViewsInLayout();
315
Adam Powella39b9872011-01-05 16:07:54 -0800316 // Make selected view and position it
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317 mFirstPosition = mSelectedPosition;
318 View sel = makeAndAddView(mSelectedPosition);
319 int width = sel.getMeasuredWidth();
Adam Powella39b9872011-01-05 16:07:54 -0800320 int selectedOffset = childrenLeft;
321 switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
322 case Gravity.CENTER_HORIZONTAL:
323 selectedOffset = childrenLeft + (childrenWidth / 2) - (width / 2);
324 break;
325 case Gravity.RIGHT:
326 selectedOffset = childrenLeft + childrenWidth - width;
327 break;
328 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329 sel.offsetLeftAndRight(selectedOffset);
330
331 // Flush any cached views that did not get reused above
332 mRecycler.clear();
333
334 invalidate();
335
336 checkSelectionChanged();
337
338 mDataChanged = false;
339 mNeedSync = false;
340 setNextSelectedPositionInt(mSelectedPosition);
341 }
342
343 /**
344 * Obtain a view, either by pulling an existing view from the recycler or
345 * by getting a new one from the adapter. If we are animating, make sure
346 * there is enough information in the view's layout parameters to animate
347 * from the old to new positions.
348 *
349 * @param position Position in the spinner for the view to obtain
350 * @return A view that has been added to the spinner
351 */
352 private View makeAndAddView(int position) {
353
354 View child;
355
356 if (!mDataChanged) {
357 child = mRecycler.get(position);
358 if (child != null) {
359 // Position the view
360 setUpChild(child);
361
362 return child;
363 }
364 }
365
366 // Nothing found in the recycler -- ask the adapter for a view
367 child = mAdapter.getView(position, null, this);
368
369 // Position the view
370 setUpChild(child);
371
372 return child;
373 }
374
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375 /**
376 * Helper for makeAndAddView to set the position of a view
377 * and fill out its layout paramters.
378 *
379 * @param child The view to position
380 */
381 private void setUpChild(View child) {
382
383 // Respect layout params that are already in the view. Otherwise
384 // make some up...
385 ViewGroup.LayoutParams lp = child.getLayoutParams();
386 if (lp == null) {
387 lp = generateDefaultLayoutParams();
388 }
389
390 addViewInLayout(child, 0, lp);
391
392 child.setSelected(hasFocus());
393
394 // Get measure specs
395 int childHeightSpec = ViewGroup.getChildMeasureSpec(mHeightMeasureSpec,
396 mSpinnerPadding.top + mSpinnerPadding.bottom, lp.height);
397 int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec,
398 mSpinnerPadding.left + mSpinnerPadding.right, lp.width);
399
400 // Measure child
401 child.measure(childWidthSpec, childHeightSpec);
402
403 int childLeft;
404 int childRight;
405
406 // Position vertically based on gravity setting
407 int childTop = mSpinnerPadding.top
Dianne Hackborn189ee182010-12-02 21:48:53 -0800408 + ((getMeasuredHeight() - mSpinnerPadding.bottom -
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409 mSpinnerPadding.top - child.getMeasuredHeight()) / 2);
410 int childBottom = childTop + child.getMeasuredHeight();
411
412 int width = child.getMeasuredWidth();
413 childLeft = 0;
414 childRight = childLeft + width;
415
416 child.layout(childLeft, childTop, childRight, childBottom);
417 }
418
419 @Override
420 public boolean performClick() {
421 boolean handled = super.performClick();
422
423 if (!handled) {
424 handled = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425
Adam Powellc3fa6302010-05-18 11:36:27 -0700426 if (!mPopup.isShowing()) {
427 mPopup.show();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429 }
430
431 return handled;
432 }
433
434 public void onClick(DialogInterface dialog, int which) {
435 setSelection(which);
436 dialog.dismiss();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800437 }
438
439 /**
440 * Sets the prompt to display when the dialog is shown.
441 * @param prompt the prompt to set
442 */
443 public void setPrompt(CharSequence prompt) {
Adam Powellc3fa6302010-05-18 11:36:27 -0700444 mPopup.setPromptText(prompt);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 }
446
447 /**
448 * Sets the prompt to display when the dialog is shown.
449 * @param promptId the resource ID of the prompt to display when the dialog is shown
450 */
451 public void setPromptId(int promptId) {
Adam Powellc3fa6302010-05-18 11:36:27 -0700452 setPrompt(getContext().getText(promptId));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800453 }
454
455 /**
456 * @return The prompt to display when the dialog is shown
457 */
458 public CharSequence getPrompt() {
Adam Powellc3fa6302010-05-18 11:36:27 -0700459 return mPopup.getHintText();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 }
Adam Powell19fd1642011-02-07 19:00:11 -0800461
Adam Powellb70c7272011-02-10 12:04:59 -0800462 int measureContentWidth(SpinnerAdapter adapter, Drawable background) {
Adam Powell19fd1642011-02-07 19:00:11 -0800463 if (adapter == null) {
464 return 0;
465 }
466
467 int width = 0;
468 View itemView = null;
469 int itemType = 0;
470 final int widthMeasureSpec =
471 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
472 final int heightMeasureSpec =
473 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
474
475 // Make sure the number of items we'll measure is capped. If it's a huge data set
476 // with wildly varying sizes, oh well.
Adam Powellb70c7272011-02-10 12:04:59 -0800477 int start = Math.max(0, getSelectedItemPosition());
478 final int end = Math.min(adapter.getCount(), start + MAX_ITEMS_MEASURED);
479 final int count = end - start;
480 start = Math.max(0, start - (MAX_ITEMS_MEASURED - count));
481 for (int i = start; i < end; i++) {
Adam Powell19fd1642011-02-07 19:00:11 -0800482 final int positionType = adapter.getItemViewType(i);
483 if (positionType != itemType) {
484 itemType = positionType;
485 itemView = null;
486 }
487 itemView = adapter.getView(i, itemView, this);
488 if (itemView.getLayoutParams() == null) {
489 itemView.setLayoutParams(new ViewGroup.LayoutParams(
490 ViewGroup.LayoutParams.WRAP_CONTENT,
491 ViewGroup.LayoutParams.WRAP_CONTENT));
492 }
493 itemView.measure(widthMeasureSpec, heightMeasureSpec);
494 width = Math.max(width, itemView.getMeasuredWidth());
495 }
496
497 // Add background padding to measured width
Adam Powellb70c7272011-02-10 12:04:59 -0800498 if (background != null) {
499 background.getPadding(mTempRect);
Adam Powell19fd1642011-02-07 19:00:11 -0800500 width += mTempRect.left + mTempRect.right;
501 }
502
503 return width;
504 }
505
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800506 /**
507 * <p>Wrapper class for an Adapter. Transforms the embedded Adapter instance
508 * into a ListAdapter.</p>
509 */
510 private static class DropDownAdapter implements ListAdapter, SpinnerAdapter {
511 private SpinnerAdapter mAdapter;
Adam Powell1f09c832010-02-18 18:13:22 -0800512 private ListAdapter mListAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513
514 /**
Adam Powell1f09c832010-02-18 18:13:22 -0800515 * <p>Creates a new ListAdapter wrapper for the specified adapter.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516 *
517 * @param adapter the Adapter to transform into a ListAdapter
518 */
519 public DropDownAdapter(SpinnerAdapter adapter) {
520 this.mAdapter = adapter;
Adam Powell1f09c832010-02-18 18:13:22 -0800521 if (adapter instanceof ListAdapter) {
522 this.mListAdapter = (ListAdapter) adapter;
523 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524 }
525
526 public int getCount() {
527 return mAdapter == null ? 0 : mAdapter.getCount();
528 }
529
530 public Object getItem(int position) {
531 return mAdapter == null ? null : mAdapter.getItem(position);
532 }
533
534 public long getItemId(int position) {
535 return mAdapter == null ? -1 : mAdapter.getItemId(position);
536 }
537
538 public View getView(int position, View convertView, ViewGroup parent) {
539 return getDropDownView(position, convertView, parent);
540 }
541
542 public View getDropDownView(int position, View convertView, ViewGroup parent) {
543 return mAdapter == null ? null :
544 mAdapter.getDropDownView(position, convertView, parent);
545 }
546
547 public boolean hasStableIds() {
548 return mAdapter != null && mAdapter.hasStableIds();
549 }
550
551 public void registerDataSetObserver(DataSetObserver observer) {
552 if (mAdapter != null) {
553 mAdapter.registerDataSetObserver(observer);
554 }
555 }
556
557 public void unregisterDataSetObserver(DataSetObserver observer) {
558 if (mAdapter != null) {
559 mAdapter.unregisterDataSetObserver(observer);
560 }
561 }
562
563 /**
Adam Powell1f09c832010-02-18 18:13:22 -0800564 * If the wrapped SpinnerAdapter is also a ListAdapter, delegate this call.
565 * Otherwise, return true.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800566 */
567 public boolean areAllItemsEnabled() {
Adam Powell1f09c832010-02-18 18:13:22 -0800568 final ListAdapter adapter = mListAdapter;
569 if (adapter != null) {
570 return adapter.areAllItemsEnabled();
571 } else {
572 return true;
573 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800574 }
575
576 /**
Adam Powell1f09c832010-02-18 18:13:22 -0800577 * If the wrapped SpinnerAdapter is also a ListAdapter, delegate this call.
578 * Otherwise, return true.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579 */
580 public boolean isEnabled(int position) {
Adam Powell1f09c832010-02-18 18:13:22 -0800581 final ListAdapter adapter = mListAdapter;
582 if (adapter != null) {
583 return adapter.isEnabled(position);
584 } else {
585 return true;
586 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587 }
588
589 public int getItemViewType(int position) {
590 return 0;
591 }
592
593 public int getViewTypeCount() {
594 return 1;
595 }
596
597 public boolean isEmpty() {
598 return getCount() == 0;
599 }
600 }
Adam Powellc3fa6302010-05-18 11:36:27 -0700601
602 /**
603 * Implements some sort of popup selection interface for selecting a spinner option.
604 * Allows for different spinner modes.
605 */
606 private interface SpinnerPopup {
607 public void setAdapter(ListAdapter adapter);
608
609 /**
610 * Show the popup
611 */
612 public void show();
613
614 /**
615 * Dismiss the popup
616 */
617 public void dismiss();
618
619 /**
620 * @return true if the popup is showing, false otherwise.
621 */
622 public boolean isShowing();
623
624 /**
625 * Set hint text to be displayed to the user. This should provide
626 * a description of the choice being made.
627 * @param hintText Hint text to set.
628 */
629 public void setPromptText(CharSequence hintText);
630 public CharSequence getHintText();
631 }
632
633 private class DialogPopup implements SpinnerPopup, DialogInterface.OnClickListener {
634 private AlertDialog mPopup;
635 private ListAdapter mListAdapter;
636 private CharSequence mPrompt;
637
638 public void dismiss() {
639 mPopup.dismiss();
640 mPopup = null;
641 }
642
643 public boolean isShowing() {
644 return mPopup != null ? mPopup.isShowing() : false;
645 }
646
647 public void setAdapter(ListAdapter adapter) {
648 mListAdapter = adapter;
649 }
650
651 public void setPromptText(CharSequence hintText) {
652 mPrompt = hintText;
653 }
654
655 public CharSequence getHintText() {
656 return mPrompt;
657 }
658
659 public void show() {
660 AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
661 if (mPrompt != null) {
662 builder.setTitle(mPrompt);
663 }
664 mPopup = builder.setSingleChoiceItems(mListAdapter,
665 getSelectedItemPosition(), this).show();
666 }
667
668 public void onClick(DialogInterface dialog, int which) {
669 setSelection(which);
670 dismiss();
671 }
672 }
673
674 private class DropdownPopup extends ListPopupWindow implements SpinnerPopup {
675 private CharSequence mHintText;
Adam Powell19fd1642011-02-07 19:00:11 -0800676 private ListAdapter mAdapter;
Chris Yergaefd08112011-01-17 00:30:08 -0800677
Adam Powella39b9872011-01-05 16:07:54 -0800678 public DropdownPopup(Context context, AttributeSet attrs, int defStyleRes) {
Adam Powellc3fa6302010-05-18 11:36:27 -0700679 super(context, attrs, 0, defStyleRes);
Adam Powell50f784c2010-12-19 16:12:19 -0800680
Adam Powellc3fa6302010-05-18 11:36:27 -0700681 setAnchorView(Spinner.this);
682 setModal(true);
Adam Powellbe4d68e2010-10-08 18:16:34 -0700683 setPromptPosition(POSITION_PROMPT_ABOVE);
Adam Powellc3fa6302010-05-18 11:36:27 -0700684 setOnItemClickListener(new OnItemClickListener() {
685 public void onItemClick(AdapterView parent, View v, int position, long id) {
686 Spinner.this.setSelection(position);
687 dismiss();
688 }
689 });
690 }
691
Adam Powell19fd1642011-02-07 19:00:11 -0800692 @Override
693 public void setAdapter(ListAdapter adapter) {
694 super.setAdapter(adapter);
695 mAdapter = adapter;
696 }
697
Adam Powellc3fa6302010-05-18 11:36:27 -0700698 public CharSequence getHintText() {
699 return mHintText;
700 }
701
702 public void setPromptText(CharSequence hintText) {
Adam Powella39b9872011-01-05 16:07:54 -0800703 // Hint text is ignored for dropdowns, but maintain it here.
Adam Powellc3fa6302010-05-18 11:36:27 -0700704 mHintText = hintText;
Adam Powellc3fa6302010-05-18 11:36:27 -0700705 }
Daisuke Miyakawa3f10b1c2010-08-28 15:59:56 -0700706
707 @Override
Adam Powellc3fa6302010-05-18 11:36:27 -0700708 public void show() {
Adam Powell8db7cb12011-02-08 14:18:38 -0800709 if (mDropDownWidth == WRAP_CONTENT) {
Adam Powellb70c7272011-02-10 12:04:59 -0800710 setWidth(Math.max(measureContentWidth((SpinnerAdapter) mAdapter, getBackground()),
Adam Powell8db7cb12011-02-08 14:18:38 -0800711 Spinner.this.getWidth()));
712 } else if (mDropDownWidth == MATCH_PARENT) {
713 setWidth(Spinner.this.getWidth());
714 } else {
715 setWidth(mDropDownWidth);
716 }
Adam Powell6f5e9342011-01-27 13:30:55 -0800717 setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
Adam Powellc3fa6302010-05-18 11:36:27 -0700718 super.show();
719 getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
720 setSelection(Spinner.this.getSelectedItemPosition());
721 }
722 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800723}