blob: 39714872dc7a934530b5be74639290285efd74ca [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.content.Context;
20import android.content.res.TypedArray;
Romain Guy6a678102010-03-16 13:49:31 -070021import android.database.DataSetObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.graphics.Rect;
23import android.graphics.drawable.Drawable;
24import android.text.Editable;
25import android.text.Selection;
26import android.text.TextUtils;
27import android.text.TextWatcher;
28import android.util.AttributeSet;
29import android.util.Log;
30import android.view.KeyEvent;
31import android.view.LayoutInflater;
32import android.view.MotionEvent;
33import android.view.View;
34import android.view.ViewGroup;
Romain Guy374aaaed32009-07-14 15:11:59 -070035import android.view.WindowManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.view.inputmethod.CompletionInfo;
37import android.view.inputmethod.InputMethodManager;
38import android.view.inputmethod.EditorInfo;
39
40import com.android.internal.R;
41
42
43/**
44 * <p>An editable text view that shows completion suggestions automatically
45 * while the user is typing. The list of suggestions is displayed in a drop
46 * down menu from which the user can choose an item to replace the content
47 * of the edit box with.</p>
48 *
49 * <p>The drop down can be dismissed at any time by pressing the back key or,
50 * if no item is selected in the drop down, by pressing the enter/dpad center
51 * key.</p>
52 *
53 * <p>The list of suggestions is obtained from a data adapter and appears
54 * only after a given number of characters defined by
55 * {@link #getThreshold() the threshold}.</p>
56 *
57 * <p>The following code snippet shows how to create a text view which suggests
58 * various countries names while the user is typing:</p>
59 *
60 * <pre class="prettyprint">
61 * public class CountriesActivity extends Activity {
62 * protected void onCreate(Bundle icicle) {
63 * super.onCreate(icicle);
64 * setContentView(R.layout.countries);
65 *
66 * ArrayAdapter&lt;String&gt; adapter = new ArrayAdapter&lt;String&gt;(this,
67 * android.R.layout.simple_dropdown_item_1line, COUNTRIES);
68 * AutoCompleteTextView textView = (AutoCompleteTextView)
69 * findViewById(R.id.countries_list);
70 * textView.setAdapter(adapter);
71 * }
72 *
73 * private static final String[] COUNTRIES = new String[] {
74 * "Belgium", "France", "Italy", "Germany", "Spain"
75 * };
76 * }
77 * </pre>
78 *
Scott Main41ec6532010-08-19 16:57:07 -070079 * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-autocomplete.html">Auto Complete
80 * tutorial</a>.</p>
81 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082 * @attr ref android.R.styleable#AutoCompleteTextView_completionHint
83 * @attr ref android.R.styleable#AutoCompleteTextView_completionThreshold
84 * @attr ref android.R.styleable#AutoCompleteTextView_completionHintView
85 * @attr ref android.R.styleable#AutoCompleteTextView_dropDownSelector
86 * @attr ref android.R.styleable#AutoCompleteTextView_dropDownAnchor
87 * @attr ref android.R.styleable#AutoCompleteTextView_dropDownWidth
Romain Guye29f0642009-06-23 21:27:02 -070088 * @attr ref android.R.styleable#AutoCompleteTextView_dropDownHeight
Romain Guyaa1c6312009-09-30 11:36:01 -070089 * @attr ref android.R.styleable#AutoCompleteTextView_dropDownVerticalOffset
90 * @attr ref android.R.styleable#AutoCompleteTextView_dropDownHorizontalOffset
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091 */
92public class AutoCompleteTextView extends EditText implements Filter.FilterListener {
93 static final boolean DEBUG = false;
94 static final String TAG = "AutoCompleteTextView";
95
96 private static final int HINT_VIEW_ID = 0x17;
97
Adam Powell387d8f82010-03-23 13:29:34 -070098 /**
99 * This value controls the length of time that the user
100 * must leave a pointer down without scrolling to expand
101 * the autocomplete dropdown list to cover the IME.
102 */
103 private static final int EXPAND_LIST_TIMEOUT = 250;
104
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 private CharSequence mHintText;
106 private int mHintResource;
107
108 private ListAdapter mAdapter;
109 private Filter mFilter;
110 private int mThreshold;
111
112 private PopupWindow mPopup;
113 private DropDownListView mDropDownList;
114 private int mDropDownVerticalOffset;
115 private int mDropDownHorizontalOffset;
116 private int mDropDownAnchorId;
117 private View mDropDownAnchorView; // view is retrieved lazily from id once needed
118 private int mDropDownWidth;
Romain Guye29f0642009-06-23 21:27:02 -0700119 private int mDropDownHeight;
Bjorn Bringert5420d012009-07-28 20:17:27 +0100120 private final Rect mTempRect = new Rect();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121
122 private Drawable mDropDownListHighlight;
123
124 private AdapterView.OnItemClickListener mItemClickListener;
125 private AdapterView.OnItemSelectedListener mItemSelectedListener;
126
127 private final DropDownItemClickListener mDropDownItemClickListener =
128 new DropDownItemClickListener();
129
Karl Rosaen875d50a2009-04-23 19:00:21 -0700130 private boolean mDropDownAlwaysVisible = false;
131
132 private boolean mDropDownDismissedOnCompletion = true;
Mike LeBeaud4760d72009-07-22 15:04:27 -0700133
134 private boolean mForceIgnoreOutsideTouch = false;
Karl Rosaen875d50a2009-04-23 19:00:21 -0700135
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136 private int mLastKeyCode = KeyEvent.KEYCODE_UNKNOWN;
137 private boolean mOpenBefore;
138
139 private Validator mValidator = null;
140
141 private boolean mBlockCompletion;
142
Romain Guy6a678102010-03-16 13:49:31 -0700143 private ListSelectorHider mHideSelector;
Satish Sampathfef8d3e2009-07-01 17:48:42 +0100144 private Runnable mShowDropDownRunnable;
Adam Powell387d8f82010-03-23 13:29:34 -0700145 private Runnable mResizePopupRunnable = new ResizePopupRunnable();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146
Romain Guy6a678102010-03-16 13:49:31 -0700147 private PassThroughClickListener mPassThroughClickListener;
148 private PopupDataSetObserver mObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149
150 public AutoCompleteTextView(Context context) {
151 this(context, null);
152 }
153
154 public AutoCompleteTextView(Context context, AttributeSet attrs) {
155 this(context, attrs, com.android.internal.R.attr.autoCompleteTextViewStyle);
156 }
157
158 public AutoCompleteTextView(Context context, AttributeSet attrs, int defStyle) {
159 super(context, attrs, defStyle);
160
161 mPopup = new PopupWindow(context, attrs,
162 com.android.internal.R.attr.autoCompleteTextViewStyle);
Romain Guy374aaaed32009-07-14 15:11:59 -0700163 mPopup.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164
165 TypedArray a =
166 context.obtainStyledAttributes(
167 attrs, com.android.internal.R.styleable.AutoCompleteTextView, defStyle, 0);
168
169 mThreshold = a.getInt(
170 R.styleable.AutoCompleteTextView_completionThreshold, 2);
171
172 mHintText = a.getText(R.styleable.AutoCompleteTextView_completionHint);
173
174 mDropDownListHighlight = a.getDrawable(
175 R.styleable.AutoCompleteTextView_dropDownSelector);
176 mDropDownVerticalOffset = (int)
177 a.getDimension(R.styleable.AutoCompleteTextView_dropDownVerticalOffset, 0.0f);
178 mDropDownHorizontalOffset = (int)
179 a.getDimension(R.styleable.AutoCompleteTextView_dropDownHorizontalOffset, 0.0f);
180
181 // Get the anchor's id now, but the view won't be ready, so wait to actually get the
182 // view and store it in mDropDownAnchorView lazily in getDropDownAnchorView later.
183 // Defaults to NO_ID, in which case the getDropDownAnchorView method will simply return
184 // this TextView, as a default anchoring point.
185 mDropDownAnchorId = a.getResourceId(R.styleable.AutoCompleteTextView_dropDownAnchor,
186 View.NO_ID);
187
Romain Guy980a9382010-01-08 15:06:28 -0800188 // For dropdown width, the developer can specify a specific width, or MATCH_PARENT
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 // (for full screen width) or WRAP_CONTENT (to match the width of the anchored view).
190 mDropDownWidth = a.getLayoutDimension(R.styleable.AutoCompleteTextView_dropDownWidth,
191 ViewGroup.LayoutParams.WRAP_CONTENT);
Romain Guye29f0642009-06-23 21:27:02 -0700192 mDropDownHeight = a.getLayoutDimension(R.styleable.AutoCompleteTextView_dropDownHeight,
193 ViewGroup.LayoutParams.WRAP_CONTENT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194
195 mHintResource = a.getResourceId(R.styleable.AutoCompleteTextView_completionHintView,
196 R.layout.simple_dropdown_hint);
197
198 // Always turn on the auto complete input type flag, since it
199 // makes no sense to use this widget without it.
200 int inputType = getInputType();
201 if ((inputType&EditorInfo.TYPE_MASK_CLASS)
202 == EditorInfo.TYPE_CLASS_TEXT) {
203 inputType |= EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE;
204 setRawInputType(inputType);
205 }
206
207 a.recycle();
208
209 setFocusable(true);
210
211 addTextChangedListener(new MyWatcher());
Mike LeBeauddf98562009-05-08 17:59:05 -0700212
Karl Rosaen98e333f2009-04-28 10:39:09 -0700213 mPassThroughClickListener = new PassThroughClickListener();
214 super.setOnClickListener(mPassThroughClickListener);
215 }
216
217 @Override
218 public void setOnClickListener(OnClickListener listener) {
219 mPassThroughClickListener.mWrapped = listener;
220 }
221
222 /**
223 * Private hook into the on click event, dispatched from {@link PassThroughClickListener}
224 */
225 private void onClickImpl() {
Amith Yamasanid25eb352010-03-10 21:09:28 -0800226 // If the dropdown is showing, bring the keyboard to the front
227 // when the user touches the text field.
228 if (mPopup.isShowing()) {
229 ensureImeVisible(true);
Karl Rosaen98e333f2009-04-28 10:39:09 -0700230 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 }
232
233 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234 * <p>Sets the optional hint text that is displayed at the bottom of the
235 * the matching list. This can be used as a cue to the user on how to
236 * best use the list, or to provide extra information.</p>
237 *
238 * @param hint the text to be displayed to the user
239 *
240 * @attr ref android.R.styleable#AutoCompleteTextView_completionHint
241 */
242 public void setCompletionHint(CharSequence hint) {
243 mHintText = hint;
244 }
245
246 /**
247 * <p>Returns the current width for the auto-complete drop down list. This can
Romain Guy980a9382010-01-08 15:06:28 -0800248 * be a fixed width, or {@link ViewGroup.LayoutParams#MATCH_PARENT} to fill the screen, or
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 * {@link ViewGroup.LayoutParams#WRAP_CONTENT} to fit the width of its anchor view.</p>
250 *
251 * @return the width for the drop down list
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700252 *
253 * @attr ref android.R.styleable#AutoCompleteTextView_dropDownWidth
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 */
255 public int getDropDownWidth() {
256 return mDropDownWidth;
257 }
258
259 /**
260 * <p>Sets the current width for the auto-complete drop down list. This can
Romain Guy980a9382010-01-08 15:06:28 -0800261 * be a fixed width, or {@link ViewGroup.LayoutParams#MATCH_PARENT} to fill the screen, or
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262 * {@link ViewGroup.LayoutParams#WRAP_CONTENT} to fit the width of its anchor view.</p>
263 *
264 * @param width the width to use
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700265 *
266 * @attr ref android.R.styleable#AutoCompleteTextView_dropDownWidth
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800267 */
268 public void setDropDownWidth(int width) {
269 mDropDownWidth = width;
270 }
Romain Guye29f0642009-06-23 21:27:02 -0700271
272 /**
273 * <p>Returns the current height for the auto-complete drop down list. This can
Romain Guy980a9382010-01-08 15:06:28 -0800274 * be a fixed height, or {@link ViewGroup.LayoutParams#MATCH_PARENT} to fill
Romain Guye29f0642009-06-23 21:27:02 -0700275 * the screen, or {@link ViewGroup.LayoutParams#WRAP_CONTENT} to fit the height
276 * of the drop down's content.</p>
277 *
278 * @return the height for the drop down list
279 *
280 * @attr ref android.R.styleable#AutoCompleteTextView_dropDownHeight
281 */
282 public int getDropDownHeight() {
283 return mDropDownHeight;
284 }
285
286 /**
287 * <p>Sets the current height for the auto-complete drop down list. This can
Romain Guy980a9382010-01-08 15:06:28 -0800288 * be a fixed height, or {@link ViewGroup.LayoutParams#MATCH_PARENT} to fill
Romain Guye29f0642009-06-23 21:27:02 -0700289 * the screen, or {@link ViewGroup.LayoutParams#WRAP_CONTENT} to fit the height
290 * of the drop down's content.</p>
291 *
292 * @param height the height to use
293 *
294 * @attr ref android.R.styleable#AutoCompleteTextView_dropDownHeight
295 */
296 public void setDropDownHeight(int height) {
297 mDropDownHeight = height;
298 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800299
300 /**
301 * <p>Returns the id for the view that the auto-complete drop down list is anchored to.</p>
302 *
303 * @return the view's id, or {@link View#NO_ID} if none specified
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700304 *
305 * @attr ref android.R.styleable#AutoCompleteTextView_dropDownAnchor
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306 */
307 public int getDropDownAnchor() {
308 return mDropDownAnchorId;
309 }
310
311 /**
312 * <p>Sets the view to which the auto-complete drop down list should anchor. The view
313 * corresponding to this id will not be loaded until the next time it is needed to avoid
314 * loading a view which is not yet instantiated.</p>
315 *
316 * @param id the id to anchor the drop down list view to
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700317 *
318 * @attr ref android.R.styleable#AutoCompleteTextView_dropDownAnchor
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 */
320 public void setDropDownAnchor(int id) {
321 mDropDownAnchorId = id;
322 mDropDownAnchorView = null;
323 }
The Android Open Source Project7b0b1ed2009-03-18 22:20:26 -0700324
325 /**
326 * <p>Gets the background of the auto-complete drop-down list.</p>
327 *
328 * @return the background drawable
329 *
330 * @attr ref android.R.styleable#PopupWindow_popupBackground
331 */
332 public Drawable getDropDownBackground() {
333 return mPopup.getBackground();
334 }
335
336 /**
337 * <p>Sets the background of the auto-complete drop-down list.</p>
338 *
339 * @param d the drawable to set as the background
340 *
341 * @attr ref android.R.styleable#PopupWindow_popupBackground
342 */
343 public void setDropDownBackgroundDrawable(Drawable d) {
344 mPopup.setBackgroundDrawable(d);
345 }
346
347 /**
348 * <p>Sets the background of the auto-complete drop-down list.</p>
349 *
350 * @param id the id of the drawable to set as the background
351 *
352 * @attr ref android.R.styleable#PopupWindow_popupBackground
353 */
354 public void setDropDownBackgroundResource(int id) {
355 mPopup.setBackgroundDrawable(getResources().getDrawable(id));
356 }
357
358 /**
359 * <p>Sets the vertical offset used for the auto-complete drop-down list.</p>
360 *
361 * @param offset the vertical offset
362 */
363 public void setDropDownVerticalOffset(int offset) {
364 mDropDownVerticalOffset = offset;
365 }
366
367 /**
368 * <p>Gets the vertical offset used for the auto-complete drop-down list.</p>
369 *
370 * @return the vertical offset
371 */
372 public int getDropDownVerticalOffset() {
373 return mDropDownVerticalOffset;
374 }
375
376 /**
377 * <p>Sets the horizontal offset used for the auto-complete drop-down list.</p>
378 *
379 * @param offset the horizontal offset
380 */
381 public void setDropDownHorizontalOffset(int offset) {
382 mDropDownHorizontalOffset = offset;
383 }
384
385 /**
386 * <p>Gets the horizontal offset used for the auto-complete drop-down list.</p>
387 *
388 * @return the horizontal offset
389 */
390 public int getDropDownHorizontalOffset() {
391 return mDropDownHorizontalOffset;
392 }
The Android Open Source Project8f080ec2009-04-29 13:34:51 -0700393
The Android Open Source Projectbff13892009-04-29 13:41:02 -0700394 /**
Karl Rosaen875d50a2009-04-23 19:00:21 -0700395 * <p>Sets the animation style of the auto-complete drop-down list.</p>
396 *
397 * <p>If the drop-down is showing, calling this method will take effect only
398 * the next time the drop-down is shown.</p>
399 *
400 * @param animationStyle animation style to use when the drop-down appears
401 * and disappears. Set to -1 for the default animation, 0 for no
402 * animation, or a resource identifier for an explicit animation.
403 *
404 * @hide Pending API council approval
405 */
406 public void setDropDownAnimationStyle(int animationStyle) {
407 mPopup.setAnimationStyle(animationStyle);
408 }
409
410 /**
411 * <p>Returns the animation style that is used when the drop-down list appears and disappears
412 * </p>
413 *
414 * @return the animation style that is used when the drop-down list appears and disappears
415 *
416 * @hide Pending API council approval
417 */
418 public int getDropDownAnimationStyle() {
419 return mPopup.getAnimationStyle();
420 }
Karl Rosaen875d50a2009-04-23 19:00:21 -0700421
422 /**
423 * @return Whether the drop-down is visible as long as there is {@link #enoughToFilter()}
424 *
425 * @hide Pending API council approval
426 */
427 public boolean isDropDownAlwaysVisible() {
428 return mDropDownAlwaysVisible;
429 }
430
431 /**
432 * Sets whether the drop-down should remain visible as long as there is there is
433 * {@link #enoughToFilter()}. This is useful if an unknown number of results are expected
434 * to show up in the adapter sometime in the future.
435 *
436 * The drop-down will occupy the entire screen below {@link #getDropDownAnchor} regardless
437 * of the size or content of the list. {@link #getDropDownBackground()} will fill any space
438 * that is not used by the list.
439 *
440 * @param dropDownAlwaysVisible Whether to keep the drop-down visible.
441 *
442 * @hide Pending API council approval
443 */
444 public void setDropDownAlwaysVisible(boolean dropDownAlwaysVisible) {
445 mDropDownAlwaysVisible = dropDownAlwaysVisible;
446 }
447
448 /**
449 * Checks whether the drop-down is dismissed when a suggestion is clicked.
450 *
451 * @hide Pending API council approval
452 */
453 public boolean isDropDownDismissedOnCompletion() {
454 return mDropDownDismissedOnCompletion;
455 }
Romain Guy6a678102010-03-16 13:49:31 -0700456
Karl Rosaen875d50a2009-04-23 19:00:21 -0700457 /**
458 * Sets whether the drop-down is dismissed when a suggestion is clicked. This is
459 * true by default.
460 *
461 * @param dropDownDismissedOnCompletion Whether to dismiss the drop-down.
462 *
463 * @hide Pending API council approval
464 */
465 public void setDropDownDismissedOnCompletion(boolean dropDownDismissedOnCompletion) {
466 mDropDownDismissedOnCompletion = dropDownDismissedOnCompletion;
467 }
468
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469 /**
470 * <p>Returns the number of characters the user must type before the drop
471 * down list is shown.</p>
472 *
473 * @return the minimum number of characters to type to show the drop down
474 *
475 * @see #setThreshold(int)
476 */
477 public int getThreshold() {
478 return mThreshold;
479 }
480
481 /**
482 * <p>Specifies the minimum number of characters the user has to type in the
483 * edit box before the drop down list is shown.</p>
484 *
485 * <p>When <code>threshold</code> is less than or equals 0, a threshold of
486 * 1 is applied.</p>
487 *
488 * @param threshold the number of characters to type before the drop down
489 * is shown
490 *
491 * @see #getThreshold()
492 *
493 * @attr ref android.R.styleable#AutoCompleteTextView_completionThreshold
494 */
495 public void setThreshold(int threshold) {
496 if (threshold <= 0) {
497 threshold = 1;
498 }
499
500 mThreshold = threshold;
501 }
502
503 /**
504 * <p>Sets the listener that will be notified when the user clicks an item
505 * in the drop down list.</p>
506 *
507 * @param l the item click listener
508 */
509 public void setOnItemClickListener(AdapterView.OnItemClickListener l) {
510 mItemClickListener = l;
511 }
512
513 /**
514 * <p>Sets the listener that will be notified when the user selects an item
515 * in the drop down list.</p>
516 *
517 * @param l the item selected listener
518 */
519 public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener l) {
520 mItemSelectedListener = l;
521 }
522
523 /**
524 * <p>Returns the listener that is notified whenever the user clicks an item
525 * in the drop down list.</p>
526 *
527 * @return the item click listener
528 *
529 * @deprecated Use {@link #getOnItemClickListener()} intead
530 */
531 @Deprecated
532 public AdapterView.OnItemClickListener getItemClickListener() {
533 return mItemClickListener;
534 }
535
536 /**
537 * <p>Returns the listener that is notified whenever the user selects an
538 * item in the drop down list.</p>
539 *
540 * @return the item selected listener
541 *
542 * @deprecated Use {@link #getOnItemSelectedListener()} intead
543 */
544 @Deprecated
545 public AdapterView.OnItemSelectedListener getItemSelectedListener() {
546 return mItemSelectedListener;
547 }
548
549 /**
550 * <p>Returns the listener that is notified whenever the user clicks an item
551 * in the drop down list.</p>
552 *
553 * @return the item click listener
554 */
555 public AdapterView.OnItemClickListener getOnItemClickListener() {
556 return mItemClickListener;
557 }
558
559 /**
560 * <p>Returns the listener that is notified whenever the user selects an
561 * item in the drop down list.</p>
562 *
563 * @return the item selected listener
564 */
565 public AdapterView.OnItemSelectedListener getOnItemSelectedListener() {
566 return mItemSelectedListener;
567 }
568
569 /**
570 * <p>Returns a filterable list adapter used for auto completion.</p>
571 *
572 * @return a data adapter used for auto completion
573 */
574 public ListAdapter getAdapter() {
575 return mAdapter;
576 }
577
578 /**
579 * <p>Changes the list of data used for auto completion. The provided list
580 * must be a filterable list adapter.</p>
581 *
582 * <p>The caller is still responsible for managing any resources used by the adapter.
583 * Notably, when the AutoCompleteTextView is closed or released, the adapter is not notified.
584 * A common case is the use of {@link android.widget.CursorAdapter}, which
585 * contains a {@link android.database.Cursor} that must be closed. This can be done
586 * automatically (see
587 * {@link android.app.Activity#startManagingCursor(android.database.Cursor)
588 * startManagingCursor()}),
589 * or by manually closing the cursor when the AutoCompleteTextView is dismissed.</p>
590 *
591 * @param adapter the adapter holding the auto completion data
592 *
593 * @see #getAdapter()
594 * @see android.widget.Filterable
595 * @see android.widget.ListAdapter
596 */
597 public <T extends ListAdapter & Filterable> void setAdapter(T adapter) {
Romain Guy6a678102010-03-16 13:49:31 -0700598 if (mObserver == null) {
599 mObserver = new PopupDataSetObserver();
600 } else if (mAdapter != null) {
601 mAdapter.unregisterDataSetObserver(mObserver);
602 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800603 mAdapter = adapter;
604 if (mAdapter != null) {
605 //noinspection unchecked
606 mFilter = ((Filterable) mAdapter).getFilter();
Romain Guy6a678102010-03-16 13:49:31 -0700607 adapter.registerDataSetObserver(mObserver);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 } else {
609 mFilter = null;
610 }
611
612 if (mDropDownList != null) {
613 mDropDownList.setAdapter(mAdapter);
614 }
615 }
616
617 @Override
618 public boolean onKeyPreIme(int keyCode, KeyEvent event) {
Dianne Hackborn8d374262009-09-14 21:21:52 -0700619 if (keyCode == KeyEvent.KEYCODE_BACK && isPopupShowing()
620 && !mDropDownAlwaysVisible) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800621 // special case for the back key, we do not even try to send it
622 // to the drop down list but instead, consume it immediately
Romain Guy2beee4d2010-03-15 17:18:22 -0700623 if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
Dianne Hackborn8d374262009-09-14 21:21:52 -0700624 getKeyDispatcherState().startTracking(event, this);
625 return true;
Bjorn Bringert4eb3efc2009-10-06 09:26:06 +0100626 } else if (event.getAction() == KeyEvent.ACTION_UP) {
627 getKeyDispatcherState().handleUpEvent(event);
628 if (event.isTracking() && !event.isCanceled()) {
629 dismissDropDown();
630 return true;
631 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800632 }
633 }
634 return super.onKeyPreIme(keyCode, event);
635 }
636
637 @Override
638 public boolean onKeyUp(int keyCode, KeyEvent event) {
639 if (isPopupShowing() && mDropDownList.getSelectedItemPosition() >= 0) {
640 boolean consumed = mDropDownList.onKeyUp(keyCode, event);
641 if (consumed) {
642 switch (keyCode) {
643 // if the list accepts the key events and the key event
644 // was a click, the text view gets the selected item
645 // from the drop down as its content
646 case KeyEvent.KEYCODE_ENTER:
647 case KeyEvent.KEYCODE_DPAD_CENTER:
648 performCompletion();
649 return true;
650 }
651 }
652 }
653 return super.onKeyUp(keyCode, event);
654 }
655
656 @Override
657 public boolean onKeyDown(int keyCode, KeyEvent event) {
658 // when the drop down is shown, we drive it directly
659 if (isPopupShowing()) {
660 // the key events are forwarded to the list in the drop down view
661 // note that ListView handles space but we don't want that to happen
662 // also if selection is not currently in the drop down, then don't
663 // let center or enter presses go there since that would cause it
664 // to select one of its items
665 if (keyCode != KeyEvent.KEYCODE_SPACE
666 && (mDropDownList.getSelectedItemPosition() >= 0
667 || (keyCode != KeyEvent.KEYCODE_ENTER
668 && keyCode != KeyEvent.KEYCODE_DPAD_CENTER))) {
669 int curIndex = mDropDownList.getSelectedItemPosition();
670 boolean consumed;
Romain Guy2beee4d2010-03-15 17:18:22 -0700671
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800672 final boolean below = !mPopup.isAboveAnchor();
Romain Guy2beee4d2010-03-15 17:18:22 -0700673
Romain Guy11d5bfd2010-03-17 11:08:17 -0700674 final ListAdapter adapter = mAdapter;
675
676 boolean allEnabled;
677 int firstItem = Integer.MAX_VALUE;
678 int lastItem = Integer.MIN_VALUE;
Romain Guy2beee4d2010-03-15 17:18:22 -0700679
Romain Guy11d5bfd2010-03-17 11:08:17 -0700680 if (adapter != null) {
681 allEnabled = adapter.areAllItemsEnabled();
682 firstItem = allEnabled ? 0 :
683 mDropDownList.lookForSelectablePosition(0, true);
684 lastItem = allEnabled ? adapter.getCount() - 1 :
685 mDropDownList.lookForSelectablePosition(adapter.getCount() - 1, false);
686 }
687
Romain Guy2beee4d2010-03-15 17:18:22 -0700688 if ((below && keyCode == KeyEvent.KEYCODE_DPAD_UP && curIndex <= firstItem) ||
689 (!below && keyCode == KeyEvent.KEYCODE_DPAD_DOWN && curIndex >= lastItem)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800690 // When the selection is at the top, we block the key
691 // event to prevent focus from moving.
Romain Guy465dee42009-06-17 16:49:17 -0700692 clearListSelection();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800693 mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
Romain Guye29f0642009-06-23 21:27:02 -0700694 showDropDown();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800695 return true;
Romain Guy465dee42009-06-17 16:49:17 -0700696 } else {
697 // WARNING: Please read the comment where mListSelectionHidden
698 // is declared
699 mDropDownList.mListSelectionHidden = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800700 }
Romain Guy465dee42009-06-17 16:49:17 -0700701
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800702 consumed = mDropDownList.onKeyDown(keyCode, event);
Romain Guy465dee42009-06-17 16:49:17 -0700703 if (DEBUG) Log.v(TAG, "Key down: code=" + keyCode + " list consumed=" + consumed);
704
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800705 if (consumed) {
706 // If it handled the key event, then the user is
707 // navigating in the list, so we should put it in front.
708 mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
709 // Here's a little trick we need to do to make sure that
710 // the list view is actually showing its focus indicator,
711 // by ensuring it has focus and getting its window out
712 // of touch mode.
713 mDropDownList.requestFocusFromTouch();
Romain Guye29f0642009-06-23 21:27:02 -0700714 showDropDown();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800715
716 switch (keyCode) {
717 // avoid passing the focus from the text view to the
718 // next component
719 case KeyEvent.KEYCODE_ENTER:
720 case KeyEvent.KEYCODE_DPAD_CENTER:
721 case KeyEvent.KEYCODE_DPAD_DOWN:
722 case KeyEvent.KEYCODE_DPAD_UP:
723 return true;
724 }
725 } else {
726 if (below && keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
727 // when the selection is at the bottom, we block the
728 // event to avoid going to the next focusable widget
Romain Guy2beee4d2010-03-15 17:18:22 -0700729 if (curIndex == lastItem) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800730 return true;
731 }
Romain Guy2beee4d2010-03-15 17:18:22 -0700732 } else if (!below && keyCode == KeyEvent.KEYCODE_DPAD_UP &&
733 curIndex == firstItem) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800734 return true;
735 }
736 }
737 }
738 } else {
739 switch(keyCode) {
740 case KeyEvent.KEYCODE_DPAD_DOWN:
741 performValidation();
742 }
743 }
744
745 mLastKeyCode = keyCode;
746 boolean handled = super.onKeyDown(keyCode, event);
747 mLastKeyCode = KeyEvent.KEYCODE_UNKNOWN;
748
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700749 if (handled && isPopupShowing() && mDropDownList != null) {
750 clearListSelection();
751 }
752
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800753 return handled;
754 }
755
756 /**
757 * Returns <code>true</code> if the amount of text in the field meets
758 * or exceeds the {@link #getThreshold} requirement. You can override
759 * this to impose a different standard for when filtering will be
760 * triggered.
761 */
762 public boolean enoughToFilter() {
763 if (DEBUG) Log.v(TAG, "Enough to filter: len=" + getText().length()
764 + " threshold=" + mThreshold);
765 return getText().length() >= mThreshold;
766 }
767
768 /**
769 * This is used to watch for edits to the text view. Note that we call
770 * to methods on the auto complete text view class so that we can access
771 * private vars without going through thunks.
772 */
773 private class MyWatcher implements TextWatcher {
774 public void afterTextChanged(Editable s) {
775 doAfterTextChanged();
776 }
777 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
778 doBeforeTextChanged();
779 }
780 public void onTextChanged(CharSequence s, int start, int before, int count) {
781 }
782 }
783
784 void doBeforeTextChanged() {
785 if (mBlockCompletion) return;
786
787 // when text is changed, inserted or deleted, we attempt to show
788 // the drop down
789 mOpenBefore = isPopupShowing();
790 if (DEBUG) Log.v(TAG, "before text changed: open=" + mOpenBefore);
791 }
792
793 void doAfterTextChanged() {
794 if (mBlockCompletion) return;
795
796 // if the list was open before the keystroke, but closed afterwards,
797 // then something in the keystroke processing (an input filter perhaps)
798 // called performCompletion() and we shouldn't do any more processing.
799 if (DEBUG) Log.v(TAG, "after text changed: openBefore=" + mOpenBefore
800 + " open=" + isPopupShowing());
801 if (mOpenBefore && !isPopupShowing()) {
802 return;
803 }
804
805 // the drop down is shown only when a minimum number of characters
806 // was typed in the text view
807 if (enoughToFilter()) {
808 if (mFilter != null) {
809 performFiltering(getText(), mLastKeyCode);
810 }
811 } else {
812 // drop down is automatically dismissed when enough characters
813 // are deleted from the text view
Romain Guy72998072009-06-22 11:09:20 -0700814 if (!mDropDownAlwaysVisible) dismissDropDown();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800815 if (mFilter != null) {
816 mFilter.filter(null);
817 }
818 }
819 }
820
821 /**
822 * <p>Indicates whether the popup menu is showing.</p>
823 *
824 * @return true if the popup menu is showing, false otherwise
825 */
826 public boolean isPopupShowing() {
827 return mPopup.isShowing();
828 }
829
830 /**
831 * <p>Converts the selected item from the drop down list into a sequence
832 * of character that can be used in the edit box.</p>
833 *
834 * @param selectedItem the item selected by the user for completion
835 *
836 * @return a sequence of characters representing the selected suggestion
837 */
838 protected CharSequence convertSelectionToString(Object selectedItem) {
839 return mFilter.convertResultToString(selectedItem);
840 }
841
842 /**
843 * <p>Clear the list selection. This may only be temporary, as user input will often bring
844 * it back.
845 */
846 public void clearListSelection() {
Romain Guy465dee42009-06-17 16:49:17 -0700847 final DropDownListView list = mDropDownList;
848 if (list != null) {
849 // WARNING: Please read the comment where mListSelectionHidden is declared
850 list.mListSelectionHidden = true;
851 list.hideSelector();
852 list.requestLayout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800853 }
854 }
855
856 /**
857 * Set the position of the dropdown view selection.
858 *
859 * @param position The position to move the selector to.
860 */
861 public void setListSelection(int position) {
862 if (mPopup.isShowing() && (mDropDownList != null)) {
Romain Guyfb7ed102009-07-07 15:18:30 -0700863 mDropDownList.mListSelectionHidden = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800864 mDropDownList.setSelection(position);
865 // ListView.setSelection() will call requestLayout()
866 }
867 }
868
869 /**
870 * Get the position of the dropdown view selection, if there is one. Returns
871 * {@link ListView#INVALID_POSITION ListView.INVALID_POSITION} if there is no dropdown or if
872 * there is no selection.
873 *
874 * @return the position of the current selection, if there is one, or
875 * {@link ListView#INVALID_POSITION ListView.INVALID_POSITION} if not.
876 *
877 * @see ListView#getSelectedItemPosition()
878 */
879 public int getListSelection() {
880 if (mPopup.isShowing() && (mDropDownList != null)) {
881 return mDropDownList.getSelectedItemPosition();
882 }
883 return ListView.INVALID_POSITION;
884 }
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700885
886 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800887 * <p>Starts filtering the content of the drop down list. The filtering
888 * pattern is the content of the edit box. Subclasses should override this
889 * method to filter with a different pattern, for instance a substring of
890 * <code>text</code>.</p>
891 *
892 * @param text the filtering pattern
893 * @param keyCode the last character inserted in the edit box; beware that
894 * this will be null when text is being added through a soft input method.
895 */
896 @SuppressWarnings({ "UnusedDeclaration" })
897 protected void performFiltering(CharSequence text, int keyCode) {
898 mFilter.filter(text, this);
899 }
900
901 /**
902 * <p>Performs the text completion by converting the selected item from
903 * the drop down list into a string, replacing the text box's content with
904 * this string and finally dismissing the drop down menu.</p>
905 */
906 public void performCompletion() {
907 performCompletion(null, -1, -1);
908 }
909
910 @Override
911 public void onCommitCompletion(CompletionInfo completion) {
912 if (isPopupShowing()) {
913 mBlockCompletion = true;
914 replaceText(completion.getText());
915 mBlockCompletion = false;
916
917 if (mItemClickListener != null) {
918 final DropDownListView list = mDropDownList;
919 // Note that we don't have a View here, so we will need to
920 // supply null. Hopefully no existing apps crash...
921 mItemClickListener.onItemClick(list, null, completion.getPosition(),
922 completion.getId());
923 }
924 }
925 }
926
927 private void performCompletion(View selectedView, int position, long id) {
928 if (isPopupShowing()) {
929 Object selectedItem;
930 if (position < 0) {
931 selectedItem = mDropDownList.getSelectedItem();
932 } else {
933 selectedItem = mAdapter.getItem(position);
934 }
935 if (selectedItem == null) {
936 Log.w(TAG, "performCompletion: no selected item");
937 return;
938 }
939
940 mBlockCompletion = true;
941 replaceText(convertSelectionToString(selectedItem));
942 mBlockCompletion = false;
943
944 if (mItemClickListener != null) {
945 final DropDownListView list = mDropDownList;
946
947 if (selectedView == null || position < 0) {
948 selectedView = list.getSelectedView();
949 position = list.getSelectedItemPosition();
950 id = list.getSelectedItemId();
951 }
952 mItemClickListener.onItemClick(list, selectedView, position, id);
953 }
954 }
955
Romain Guy72998072009-06-22 11:09:20 -0700956 if (mDropDownDismissedOnCompletion && !mDropDownAlwaysVisible) {
Karl Rosaen875d50a2009-04-23 19:00:21 -0700957 dismissDropDown();
958 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800959 }
960
961 /**
962 * Identifies whether the view is currently performing a text completion, so subclasses
963 * can decide whether to respond to text changed events.
964 */
965 public boolean isPerformingCompletion() {
966 return mBlockCompletion;
967 }
968
969 /**
Karl Rosaen875d50a2009-04-23 19:00:21 -0700970 * Like {@link #setText(CharSequence)}, except that it can disable filtering.
971 *
972 * @param filter If <code>false</code>, no filtering will be performed
973 * as a result of this call.
974 *
975 * @hide Pending API council approval.
976 */
977 public void setText(CharSequence text, boolean filter) {
978 if (filter) {
979 setText(text);
980 } else {
981 mBlockCompletion = true;
982 setText(text);
983 mBlockCompletion = false;
984 }
985 }
Romain Guy6a678102010-03-16 13:49:31 -0700986
Karl Rosaen875d50a2009-04-23 19:00:21 -0700987 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800988 * <p>Performs the text completion by replacing the current text by the
989 * selected item. Subclasses should override this method to avoid replacing
990 * the whole content of the edit box.</p>
991 *
992 * @param text the selected suggestion in the drop down list
993 */
994 protected void replaceText(CharSequence text) {
Daisuke Miyakawac1d27482009-05-25 17:37:41 +0900995 clearComposingText();
996
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800997 setText(text);
998 // make sure we keep the caret at the end of the text view
999 Editable spannable = getText();
1000 Selection.setSelection(spannable, spannable.length());
1001 }
1002
Karl Rosaen875d50a2009-04-23 19:00:21 -07001003 /** {@inheritDoc} */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001004 public void onFilterComplete(int count) {
Romain Guy4f43ae02010-03-17 10:31:15 -07001005 updateDropDownForFilter(count);
1006
1007 }
1008
1009 private void updateDropDownForFilter(int count) {
Bjorn Bringert50145bc2009-06-11 12:30:48 +01001010 // Not attached to window, don't update drop-down
1011 if (getWindowVisibility() == View.GONE) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001012
1013 /*
1014 * This checks enoughToFilter() again because filtering requests
1015 * are asynchronous, so the result may come back after enough text
1016 * has since been deleted to make it no longer appropriate
1017 * to filter.
1018 */
1019
Karl Rosaen875d50a2009-04-23 19:00:21 -07001020 if ((count > 0 || mDropDownAlwaysVisible) && enoughToFilter()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001021 if (hasFocus() && hasWindowFocus()) {
1022 showDropDown();
1023 }
Romain Guy72998072009-06-22 11:09:20 -07001024 } else if (!mDropDownAlwaysVisible) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001025 dismissDropDown();
1026 }
1027 }
1028
1029 @Override
1030 public void onWindowFocusChanged(boolean hasWindowFocus) {
1031 super.onWindowFocusChanged(hasWindowFocus);
Romain Guy72998072009-06-22 11:09:20 -07001032 if (!hasWindowFocus && !mDropDownAlwaysVisible) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001033 dismissDropDown();
1034 }
1035 }
1036
1037 @Override
Romain Guy43c9cdf2010-01-27 13:53:55 -08001038 protected void onDisplayHint(int hint) {
1039 super.onDisplayHint(hint);
1040 switch (hint) {
1041 case INVISIBLE:
1042 if (!mDropDownAlwaysVisible) {
1043 dismissDropDown();
1044 }
1045 break;
1046 }
1047 }
1048
1049 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001050 protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
1051 super.onFocusChanged(focused, direction, previouslyFocusedRect);
Evan Millar280c7532009-11-11 14:59:22 -08001052 // Perform validation if the view is losing focus.
1053 if (!focused) {
1054 performValidation();
1055 }
Romain Guy72998072009-06-22 11:09:20 -07001056 if (!focused && !mDropDownAlwaysVisible) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001057 dismissDropDown();
1058 }
1059 }
1060
1061 @Override
1062 protected void onAttachedToWindow() {
1063 super.onAttachedToWindow();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001064 }
1065
1066 @Override
1067 protected void onDetachedFromWindow() {
1068 dismissDropDown();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001069 super.onDetachedFromWindow();
1070 }
1071
1072 /**
1073 * <p>Closes the drop down if present on screen.</p>
1074 */
1075 public void dismissDropDown() {
1076 InputMethodManager imm = InputMethodManager.peekInstance();
1077 if (imm != null) {
1078 imm.displayCompletions(this, null);
1079 }
1080 mPopup.dismiss();
1081 mPopup.setContentView(null);
1082 mDropDownList = null;
1083 }
1084
1085 @Override
Romain Guy3e141682010-03-08 17:44:40 -08001086 protected boolean setFrame(final int l, int t, final int r, int b) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001087 boolean result = super.setFrame(l, t, r, b);
1088
1089 if (mPopup.isShowing()) {
Romain Guy3e141682010-03-08 17:44:40 -08001090 showDropDown();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001091 }
1092
1093 return result;
1094 }
1095
1096 /**
1097 * <p>Used for lazy instantiation of the anchor view from the id we have. If the value of
1098 * the id is NO_ID or we can't find a view for the given id, we return this TextView as
1099 * the default anchoring point.</p>
1100 */
1101 private View getDropDownAnchorView() {
1102 if (mDropDownAnchorView == null && mDropDownAnchorId != View.NO_ID) {
1103 mDropDownAnchorView = getRootView().findViewById(mDropDownAnchorId);
1104 }
1105 return mDropDownAnchorView == null ? this : mDropDownAnchorView;
1106 }
1107
1108 /**
Satish Sampathfef8d3e2009-07-01 17:48:42 +01001109 * Issues a runnable to show the dropdown as soon as possible.
Satish Sampath1b1a6e42009-07-01 19:06:24 +01001110 *
Mike LeBeauffe3ecf2009-07-16 18:18:37 -07001111 * @hide internal used only by SearchDialog
Satish Sampathfef8d3e2009-07-01 17:48:42 +01001112 */
1113 public void showDropDownAfterLayout() {
1114 post(mShowDropDownRunnable);
1115 }
Mike LeBeauffe3ecf2009-07-16 18:18:37 -07001116
1117 /**
1118 * Ensures that the drop down is not obscuring the IME.
Amith Yamasanid25eb352010-03-10 21:09:28 -08001119 * @param visible whether the ime should be in front. If false, the ime is pushed to
1120 * the background.
Mike LeBeauffe3ecf2009-07-16 18:18:37 -07001121 * @hide internal used only here and SearchDialog
1122 */
Amith Yamasanid25eb352010-03-10 21:09:28 -08001123 public void ensureImeVisible(boolean visible) {
1124 mPopup.setInputMethodMode(visible
1125 ? PopupWindow.INPUT_METHOD_NEEDED : PopupWindow.INPUT_METHOD_NOT_NEEDED);
Mike LeBeauffe3ecf2009-07-16 18:18:37 -07001126 showDropDown();
1127 }
Satish Sampathfef8d3e2009-07-01 17:48:42 +01001128
1129 /**
Bjorn Bringert003ad482009-07-28 12:03:29 +01001130 * @hide internal used only here and SearchDialog
1131 */
1132 public boolean isInputMethodNotNeeded() {
1133 return mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED;
1134 }
1135
1136 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001137 * <p>Displays the drop down on screen.</p>
1138 */
1139 public void showDropDown() {
1140 int height = buildDropDown();
Romain Guye29f0642009-06-23 21:27:02 -07001141
1142 int widthSpec = 0;
1143 int heightSpec = 0;
1144
Bjorn Bringert003ad482009-07-28 12:03:29 +01001145 boolean noInputMethod = isInputMethodNotNeeded();
Romain Guye29f0642009-06-23 21:27:02 -07001146
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001147 if (mPopup.isShowing()) {
Romain Guy980a9382010-01-08 15:06:28 -08001148 if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001149 // The call to PopupWindow's update method below can accept -1 for any
1150 // value you do not want to update.
1151 widthSpec = -1;
1152 } else if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
1153 widthSpec = getDropDownAnchorView().getWidth();
1154 } else {
1155 widthSpec = mDropDownWidth;
1156 }
Romain Guye29f0642009-06-23 21:27:02 -07001157
Romain Guy980a9382010-01-08 15:06:28 -08001158 if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
Romain Guye29f0642009-06-23 21:27:02 -07001159 // The call to PopupWindow's update method below can accept -1 for any
1160 // value you do not want to update.
Romain Guy980a9382010-01-08 15:06:28 -08001161 heightSpec = noInputMethod ? height : ViewGroup.LayoutParams.MATCH_PARENT;
Romain Guye29f0642009-06-23 21:27:02 -07001162 if (noInputMethod) {
1163 mPopup.setWindowLayoutMode(
Romain Guy980a9382010-01-08 15:06:28 -08001164 mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
1165 ViewGroup.LayoutParams.MATCH_PARENT : 0, 0);
Romain Guye29f0642009-06-23 21:27:02 -07001166 } else {
1167 mPopup.setWindowLayoutMode(
Romain Guy980a9382010-01-08 15:06:28 -08001168 mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
1169 ViewGroup.LayoutParams.MATCH_PARENT : 0,
1170 ViewGroup.LayoutParams.MATCH_PARENT);
Romain Guye29f0642009-06-23 21:27:02 -07001171 }
1172 } else if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
1173 heightSpec = height;
1174 } else {
1175 heightSpec = mDropDownHeight;
1176 }
1177
Romain Guyaa1c6312009-09-30 11:36:01 -07001178 mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible);
Mike LeBeaud4760d72009-07-22 15:04:27 -07001179
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001180 mPopup.update(getDropDownAnchorView(), mDropDownHorizontalOffset,
Romain Guye29f0642009-06-23 21:27:02 -07001181 mDropDownVerticalOffset, widthSpec, heightSpec);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001182 } else {
Romain Guy980a9382010-01-08 15:06:28 -08001183 if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
1184 widthSpec = ViewGroup.LayoutParams.MATCH_PARENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001185 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001186 if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
1187 mPopup.setWidth(getDropDownAnchorView().getWidth());
1188 } else {
1189 mPopup.setWidth(mDropDownWidth);
1190 }
1191 }
Romain Guye29f0642009-06-23 21:27:02 -07001192
Romain Guy980a9382010-01-08 15:06:28 -08001193 if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
1194 heightSpec = ViewGroup.LayoutParams.MATCH_PARENT;
Romain Guye29f0642009-06-23 21:27:02 -07001195 } else {
1196 if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
1197 mPopup.setHeight(height);
1198 } else {
1199 mPopup.setHeight(mDropDownHeight);
1200 }
1201 }
1202
1203 mPopup.setWindowLayoutMode(widthSpec, heightSpec);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001204 mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
Karl Rosaen98e333f2009-04-28 10:39:09 -07001205
1206 // use outside touchable to dismiss drop down when touching outside of it, so
1207 // only set this if the dropdown is not always visible
Romain Guyaa1c6312009-09-30 11:36:01 -07001208 mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible);
1209 mPopup.setTouchInterceptor(new PopupTouchInterceptor());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001210 mPopup.showAsDropDown(getDropDownAnchorView(),
1211 mDropDownHorizontalOffset, mDropDownVerticalOffset);
1212 mDropDownList.setSelection(ListView.INVALID_POSITION);
Romain Guy465dee42009-06-17 16:49:17 -07001213 clearListSelection();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001214 post(mHideSelector);
1215 }
1216 }
Mike LeBeaud4760d72009-07-22 15:04:27 -07001217
1218 /**
1219 * Forces outside touches to be ignored. Normally if {@link #isDropDownAlwaysVisible()} is
1220 * false, we allow outside touch to dismiss the dropdown. If this is set to true, then we
1221 * ignore outside touch even when the drop down is not set to always visible.
1222 *
1223 * @hide used only by SearchDialog
1224 */
1225 public void setForceIgnoreOutsideTouch(boolean forceIgnoreOutsideTouch) {
1226 mForceIgnoreOutsideTouch = forceIgnoreOutsideTouch;
1227 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001228
1229 /**
1230 * <p>Builds the popup window's content and returns the height the popup
1231 * should have. Returns -1 when the content already exists.</p>
1232 *
1233 * @return the content's height or -1 if content already exists
1234 */
1235 private int buildDropDown() {
1236 ViewGroup dropDownView;
1237 int otherHeights = 0;
1238
Romain Guy4f43ae02010-03-17 10:31:15 -07001239 final ListAdapter adapter = mAdapter;
1240 if (adapter != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001241 InputMethodManager imm = InputMethodManager.peekInstance();
1242 if (imm != null) {
Romain Guy4f43ae02010-03-17 10:31:15 -07001243 final int count = Math.min(adapter.getCount(), 20);
1244 CompletionInfo[] completions = new CompletionInfo[count];
1245 int realCount = 0;
1246
1247 for (int i = 0; i < count; i++) {
1248 if (adapter.isEnabled(i)) {
1249 realCount++;
1250 Object item = adapter.getItem(i);
1251 long id = adapter.getItemId(i);
1252 completions[i] = new CompletionInfo(id, i,
1253 convertSelectionToString(item));
1254 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001255 }
Romain Guy4f43ae02010-03-17 10:31:15 -07001256
1257 if (realCount != count) {
1258 CompletionInfo[] tmp = new CompletionInfo[realCount];
1259 System.arraycopy(completions, 0, tmp, 0, realCount);
1260 completions = tmp;
1261 }
1262
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001263 imm.displayCompletions(this, completions);
1264 }
1265 }
1266
1267 if (mDropDownList == null) {
1268 Context context = getContext();
1269
1270 mHideSelector = new ListSelectorHider();
1271
Satish Sampathfef8d3e2009-07-01 17:48:42 +01001272 /**
1273 * This Runnable exists for the sole purpose of checking if the view layout has got
1274 * completed and if so call showDropDown to display the drop down. This is used to show
1275 * the drop down as soon as possible after user opens up the search dialog, without
1276 * waiting for the normal UI pipeline to do it's job which is slower than this method.
1277 */
1278 mShowDropDownRunnable = new Runnable() {
1279 public void run() {
1280 // View layout should be all done before displaying the drop down.
1281 View view = getDropDownAnchorView();
1282 if (view != null && view.getWindowToken() != null) {
1283 showDropDown();
1284 }
1285 }
1286 };
1287
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001288 mDropDownList = new DropDownListView(context);
1289 mDropDownList.setSelector(mDropDownListHighlight);
Romain Guy4f43ae02010-03-17 10:31:15 -07001290 mDropDownList.setAdapter(adapter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001291 mDropDownList.setVerticalFadingEdgeEnabled(true);
1292 mDropDownList.setOnItemClickListener(mDropDownItemClickListener);
1293 mDropDownList.setFocusable(true);
1294 mDropDownList.setFocusableInTouchMode(true);
Romain Guy465dee42009-06-17 16:49:17 -07001295 mDropDownList.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
1296 public void onItemSelected(AdapterView<?> parent, View view,
1297 int position, long id) {
1298
1299 if (position != -1) {
Eric Fischerd0721542009-06-26 16:14:59 -07001300 DropDownListView dropDownList = mDropDownList;
1301
1302 if (dropDownList != null) {
1303 dropDownList.mListSelectionHidden = false;
1304 }
Romain Guy465dee42009-06-17 16:49:17 -07001305 }
1306 }
1307
1308 public void onNothingSelected(AdapterView<?> parent) {
1309 }
1310 });
Adam Powell387d8f82010-03-23 13:29:34 -07001311 mDropDownList.setOnScrollListener(new PopupScrollListener());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001312
1313 if (mItemSelectedListener != null) {
1314 mDropDownList.setOnItemSelectedListener(mItemSelectedListener);
1315 }
1316
1317 dropDownView = mDropDownList;
1318
1319 View hintView = getHintView(context);
1320 if (hintView != null) {
1321 // if an hint has been specified, we accomodate more space for it and
1322 // add a text view in the drop down menu, at the bottom of the list
1323 LinearLayout hintContainer = new LinearLayout(context);
1324 hintContainer.setOrientation(LinearLayout.VERTICAL);
1325
1326 LinearLayout.LayoutParams hintParams = new LinearLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001327 ViewGroup.LayoutParams.MATCH_PARENT, 0, 1.0f
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001328 );
1329 hintContainer.addView(dropDownView, hintParams);
1330 hintContainer.addView(hintView);
1331
1332 // measure the hint's height to find how much more vertical space
1333 // we need to add to the drop down's height
1334 int widthSpec = MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST);
1335 int heightSpec = MeasureSpec.UNSPECIFIED;
1336 hintView.measure(widthSpec, heightSpec);
1337
1338 hintParams = (LinearLayout.LayoutParams) hintView.getLayoutParams();
1339 otherHeights = hintView.getMeasuredHeight() + hintParams.topMargin
1340 + hintParams.bottomMargin;
1341
1342 dropDownView = hintContainer;
1343 }
1344
1345 mPopup.setContentView(dropDownView);
1346 } else {
1347 dropDownView = (ViewGroup) mPopup.getContentView();
1348 final View view = dropDownView.findViewById(HINT_VIEW_ID);
1349 if (view != null) {
1350 LinearLayout.LayoutParams hintParams =
1351 (LinearLayout.LayoutParams) view.getLayoutParams();
1352 otherHeights = view.getMeasuredHeight() + hintParams.topMargin
1353 + hintParams.bottomMargin;
1354 }
1355 }
1356
Bjorn Bringert00a5b992009-07-20 15:36:24 +01001357 // Max height available on the screen for a popup.
1358 boolean ignoreBottomDecorations =
Mike LeBeau98acd542009-05-07 19:04:39 -07001359 mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED;
1360 final int maxHeight = mPopup.getMaxAvailableHeight(
1361 getDropDownAnchorView(), mDropDownVerticalOffset, ignoreBottomDecorations);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001362
Romain Guy73beee22010-01-19 16:56:05 -08001363 // getMaxAvailableHeight() subtracts the padding, so we put it back,
1364 // to get the available height for the whole window
1365 int padding = 0;
1366 Drawable background = mPopup.getBackground();
1367 if (background != null) {
1368 background.getPadding(mTempRect);
1369 padding = mTempRect.top + mTempRect.bottom;
1370 }
1371
Romain Guy980a9382010-01-08 15:06:28 -08001372 if (mDropDownAlwaysVisible || mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
Bjorn Bringert5420d012009-07-28 20:17:27 +01001373 return maxHeight + padding;
Romain Guye29f0642009-06-23 21:27:02 -07001374 }
Karl Rosaen875d50a2009-04-23 19:00:21 -07001375
Romain Guy73beee22010-01-19 16:56:05 -08001376 final int listContent = mDropDownList.measureHeightOfChildren(MeasureSpec.UNSPECIFIED,
1377 0, ListView.NO_POSITION, maxHeight - otherHeights, 2);
1378 // add padding only if the list has items in it, that way we don't show
1379 // the popup if it is not needed
1380 if (listContent > 0) otherHeights += padding;
1381
1382 return listContent + otherHeights;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001383 }
1384
1385 private View getHintView(Context context) {
1386 if (mHintText != null && mHintText.length() > 0) {
1387 final TextView hintView = (TextView) LayoutInflater.from(context).inflate(
1388 mHintResource, null).findViewById(com.android.internal.R.id.text1);
1389 hintView.setText(mHintText);
1390 hintView.setId(HINT_VIEW_ID);
1391 return hintView;
1392 } else {
1393 return null;
1394 }
1395 }
1396
1397 /**
1398 * Sets the validator used to perform text validation.
1399 *
1400 * @param validator The validator used to validate the text entered in this widget.
1401 *
1402 * @see #getValidator()
1403 * @see #performValidation()
1404 */
1405 public void setValidator(Validator validator) {
1406 mValidator = validator;
1407 }
1408
1409 /**
1410 * Returns the Validator set with {@link #setValidator},
1411 * or <code>null</code> if it was not set.
1412 *
1413 * @see #setValidator(android.widget.AutoCompleteTextView.Validator)
1414 * @see #performValidation()
1415 */
1416 public Validator getValidator() {
1417 return mValidator;
1418 }
1419
1420 /**
1421 * If a validator was set on this view and the current string is not valid,
1422 * ask the validator to fix it.
1423 *
1424 * @see #getValidator()
1425 * @see #setValidator(android.widget.AutoCompleteTextView.Validator)
1426 */
1427 public void performValidation() {
1428 if (mValidator == null) return;
1429
1430 CharSequence text = getText();
1431
1432 if (!TextUtils.isEmpty(text) && !mValidator.isValid(text)) {
1433 setText(mValidator.fixText(text));
1434 }
1435 }
1436
1437 /**
1438 * Returns the Filter obtained from {@link Filterable#getFilter},
1439 * or <code>null</code> if {@link #setAdapter} was not called with
1440 * a Filterable.
1441 */
1442 protected Filter getFilter() {
1443 return mFilter;
1444 }
1445
1446 private class ListSelectorHider implements Runnable {
1447 public void run() {
Romain Guy465dee42009-06-17 16:49:17 -07001448 clearListSelection();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001449 }
1450 }
1451
Adam Powell387d8f82010-03-23 13:29:34 -07001452 private class ResizePopupRunnable implements Runnable {
1453 public void run() {
1454 mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
1455 showDropDown();
1456 }
1457 }
1458
Romain Guyaa1c6312009-09-30 11:36:01 -07001459 private class PopupTouchInterceptor implements OnTouchListener {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001460 public boolean onTouch(View v, MotionEvent event) {
Adam Powell387d8f82010-03-23 13:29:34 -07001461 final int action = event.getAction();
1462 if (action == MotionEvent.ACTION_DOWN &&
Romain Guyaa1c6312009-09-30 11:36:01 -07001463 mPopup != null && mPopup.isShowing()) {
Adam Powell387d8f82010-03-23 13:29:34 -07001464 postDelayed(mResizePopupRunnable, EXPAND_LIST_TIMEOUT);
1465 } else if (action == MotionEvent.ACTION_UP) {
1466 removeCallbacks(mResizePopupRunnable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001467 }
1468 return false;
1469 }
1470 }
1471
Adam Powell387d8f82010-03-23 13:29:34 -07001472 private class PopupScrollListener implements ListView.OnScrollListener {
1473 public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
1474 int totalItemCount) {
1475
1476 }
1477
1478 public void onScrollStateChanged(AbsListView view, int scrollState) {
1479 if (scrollState == SCROLL_STATE_TOUCH_SCROLL &&
1480 !isInputMethodNotNeeded() && mPopup.getContentView() != null) {
1481 removeCallbacks(mResizePopupRunnable);
1482 mResizePopupRunnable.run();
1483 }
1484 }
1485 }
1486
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001487 private class DropDownItemClickListener implements AdapterView.OnItemClickListener {
1488 public void onItemClick(AdapterView parent, View v, int position, long id) {
1489 performCompletion(v, position, id);
1490 }
1491 }
1492
1493 /**
1494 * <p>Wrapper class for a ListView. This wrapper hijacks the focus to
1495 * make sure the list uses the appropriate drawables and states when
1496 * displayed on screen within a drop down. The focus is never actually
1497 * passed to the drop down; the list only looks focused.</p>
1498 */
1499 private static class DropDownListView extends ListView {
Romain Guy465dee42009-06-17 16:49:17 -07001500 /*
1501 * WARNING: This is a workaround for a touch mode issue.
1502 *
1503 * Touch mode is propagated lazily to windows. This causes problems in
1504 * the following scenario:
1505 * - Type something in the AutoCompleteTextView and get some results
1506 * - Move down with the d-pad to select an item in the list
1507 * - Move up with the d-pad until the selection disappears
1508 * - Type more text in the AutoCompleteTextView *using the soft keyboard*
1509 * and get new results; you are now in touch mode
1510 * - The selection comes back on the first item in the list, even though
1511 * the list is supposed to be in touch mode
1512 *
1513 * Using the soft keyboard triggers the touch mode change but that change
1514 * is propagated to our window only after the first list layout, therefore
1515 * after the list attempts to resurrect the selection.
1516 *
1517 * The trick to work around this issue is to pretend the list is in touch
1518 * mode when we know that the selection should not appear, that is when
1519 * we know the user moved the selection away from the list.
1520 *
1521 * This boolean is set to true whenever we explicitely hide the list's
1522 * selection and reset to false whenver we know the user moved the
1523 * selection back to the list.
1524 *
1525 * When this boolean is true, isInTouchMode() returns true, otherwise it
1526 * returns super.isInTouchMode().
1527 */
1528 private boolean mListSelectionHidden;
1529
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001530 /**
1531 * <p>Creates a new list view wrapper.</p>
1532 *
1533 * @param context this view's context
1534 */
1535 public DropDownListView(Context context) {
1536 super(context, null, com.android.internal.R.attr.dropDownListViewStyle);
1537 }
1538
1539 /**
1540 * <p>Avoids jarring scrolling effect by ensuring that list elements
1541 * made of a text view fit on a single line.</p>
1542 *
1543 * @param position the item index in the list to get a view for
1544 * @return the view for the specified item
1545 */
1546 @Override
Romain Guy21875052010-01-06 18:48:08 -08001547 View obtainView(int position, boolean[] isScrap) {
1548 View view = super.obtainView(position, isScrap);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001549
1550 if (view instanceof TextView) {
1551 ((TextView) view).setHorizontallyScrolling(true);
1552 }
1553
1554 return view;
1555 }
1556
Romain Guy465dee42009-06-17 16:49:17 -07001557 @Override
1558 public boolean isInTouchMode() {
1559 // WARNING: Please read the comment where mListSelectionHidden is declared
1560 return mListSelectionHidden || super.isInTouchMode();
1561 }
1562
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001563 /**
1564 * <p>Returns the focus state in the drop down.</p>
1565 *
1566 * @return true always
1567 */
1568 @Override
1569 public boolean hasWindowFocus() {
1570 return true;
1571 }
1572
1573 /**
1574 * <p>Returns the focus state in the drop down.</p>
1575 *
1576 * @return true always
1577 */
1578 @Override
1579 public boolean isFocused() {
1580 return true;
1581 }
1582
1583 /**
1584 * <p>Returns the focus state in the drop down.</p>
1585 *
1586 * @return true always
1587 */
1588 @Override
1589 public boolean hasFocus() {
1590 return true;
1591 }
1592
1593 protected int[] onCreateDrawableState(int extraSpace) {
1594 int[] res = super.onCreateDrawableState(extraSpace);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001595 //noinspection ConstantIfStatement
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001596 if (false) {
1597 StringBuilder sb = new StringBuilder("Created drawable state: [");
1598 for (int i=0; i<res.length; i++) {
1599 if (i > 0) sb.append(", ");
1600 sb.append("0x");
1601 sb.append(Integer.toHexString(res[i]));
1602 }
1603 sb.append("]");
1604 Log.i(TAG, sb.toString());
1605 }
1606 return res;
1607 }
1608 }
1609
1610 /**
1611 * This interface is used to make sure that the text entered in this TextView complies to
1612 * a certain format. Since there is no foolproof way to prevent the user from leaving
1613 * this View with an incorrect value in it, all we can do is try to fix it ourselves
1614 * when this happens.
1615 */
1616 public interface Validator {
1617 /**
1618 * Validates the specified text.
1619 *
1620 * @return true If the text currently in the text editor is valid.
1621 *
1622 * @see #fixText(CharSequence)
1623 */
1624 boolean isValid(CharSequence text);
1625
1626 /**
1627 * Corrects the specified text to make it valid.
1628 *
1629 * @param invalidText A string that doesn't pass validation: isValid(invalidText)
1630 * returns false
1631 *
1632 * @return A string based on invalidText such as invoking isValid() on it returns true.
1633 *
1634 * @see #isValid(CharSequence)
1635 */
1636 CharSequence fixText(CharSequence invalidText);
1637 }
Mike LeBeauddf98562009-05-08 17:59:05 -07001638
Karl Rosaen98e333f2009-04-28 10:39:09 -07001639 /**
1640 * Allows us a private hook into the on click event without preventing users from setting
1641 * their own click listener.
1642 */
1643 private class PassThroughClickListener implements OnClickListener {
1644
1645 private View.OnClickListener mWrapped;
1646
1647 /** {@inheritDoc} */
1648 public void onClick(View v) {
1649 onClickImpl();
1650
1651 if (mWrapped != null) mWrapped.onClick(v);
1652 }
1653 }
Romain Guy6a678102010-03-16 13:49:31 -07001654
1655 private class PopupDataSetObserver extends DataSetObserver {
1656 @Override
1657 public void onChanged() {
1658 if (isPopupShowing()) {
1659 // This will resize the popup to fit the new adapter's content
1660 showDropDown();
Romain Guy4f43ae02010-03-17 10:31:15 -07001661 } else if (mAdapter != null) {
1662 // If the popup is not showing already, showing it will cause
1663 // the list of data set observers attached to the adapter to
1664 // change. We can't do it from here, because we are in the middle
1665 // of iterating throught he list of observers.
1666 post(new Runnable() {
1667 public void run() {
Kenny Root7254afd2010-03-19 10:09:59 -07001668 final ListAdapter adapter = mAdapter;
1669 if (adapter != null) {
1670 updateDropDownForFilter(adapter.getCount());
1671 }
Romain Guy4f43ae02010-03-17 10:31:15 -07001672 }
1673 });
Romain Guy6a678102010-03-16 13:49:31 -07001674 }
1675 }
1676
1677 @Override
1678 public void onInvalidated() {
1679 if (!mDropDownAlwaysVisible) {
1680 // There's no data to display so make sure we're not showing
1681 // the drop down and its list
1682 dismissDropDown();
1683 }
1684 }
1685 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001686}