Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2010 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 
     17 package android.widget;
     18 
     19 import android.content.Context;
     20 import android.database.DataSetObserver;
     21 import android.graphics.Rect;
     22 import android.graphics.drawable.Drawable;
     23 import android.os.Handler;
     24 import android.text.TextUtils;
     25 import android.util.AttributeSet;
     26 import android.util.Log;
     27 import android.view.KeyEvent;
     28 import android.view.MotionEvent;
     29 import android.view.View;
     30 import android.view.View.MeasureSpec;
     31 import android.view.View.OnTouchListener;
     32 import android.view.ViewGroup;
     33 import android.view.ViewParent;
     34 
     35 import java.util.Locale;
     36 
     37 /**
     38  * A ListPopupWindow anchors itself to a host view and displays a
     39  * list of choices.
     40  *
     41  * <p>ListPopupWindow contains a number of tricky behaviors surrounding
     42  * positioning, scrolling parents to fit the dropdown, interacting
     43  * sanely with the IME if present, and others.
     44  *
     45  * @see android.widget.AutoCompleteTextView
     46  * @see android.widget.Spinner
     47  */
     48 public class ListPopupWindow {
     49     private static final String TAG = "ListPopupWindow";
     50     private static final boolean DEBUG = false;
     51 
     52     /**
     53      * This value controls the length of time that the user
     54      * must leave a pointer down without scrolling to expand
     55      * the autocomplete dropdown list to cover the IME.
     56      */
     57     private static final int EXPAND_LIST_TIMEOUT = 250;
     58 
     59     private Context mContext;
     60     private PopupWindow mPopup;
     61     private ListAdapter mAdapter;
     62     private DropDownListView mDropDownList;
     63 
     64     private int mDropDownHeight = ViewGroup.LayoutParams.WRAP_CONTENT;
     65     private int mDropDownWidth = ViewGroup.LayoutParams.WRAP_CONTENT;
     66     private int mDropDownHorizontalOffset;
     67     private int mDropDownVerticalOffset;
     68     private boolean mDropDownVerticalOffsetSet;
     69 
     70     private boolean mDropDownAlwaysVisible = false;
     71     private boolean mForceIgnoreOutsideTouch = false;
     72     int mListItemExpandMaximum = Integer.MAX_VALUE;
     73 
     74     private View mPromptView;
     75     private int mPromptPosition = POSITION_PROMPT_ABOVE;
     76 
     77     private DataSetObserver mObserver;
     78 
     79     private View mDropDownAnchorView;
     80 
     81     private Drawable mDropDownListHighlight;
     82 
     83     private AdapterView.OnItemClickListener mItemClickListener;
     84     private AdapterView.OnItemSelectedListener mItemSelectedListener;
     85 
     86     private final ResizePopupRunnable mResizePopupRunnable = new ResizePopupRunnable();
     87     private final PopupTouchInterceptor mTouchInterceptor = new PopupTouchInterceptor();
     88     private final PopupScrollListener mScrollListener = new PopupScrollListener();
     89     private final ListSelectorHider mHideSelector = new ListSelectorHider();
     90     private Runnable mShowDropDownRunnable;
     91 
     92     private Handler mHandler = new Handler();
     93 
     94     private Rect mTempRect = new Rect();
     95 
     96     private boolean mModal;
     97 
     98     private int mLayoutDirection;
     99 
    100     /**
    101      * The provided prompt view should appear above list content.
    102      *
    103      * @see #setPromptPosition(int)
    104      * @see #getPromptPosition()
    105      * @see #setPromptView(View)
    106      */
    107     public static final int POSITION_PROMPT_ABOVE = 0;
    108 
    109     /**
    110      * The provided prompt view should appear below list content.
    111      *
    112      * @see #setPromptPosition(int)
    113      * @see #getPromptPosition()
    114      * @see #setPromptView(View)
    115      */
    116     public static final int POSITION_PROMPT_BELOW = 1;
    117 
    118     /**
    119      * Alias for {@link ViewGroup.LayoutParams#MATCH_PARENT}.
    120      * If used to specify a popup width, the popup will match the width of the anchor view.
    121      * If used to specify a popup height, the popup will fill available space.
    122      */
    123     public static final int MATCH_PARENT = ViewGroup.LayoutParams.MATCH_PARENT;
    124 
    125     /**
    126      * Alias for {@link ViewGroup.LayoutParams#WRAP_CONTENT}.
    127      * If used to specify a popup width, the popup will use the width of its content.
    128      */
    129     public static final int WRAP_CONTENT = ViewGroup.LayoutParams.WRAP_CONTENT;
    130 
    131     /**
    132      * Mode for {@link #setInputMethodMode(int)}: the requirements for the
    133      * input method should be based on the focusability of the popup.  That is
    134      * if it is focusable than it needs to work with the input method, else
    135      * it doesn't.
    136      */
    137     public static final int INPUT_METHOD_FROM_FOCUSABLE = PopupWindow.INPUT_METHOD_FROM_FOCUSABLE;
    138 
    139     /**
    140      * Mode for {@link #setInputMethodMode(int)}: this popup always needs to
    141      * work with an input method, regardless of whether it is focusable.  This
    142      * means that it will always be displayed so that the user can also operate
    143      * the input method while it is shown.
    144      */
    145     public static final int INPUT_METHOD_NEEDED = PopupWindow.INPUT_METHOD_NEEDED;
    146 
    147     /**
    148      * Mode for {@link #setInputMethodMode(int)}: this popup never needs to
    149      * work with an input method, regardless of whether it is focusable.  This
    150      * means that it will always be displayed to use as much space on the
    151      * screen as needed, regardless of whether this covers the input method.
    152      */
    153     public static final int INPUT_METHOD_NOT_NEEDED = PopupWindow.INPUT_METHOD_NOT_NEEDED;
    154 
    155     /**
    156      * Create a new, empty popup window capable of displaying items from a ListAdapter.
    157      * Backgrounds should be set using {@link #setBackgroundDrawable(Drawable)}.
    158      *
    159      * @param context Context used for contained views.
    160      */
    161     public ListPopupWindow(Context context) {
    162         this(context, null, com.android.internal.R.attr.listPopupWindowStyle, 0);
    163     }
    164 
    165     /**
    166      * Create a new, empty popup window capable of displaying items from a ListAdapter.
    167      * Backgrounds should be set using {@link #setBackgroundDrawable(Drawable)}.
    168      *
    169      * @param context Context used for contained views.
    170      * @param attrs Attributes from inflating parent views used to style the popup.
    171      */
    172     public ListPopupWindow(Context context, AttributeSet attrs) {
    173         this(context, attrs, com.android.internal.R.attr.listPopupWindowStyle, 0);
    174     }
    175 
    176     /**
    177      * Create a new, empty popup window capable of displaying items from a ListAdapter.
    178      * Backgrounds should be set using {@link #setBackgroundDrawable(Drawable)}.
    179      *
    180      * @param context Context used for contained views.
    181      * @param attrs Attributes from inflating parent views used to style the popup.
    182      * @param defStyleAttr Default style attribute to use for popup content.
    183      */
    184     public ListPopupWindow(Context context, AttributeSet attrs, int defStyleAttr) {
    185         this(context, attrs, defStyleAttr, 0);
    186     }
    187 
    188     /**
    189      * Create a new, empty popup window capable of displaying items from a ListAdapter.
    190      * Backgrounds should be set using {@link #setBackgroundDrawable(Drawable)}.
    191      *
    192      * @param context Context used for contained views.
    193      * @param attrs Attributes from inflating parent views used to style the popup.
    194      * @param defStyleAttr Style attribute to read for default styling of popup content.
    195      * @param defStyleRes Style resource ID to use for default styling of popup content.
    196      */
    197     public ListPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    198         mContext = context;
    199         mPopup = new PopupWindow(context, attrs, defStyleAttr, defStyleRes);
    200         mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
    201         // Set the default layout direction to match the default locale one
    202         final Locale locale = mContext.getResources().getConfiguration().locale;
    203         mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(locale);
    204     }
    205 
    206     /**
    207      * Sets the adapter that provides the data and the views to represent the data
    208      * in this popup window.
    209      *
    210      * @param adapter The adapter to use to create this window's content.
    211      */
    212     public void setAdapter(ListAdapter adapter) {
    213         if (mObserver == null) {
    214             mObserver = new PopupDataSetObserver();
    215         } else if (mAdapter != null) {
    216             mAdapter.unregisterDataSetObserver(mObserver);
    217         }
    218         mAdapter = adapter;
    219         if (mAdapter != null) {
    220             adapter.registerDataSetObserver(mObserver);
    221         }
    222 
    223         if (mDropDownList != null) {
    224             mDropDownList.setAdapter(mAdapter);
    225         }
    226     }
    227 
    228     /**
    229      * Set where the optional prompt view should appear. The default is
    230      * {@link #POSITION_PROMPT_ABOVE}.
    231      *
    232      * @param position A position constant declaring where the prompt should be displayed.
    233      *
    234      * @see #POSITION_PROMPT_ABOVE
    235      * @see #POSITION_PROMPT_BELOW
    236      */
    237     public void setPromptPosition(int position) {
    238         mPromptPosition = position;
    239     }
    240 
    241     /**
    242      * @return Where the optional prompt view should appear.
    243      *
    244      * @see #POSITION_PROMPT_ABOVE
    245      * @see #POSITION_PROMPT_BELOW
    246      */
    247     public int getPromptPosition() {
    248         return mPromptPosition;
    249     }
    250 
    251     /**
    252      * Set whether this window should be modal when shown.
    253      *
    254      * <p>If a popup window is modal, it will receive all touch and key input.
    255      * If the user touches outside the popup window's content area the popup window
    256      * will be dismissed.
    257      *
    258      * @param modal {@code true} if the popup window should be modal, {@code false} otherwise.
    259      */
    260     public void setModal(boolean modal) {
    261         mModal = true;
    262         mPopup.setFocusable(modal);
    263     }
    264 
    265     /**
    266      * Returns whether the popup window will be modal when shown.
    267      *
    268      * @return {@code true} if the popup window will be modal, {@code false} otherwise.
    269      */
    270     public boolean isModal() {
    271         return mModal;
    272     }
    273 
    274     /**
    275      * Forces outside touches to be ignored. Normally if {@link #isDropDownAlwaysVisible()} is
    276      * false, we allow outside touch to dismiss the dropdown. If this is set to true, then we
    277      * ignore outside touch even when the drop down is not set to always visible.
    278      *
    279      * @hide Used only by AutoCompleteTextView to handle some internal special cases.
    280      */
    281     public void setForceIgnoreOutsideTouch(boolean forceIgnoreOutsideTouch) {
    282         mForceIgnoreOutsideTouch = forceIgnoreOutsideTouch;
    283     }
    284 
    285     /**
    286      * Sets whether the drop-down should remain visible under certain conditions.
    287      *
    288      * The drop-down will occupy the entire screen below {@link #getAnchorView} regardless
    289      * of the size or content of the list.  {@link #getBackground()} will fill any space
    290      * that is not used by the list.
    291      *
    292      * @param dropDownAlwaysVisible Whether to keep the drop-down visible.
    293      *
    294      * @hide Only used by AutoCompleteTextView under special conditions.
    295      */
    296     public void setDropDownAlwaysVisible(boolean dropDownAlwaysVisible) {
    297         mDropDownAlwaysVisible = dropDownAlwaysVisible;
    298     }
    299 
    300     /**
    301      * @return Whether the drop-down is visible under special conditions.
    302      *
    303      * @hide Only used by AutoCompleteTextView under special conditions.
    304      */
    305     public boolean isDropDownAlwaysVisible() {
    306         return mDropDownAlwaysVisible;
    307     }
    308 
    309     /**
    310      * Sets the operating mode for the soft input area.
    311      *
    312      * @param mode The desired mode, see
    313      *        {@link android.view.WindowManager.LayoutParams#softInputMode}
    314      *        for the full list
    315      *
    316      * @see android.view.WindowManager.LayoutParams#softInputMode
    317      * @see #getSoftInputMode()
    318      */
    319     public void setSoftInputMode(int mode) {
    320         mPopup.setSoftInputMode(mode);
    321     }
    322 
    323     /**
    324      * Returns the current value in {@link #setSoftInputMode(int)}.
    325      *
    326      * @see #setSoftInputMode(int)
    327      * @see android.view.WindowManager.LayoutParams#softInputMode
    328      */
    329     public int getSoftInputMode() {
    330         return mPopup.getSoftInputMode();
    331     }
    332 
    333     /**
    334      * Sets a drawable to use as the list item selector.
    335      *
    336      * @param selector List selector drawable to use in the popup.
    337      */
    338     public void setListSelector(Drawable selector) {
    339         mDropDownListHighlight = selector;
    340     }
    341 
    342     /**
    343      * @return The background drawable for the popup window.
    344      */
    345     public Drawable getBackground() {
    346         return mPopup.getBackground();
    347     }
    348 
    349     /**
    350      * Sets a drawable to be the background for the popup window.
    351      *
    352      * @param d A drawable to set as the background.
    353      */
    354     public void setBackgroundDrawable(Drawable d) {
    355         mPopup.setBackgroundDrawable(d);
    356     }
    357 
    358     /**
    359      * Set an animation style to use when the popup window is shown or dismissed.
    360      *
    361      * @param animationStyle Animation style to use.
    362      */
    363     public void setAnimationStyle(int animationStyle) {
    364         mPopup.setAnimationStyle(animationStyle);
    365     }
    366 
    367     /**
    368      * Returns the animation style that will be used when the popup window is
    369      * shown or dismissed.
    370      *
    371      * @return Animation style that will be used.
    372      */
    373     public int getAnimationStyle() {
    374         return mPopup.getAnimationStyle();
    375     }
    376 
    377     /**
    378      * Returns the view that will be used to anchor this popup.
    379      *
    380      * @return The popup's anchor view
    381      */
    382     public View getAnchorView() {
    383         return mDropDownAnchorView;
    384     }
    385 
    386     /**
    387      * Sets the popup's anchor view. This popup will always be positioned relative to
    388      * the anchor view when shown.
    389      *
    390      * @param anchor The view to use as an anchor.
    391      */
    392     public void setAnchorView(View anchor) {
    393         mDropDownAnchorView = anchor;
    394     }
    395 
    396     /**
    397      * @return The horizontal offset of the popup from its anchor in pixels.
    398      */
    399     public int getHorizontalOffset() {
    400         return mDropDownHorizontalOffset;
    401     }
    402 
    403     /**
    404      * Set the horizontal offset of this popup from its anchor view in pixels.
    405      *
    406      * @param offset The horizontal offset of the popup from its anchor.
    407      */
    408     public void setHorizontalOffset(int offset) {
    409         mDropDownHorizontalOffset = offset;
    410     }
    411 
    412     /**
    413      * @return The vertical offset of the popup from its anchor in pixels.
    414      */
    415     public int getVerticalOffset() {
    416         if (!mDropDownVerticalOffsetSet) {
    417             return 0;
    418         }
    419         return mDropDownVerticalOffset;
    420     }
    421 
    422     /**
    423      * Set the vertical offset of this popup from its anchor view in pixels.
    424      *
    425      * @param offset The vertical offset of the popup from its anchor.
    426      */
    427     public void setVerticalOffset(int offset) {
    428         mDropDownVerticalOffset = offset;
    429         mDropDownVerticalOffsetSet = true;
    430     }
    431 
    432     /**
    433      * @return The width of the popup window in pixels.
    434      */
    435     public int getWidth() {
    436         return mDropDownWidth;
    437     }
    438 
    439     /**
    440      * Sets the width of the popup window in pixels. Can also be {@link #MATCH_PARENT}
    441      * or {@link #WRAP_CONTENT}.
    442      *
    443      * @param width Width of the popup window.
    444      */
    445     public void setWidth(int width) {
    446         mDropDownWidth = width;
    447     }
    448 
    449     /**
    450      * Sets the width of the popup window by the size of its content. The final width may be
    451      * larger to accommodate styled window dressing.
    452      *
    453      * @param width Desired width of content in pixels.
    454      */
    455     public void setContentWidth(int width) {
    456         Drawable popupBackground = mPopup.getBackground();
    457         if (popupBackground != null) {
    458             popupBackground.getPadding(mTempRect);
    459             mDropDownWidth = mTempRect.left + mTempRect.right + width;
    460         } else {
    461             setWidth(width);
    462         }
    463     }
    464 
    465     /**
    466      * @return The height of the popup window in pixels.
    467      */
    468     public int getHeight() {
    469         return mDropDownHeight;
    470     }
    471 
    472     /**
    473      * Sets the height of the popup window in pixels. Can also be {@link #MATCH_PARENT}.
    474      *
    475      * @param height Height of the popup window.
    476      */
    477     public void setHeight(int height) {
    478         mDropDownHeight = height;
    479     }
    480 
    481     /**
    482      * Sets a listener to receive events when a list item is clicked.
    483      *
    484      * @param clickListener Listener to register
    485      *
    486      * @see ListView#setOnItemClickListener(android.widget.AdapterView.OnItemClickListener)
    487      */
    488     public void setOnItemClickListener(AdapterView.OnItemClickListener clickListener) {
    489         mItemClickListener = clickListener;
    490     }
    491 
    492     /**
    493      * Sets a listener to receive events when a list item is selected.
    494      *
    495      * @param selectedListener Listener to register.
    496      *
    497      * @see ListView#setOnItemSelectedListener(android.widget.AdapterView.OnItemSelectedListener)
    498      */
    499     public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener selectedListener) {
    500         mItemSelectedListener = selectedListener;
    501     }
    502 
    503     /**
    504      * Set a view to act as a user prompt for this popup window. Where the prompt view will appear
    505      * is controlled by {@link #setPromptPosition(int)}.
    506      *
    507      * @param prompt View to use as an informational prompt.
    508      */
    509     public void setPromptView(View prompt) {
    510         boolean showing = isShowing();
    511         if (showing) {
    512             removePromptView();
    513         }
    514         mPromptView = prompt;
    515         if (showing) {
    516             show();
    517         }
    518     }
    519 
    520     /**
    521      * Post a {@link #show()} call to the UI thread.
    522      */
    523     public void postShow() {
    524         mHandler.post(mShowDropDownRunnable);
    525     }
    526 
    527     /**
    528      * Show the popup list. If the list is already showing, this method
    529      * will recalculate the popup's size and position.
    530      */
    531     public void show() {
    532         int height = buildDropDown();
    533 
    534         int widthSpec = 0;
    535         int heightSpec = 0;
    536 
    537         boolean noInputMethod = isInputMethodNotNeeded();
    538         mPopup.setAllowScrollingAnchorParent(!noInputMethod);
    539 
    540         if (mPopup.isShowing()) {
    541             if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
    542                 // The call to PopupWindow's update method below can accept -1 for any
    543                 // value you do not want to update.
    544                 widthSpec = -1;
    545             } else if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
    546                 widthSpec = getAnchorView().getWidth();
    547             } else {
    548                 widthSpec = mDropDownWidth;
    549             }
    550 
    551             if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
    552                 // The call to PopupWindow's update method below can accept -1 for any
    553                 // value you do not want to update.
    554                 heightSpec = noInputMethod ? height : ViewGroup.LayoutParams.MATCH_PARENT;
    555                 if (noInputMethod) {
    556                     mPopup.setWindowLayoutMode(
    557                             mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
    558                                     ViewGroup.LayoutParams.MATCH_PARENT : 0, 0);
    559                 } else {
    560                     mPopup.setWindowLayoutMode(
    561                             mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
    562                                     ViewGroup.LayoutParams.MATCH_PARENT : 0,
    563                             ViewGroup.LayoutParams.MATCH_PARENT);
    564                 }
    565             } else if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
    566                 heightSpec = height;
    567             } else {
    568                 heightSpec = mDropDownHeight;
    569             }
    570 
    571             mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible);
    572 
    573             mPopup.update(getAnchorView(), mDropDownHorizontalOffset,
    574                     mDropDownVerticalOffset, widthSpec, heightSpec);
    575         } else {
    576             if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
    577                 widthSpec = ViewGroup.LayoutParams.MATCH_PARENT;
    578             } else {
    579                 if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
    580                     mPopup.setWidth(getAnchorView().getWidth());
    581                 } else {
    582                     mPopup.setWidth(mDropDownWidth);
    583                 }
    584             }
    585 
    586             if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
    587                 heightSpec = ViewGroup.LayoutParams.MATCH_PARENT;
    588             } else {
    589                 if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
    590                     mPopup.setHeight(height);
    591                 } else {
    592                     mPopup.setHeight(mDropDownHeight);
    593                 }
    594             }
    595 
    596             mPopup.setWindowLayoutMode(widthSpec, heightSpec);
    597             mPopup.setClipToScreenEnabled(true);
    598 
    599             // use outside touchable to dismiss drop down when touching outside of it, so
    600             // only set this if the dropdown is not always visible
    601             mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible);
    602             mPopup.setTouchInterceptor(mTouchInterceptor);
    603             mPopup.showAsDropDown(getAnchorView(),
    604                     mDropDownHorizontalOffset, mDropDownVerticalOffset);
    605             mDropDownList.setSelection(ListView.INVALID_POSITION);
    606 
    607             if (!mModal || mDropDownList.isInTouchMode()) {
    608                 clearListSelection();
    609             }
    610             if (!mModal) {
    611                 mHandler.post(mHideSelector);
    612             }
    613         }
    614     }
    615 
    616     /**
    617      * Dismiss the popup window.
    618      */
    619     public void dismiss() {
    620         mPopup.dismiss();
    621         removePromptView();
    622         mPopup.setContentView(null);
    623         mDropDownList = null;
    624         mHandler.removeCallbacks(mResizePopupRunnable);
    625     }
    626 
    627     /**
    628      * Set a listener to receive a callback when the popup is dismissed.
    629      *
    630      * @param listener Listener that will be notified when the popup is dismissed.
    631      */
    632     public void setOnDismissListener(PopupWindow.OnDismissListener listener) {
    633         mPopup.setOnDismissListener(listener);
    634     }
    635 
    636     private void removePromptView() {
    637         if (mPromptView != null) {
    638             final ViewParent parent = mPromptView.getParent();
    639             if (parent instanceof ViewGroup) {
    640                 final ViewGroup group = (ViewGroup) parent;
    641                 group.removeView(mPromptView);
    642             }
    643         }
    644     }
    645 
    646     /**
    647      * Control how the popup operates with an input method: one of
    648      * {@link #INPUT_METHOD_FROM_FOCUSABLE}, {@link #INPUT_METHOD_NEEDED},
    649      * or {@link #INPUT_METHOD_NOT_NEEDED}.
    650      *
    651      * <p>If the popup is showing, calling this method will take effect only
    652      * the next time the popup is shown or through a manual call to the {@link #show()}
    653      * method.</p>
    654      *
    655      * @see #getInputMethodMode()
    656      * @see #show()
    657      */
    658     public void setInputMethodMode(int mode) {
    659         mPopup.setInputMethodMode(mode);
    660     }
    661 
    662     /**
    663      * Return the current value in {@link #setInputMethodMode(int)}.
    664      *
    665      * @see #setInputMethodMode(int)
    666      */
    667     public int getInputMethodMode() {
    668         return mPopup.getInputMethodMode();
    669     }
    670 
    671     /**
    672      * Set the selected position of the list.
    673      * Only valid when {@link #isShowing()} == {@code true}.
    674      *
    675      * @param position List position to set as selected.
    676      */
    677     public void setSelection(int position) {
    678         DropDownListView list = mDropDownList;
    679         if (isShowing() && list != null) {
    680             list.mListSelectionHidden = false;
    681             list.setSelection(position);
    682             if (list.getChoiceMode() != ListView.CHOICE_MODE_NONE) {
    683                 list.setItemChecked(position, true);
    684             }
    685         }
    686     }
    687 
    688     /**
    689      * Clear any current list selection.
    690      * Only valid when {@link #isShowing()} == {@code true}.
    691      */
    692     public void clearListSelection() {
    693         final DropDownListView list = mDropDownList;
    694         if (list != null) {
    695             // WARNING: Please read the comment where mListSelectionHidden is declared
    696             list.mListSelectionHidden = true;
    697             list.hideSelector();
    698             list.requestLayout();
    699         }
    700     }
    701 
    702     /**
    703      * @return {@code true} if the popup is currently showing, {@code false} otherwise.
    704      */
    705     public boolean isShowing() {
    706         return mPopup.isShowing();
    707     }
    708 
    709     /**
    710      * @return {@code true} if this popup is configured to assume the user does not need
    711      * to interact with the IME while it is showing, {@code false} otherwise.
    712      */
    713     public boolean isInputMethodNotNeeded() {
    714         return mPopup.getInputMethodMode() == INPUT_METHOD_NOT_NEEDED;
    715     }
    716 
    717     /**
    718      * Perform an item click operation on the specified list adapter position.
    719      *
    720      * @param position Adapter position for performing the click
    721      * @return true if the click action could be performed, false if not.
    722      *         (e.g. if the popup was not showing, this method would return false.)
    723      */
    724     public boolean performItemClick(int position) {
    725         if (isShowing()) {
    726             if (mItemClickListener != null) {
    727                 final DropDownListView list = mDropDownList;
    728                 final View child = list.getChildAt(position - list.getFirstVisiblePosition());
    729                 final ListAdapter adapter = list.getAdapter();
    730                 mItemClickListener.onItemClick(list, child, position, adapter.getItemId(position));
    731             }
    732             return true;
    733         }
    734         return false;
    735     }
    736 
    737     /**
    738      * @return The currently selected item or null if the popup is not showing.
    739      */
    740     public Object getSelectedItem() {
    741         if (!isShowing()) {
    742             return null;
    743         }
    744         return mDropDownList.getSelectedItem();
    745     }
    746 
    747     /**
    748      * @return The position of the currently selected item or {@link ListView#INVALID_POSITION}
    749      * if {@link #isShowing()} == {@code false}.
    750      *
    751      * @see ListView#getSelectedItemPosition()
    752      */
    753     public int getSelectedItemPosition() {
    754         if (!isShowing()) {
    755             return ListView.INVALID_POSITION;
    756         }
    757         return mDropDownList.getSelectedItemPosition();
    758     }
    759 
    760     /**
    761      * @return The ID of the currently selected item or {@link ListView#INVALID_ROW_ID}
    762      * if {@link #isShowing()} == {@code false}.
    763      *
    764      * @see ListView#getSelectedItemId()
    765      */
    766     public long getSelectedItemId() {
    767         if (!isShowing()) {
    768             return ListView.INVALID_ROW_ID;
    769         }
    770         return mDropDownList.getSelectedItemId();
    771     }
    772 
    773     /**
    774      * @return The View for the currently selected item or null if
    775      * {@link #isShowing()} == {@code false}.
    776      *
    777      * @see ListView#getSelectedView()
    778      */
    779     public View getSelectedView() {
    780         if (!isShowing()) {
    781             return null;
    782         }
    783         return mDropDownList.getSelectedView();
    784     }
    785 
    786     /**
    787      * @return The {@link ListView} displayed within the popup window.
    788      * Only valid when {@link #isShowing()} == {@code true}.
    789      */
    790     public ListView getListView() {
    791         return mDropDownList;
    792     }
    793 
    794     /**
    795      * The maximum number of list items that can be visible and still have
    796      * the list expand when touched.
    797      *
    798      * @param max Max number of items that can be visible and still allow the list to expand.
    799      */
    800     void setListItemExpandMax(int max) {
    801         mListItemExpandMaximum = max;
    802     }
    803 
    804     /**
    805      * Filter key down events. By forwarding key down events to this function,
    806      * views using non-modal ListPopupWindow can have it handle key selection of items.
    807      *
    808      * @param keyCode keyCode param passed to the host view's onKeyDown
    809      * @param event event param passed to the host view's onKeyDown
    810      * @return true if the event was handled, false if it was ignored.
    811      *
    812      * @see #setModal(boolean)
    813      */
    814     public boolean onKeyDown(int keyCode, KeyEvent event) {
    815         // when the drop down is shown, we drive it directly
    816         if (isShowing()) {
    817             // the key events are forwarded to the list in the drop down view
    818             // note that ListView handles space but we don't want that to happen
    819             // also if selection is not currently in the drop down, then don't
    820             // let center or enter presses go there since that would cause it
    821             // to select one of its items
    822             if (keyCode != KeyEvent.KEYCODE_SPACE
    823                     && (mDropDownList.getSelectedItemPosition() >= 0
    824                             || (keyCode != KeyEvent.KEYCODE_ENTER
    825                                     && keyCode != KeyEvent.KEYCODE_DPAD_CENTER))) {
    826                 int curIndex = mDropDownList.getSelectedItemPosition();
    827                 boolean consumed;
    828 
    829                 final boolean below = !mPopup.isAboveAnchor();
    830 
    831                 final ListAdapter adapter = mAdapter;
    832 
    833                 boolean allEnabled;
    834                 int firstItem = Integer.MAX_VALUE;
    835                 int lastItem = Integer.MIN_VALUE;
    836 
    837                 if (adapter != null) {
    838                     allEnabled = adapter.areAllItemsEnabled();
    839                     firstItem = allEnabled ? 0 :
    840                             mDropDownList.lookForSelectablePosition(0, true);
    841                     lastItem = allEnabled ? adapter.getCount() - 1 :
    842                             mDropDownList.lookForSelectablePosition(adapter.getCount() - 1, false);
    843                 }
    844 
    845                 if ((below && keyCode == KeyEvent.KEYCODE_DPAD_UP && curIndex <= firstItem) ||
    846                         (!below && keyCode == KeyEvent.KEYCODE_DPAD_DOWN && curIndex >= lastItem)) {
    847                     // When the selection is at the top, we block the key
    848                     // event to prevent focus from moving.
    849                     clearListSelection();
    850                     mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
    851                     show();
    852                     return true;
    853                 } else {
    854                     // WARNING: Please read the comment where mListSelectionHidden
    855                     //          is declared
    856                     mDropDownList.mListSelectionHidden = false;
    857                 }
    858 
    859                 consumed = mDropDownList.onKeyDown(keyCode, event);
    860                 if (DEBUG) Log.v(TAG, "Key down: code=" + keyCode + " list consumed=" + consumed);
    861 
    862                 if (consumed) {
    863                     // If it handled the key event, then the user is
    864                     // navigating in the list, so we should put it in front.
    865                     mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
    866                     // Here's a little trick we need to do to make sure that
    867                     // the list view is actually showing its focus indicator,
    868                     // by ensuring it has focus and getting its window out
    869                     // of touch mode.
    870                     mDropDownList.requestFocusFromTouch();
    871                     show();
    872 
    873                     switch (keyCode) {
    874                         // avoid passing the focus from the text view to the
    875                         // next component
    876                         case KeyEvent.KEYCODE_ENTER:
    877                         case KeyEvent.KEYCODE_DPAD_CENTER:
    878                         case KeyEvent.KEYCODE_DPAD_DOWN:
    879                         case KeyEvent.KEYCODE_DPAD_UP:
    880                             return true;
    881                     }
    882                 } else {
    883                     if (below && keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
    884                         // when the selection is at the bottom, we block the
    885                         // event to avoid going to the next focusable widget
    886                         if (curIndex == lastItem) {
    887                             return true;
    888                         }
    889                     } else if (!below && keyCode == KeyEvent.KEYCODE_DPAD_UP &&
    890                             curIndex == firstItem) {
    891                         return true;
    892                     }
    893                 }
    894             }
    895         }
    896 
    897         return false;
    898     }
    899 
    900     /**
    901      * Filter key down events. By forwarding key up events to this function,
    902      * views using non-modal ListPopupWindow can have it handle key selection of items.
    903      *
    904      * @param keyCode keyCode param passed to the host view's onKeyUp
    905      * @param event event param passed to the host view's onKeyUp
    906      * @return true if the event was handled, false if it was ignored.
    907      *
    908      * @see #setModal(boolean)
    909      */
    910     public boolean onKeyUp(int keyCode, KeyEvent event) {
    911         if (isShowing() && mDropDownList.getSelectedItemPosition() >= 0) {
    912             boolean consumed = mDropDownList.onKeyUp(keyCode, event);
    913             if (consumed) {
    914                 switch (keyCode) {
    915                     // if the list accepts the key events and the key event
    916                     // was a click, the text view gets the selected item
    917                     // from the drop down as its content
    918                     case KeyEvent.KEYCODE_ENTER:
    919                     case KeyEvent.KEYCODE_DPAD_CENTER:
    920                         dismiss();
    921                         break;
    922                 }
    923             }
    924             return consumed;
    925         }
    926         return false;
    927     }
    928 
    929     /**
    930      * Filter pre-IME key events. By forwarding {@link View#onKeyPreIme(int, KeyEvent)}
    931      * events to this function, views using ListPopupWindow can have it dismiss the popup
    932      * when the back key is pressed.
    933      *
    934      * @param keyCode keyCode param passed to the host view's onKeyPreIme
    935      * @param event event param passed to the host view's onKeyPreIme
    936      * @return true if the event was handled, false if it was ignored.
    937      *
    938      * @see #setModal(boolean)
    939      */
    940     public boolean onKeyPreIme(int keyCode, KeyEvent event) {
    941         if (keyCode == KeyEvent.KEYCODE_BACK && isShowing()) {
    942             // special case for the back key, we do not even try to send it
    943             // to the drop down list but instead, consume it immediately
    944             final View anchorView = mDropDownAnchorView;
    945             if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
    946                 KeyEvent.DispatcherState state = anchorView.getKeyDispatcherState();
    947                 if (state != null) {
    948                     state.startTracking(event, this);
    949                 }
    950                 return true;
    951             } else if (event.getAction() == KeyEvent.ACTION_UP) {
    952                 KeyEvent.DispatcherState state = anchorView.getKeyDispatcherState();
    953                 if (state != null) {
    954                     state.handleUpEvent(event);
    955                 }
    956                 if (event.isTracking() && !event.isCanceled()) {
    957                     dismiss();
    958                     return true;
    959                 }
    960             }
    961         }
    962         return false;
    963     }
    964 
    965     /**
    966      * <p>Builds the popup window's content and returns the height the popup
    967      * should have. Returns -1 when the content already exists.</p>
    968      *
    969      * @return the content's height or -1 if content already exists
    970      */
    971     private int buildDropDown() {
    972         ViewGroup dropDownView;
    973         int otherHeights = 0;
    974 
    975         if (mDropDownList == null) {
    976             Context context = mContext;
    977 
    978             /**
    979              * This Runnable exists for the sole purpose of checking if the view layout has got
    980              * completed and if so call showDropDown to display the drop down. This is used to show
    981              * the drop down as soon as possible after user opens up the search dialog, without
    982              * waiting for the normal UI pipeline to do it's job which is slower than this method.
    983              */
    984             mShowDropDownRunnable = new Runnable() {
    985                 public void run() {
    986                     // View layout should be all done before displaying the drop down.
    987                     View view = getAnchorView();
    988                     if (view != null && view.getWindowToken() != null) {
    989                         show();
    990                     }
    991                 }
    992             };
    993 
    994             mDropDownList = new DropDownListView(context, !mModal);
    995             if (mDropDownListHighlight != null) {
    996                 mDropDownList.setSelector(mDropDownListHighlight);
    997             }
    998             mDropDownList.setAdapter(mAdapter);
    999             mDropDownList.setOnItemClickListener(mItemClickListener);
   1000             mDropDownList.setFocusable(true);
   1001             mDropDownList.setFocusableInTouchMode(true);
   1002             mDropDownList.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
   1003                 public void onItemSelected(AdapterView<?> parent, View view,
   1004                         int position, long id) {
   1005 
   1006                     if (position != -1) {
   1007                         DropDownListView dropDownList = mDropDownList;
   1008 
   1009                         if (dropDownList != null) {
   1010                             dropDownList.mListSelectionHidden = false;
   1011                         }
   1012                     }
   1013                 }
   1014 
   1015                 public void onNothingSelected(AdapterView<?> parent) {
   1016                 }
   1017             });
   1018             mDropDownList.setOnScrollListener(mScrollListener);
   1019 
   1020             if (mItemSelectedListener != null) {
   1021                 mDropDownList.setOnItemSelectedListener(mItemSelectedListener);
   1022             }
   1023 
   1024             dropDownView = mDropDownList;
   1025 
   1026             View hintView = mPromptView;
   1027             if (hintView != null) {
   1028                 // if a hint has been specified, we accomodate more space for it and
   1029                 // add a text view in the drop down menu, at the bottom of the list
   1030                 LinearLayout hintContainer = new LinearLayout(context);
   1031                 hintContainer.setOrientation(LinearLayout.VERTICAL);
   1032 
   1033                 LinearLayout.LayoutParams hintParams = new LinearLayout.LayoutParams(
   1034                         ViewGroup.LayoutParams.MATCH_PARENT, 0, 1.0f
   1035                 );
   1036 
   1037                 switch (mPromptPosition) {
   1038                 case POSITION_PROMPT_BELOW:
   1039                     hintContainer.addView(dropDownView, hintParams);
   1040                     hintContainer.addView(hintView);
   1041                     break;
   1042 
   1043                 case POSITION_PROMPT_ABOVE:
   1044                     hintContainer.addView(hintView);
   1045                     hintContainer.addView(dropDownView, hintParams);
   1046                     break;
   1047 
   1048                 default:
   1049                     Log.e(TAG, "Invalid hint position " + mPromptPosition);
   1050                     break;
   1051                 }
   1052 
   1053                 // measure the hint's height to find how much more vertical space
   1054                 // we need to add to the drop down's height
   1055                 int widthSpec = MeasureSpec.makeMeasureSpec(mDropDownWidth, MeasureSpec.AT_MOST);
   1056                 int heightSpec = MeasureSpec.UNSPECIFIED;
   1057                 hintView.measure(widthSpec, heightSpec);
   1058 
   1059                 hintParams = (LinearLayout.LayoutParams) hintView.getLayoutParams();
   1060                 otherHeights = hintView.getMeasuredHeight() + hintParams.topMargin
   1061                         + hintParams.bottomMargin;
   1062 
   1063                 dropDownView = hintContainer;
   1064             }
   1065 
   1066             mPopup.setContentView(dropDownView);
   1067         } else {
   1068             dropDownView = (ViewGroup) mPopup.getContentView();
   1069             final View view = mPromptView;
   1070             if (view != null) {
   1071                 LinearLayout.LayoutParams hintParams =
   1072                         (LinearLayout.LayoutParams) view.getLayoutParams();
   1073                 otherHeights = view.getMeasuredHeight() + hintParams.topMargin
   1074                         + hintParams.bottomMargin;
   1075             }
   1076         }
   1077 
   1078         // getMaxAvailableHeight() subtracts the padding, so we put it back
   1079         // to get the available height for the whole window
   1080         int padding = 0;
   1081         Drawable background = mPopup.getBackground();
   1082         if (background != null) {
   1083             background.getPadding(mTempRect);
   1084             padding = mTempRect.top + mTempRect.bottom;
   1085 
   1086             // If we don't have an explicit vertical offset, determine one from the window
   1087             // background so that content will line up.
   1088             if (!mDropDownVerticalOffsetSet) {
   1089                 mDropDownVerticalOffset = -mTempRect.top;
   1090             }
   1091         } else {
   1092             mTempRect.setEmpty();
   1093         }
   1094 
   1095         // Max height available on the screen for a popup.
   1096         boolean ignoreBottomDecorations =
   1097                 mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED;
   1098         final int maxHeight = mPopup.getMaxAvailableHeight(
   1099                 getAnchorView(), mDropDownVerticalOffset, ignoreBottomDecorations);
   1100 
   1101         if (mDropDownAlwaysVisible || mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
   1102             return maxHeight + padding;
   1103         }
   1104 
   1105         final int childWidthSpec;
   1106         switch (mDropDownWidth) {
   1107             case ViewGroup.LayoutParams.WRAP_CONTENT:
   1108                 childWidthSpec = MeasureSpec.makeMeasureSpec(
   1109                         mContext.getResources().getDisplayMetrics().widthPixels -
   1110                         (mTempRect.left + mTempRect.right),
   1111                         MeasureSpec.AT_MOST);
   1112                 break;
   1113             case ViewGroup.LayoutParams.MATCH_PARENT:
   1114                 childWidthSpec = MeasureSpec.makeMeasureSpec(
   1115                         mContext.getResources().getDisplayMetrics().widthPixels -
   1116                         (mTempRect.left + mTempRect.right),
   1117                         MeasureSpec.EXACTLY);
   1118                 break;
   1119             default:
   1120                 childWidthSpec = MeasureSpec.makeMeasureSpec(mDropDownWidth, MeasureSpec.EXACTLY);
   1121                 break;
   1122         }
   1123         final int listContent = mDropDownList.measureHeightOfChildren(childWidthSpec,
   1124                 0, ListView.NO_POSITION, maxHeight - otherHeights, -1);
   1125         // add padding only if the list has items in it, that way we don't show
   1126         // the popup if it is not needed
   1127         if (listContent > 0) otherHeights += padding;
   1128 
   1129         return listContent + otherHeights;
   1130     }
   1131 
   1132     /**
   1133      * <p>Wrapper class for a ListView. This wrapper can hijack the focus to
   1134      * make sure the list uses the appropriate drawables and states when
   1135      * displayed on screen within a drop down. The focus is never actually
   1136      * passed to the drop down in this mode; the list only looks focused.</p>
   1137      */
   1138     private static class DropDownListView extends ListView {
   1139         private static final String TAG = ListPopupWindow.TAG + ".DropDownListView";
   1140         /*
   1141          * WARNING: This is a workaround for a touch mode issue.
   1142          *
   1143          * Touch mode is propagated lazily to windows. This causes problems in
   1144          * the following scenario:
   1145          * - Type something in the AutoCompleteTextView and get some results
   1146          * - Move down with the d-pad to select an item in the list
   1147          * - Move up with the d-pad until the selection disappears
   1148          * - Type more text in the AutoCompleteTextView *using the soft keyboard*
   1149          *   and get new results; you are now in touch mode
   1150          * - The selection comes back on the first item in the list, even though
   1151          *   the list is supposed to be in touch mode
   1152          *
   1153          * Using the soft keyboard triggers the touch mode change but that change
   1154          * is propagated to our window only after the first list layout, therefore
   1155          * after the list attempts to resurrect the selection.
   1156          *
   1157          * The trick to work around this issue is to pretend the list is in touch
   1158          * mode when we know that the selection should not appear, that is when
   1159          * we know the user moved the selection away from the list.
   1160          *
   1161          * This boolean is set to true whenever we explicitly hide the list's
   1162          * selection and reset to false whenever we know the user moved the
   1163          * selection back to the list.
   1164          *
   1165          * When this boolean is true, isInTouchMode() returns true, otherwise it
   1166          * returns super.isInTouchMode().
   1167          */
   1168         private boolean mListSelectionHidden;
   1169 
   1170         /**
   1171          * True if this wrapper should fake focus.
   1172          */
   1173         private boolean mHijackFocus;
   1174 
   1175         /**
   1176          * <p>Creates a new list view wrapper.</p>
   1177          *
   1178          * @param context this view's context
   1179          */
   1180         public DropDownListView(Context context, boolean hijackFocus) {
   1181             super(context, null, com.android.internal.R.attr.dropDownListViewStyle);
   1182             mHijackFocus = hijackFocus;
   1183             // TODO: Add an API to control this
   1184             setCacheColorHint(0); // Transparent, since the background drawable could be anything.
   1185         }
   1186 
   1187         /**
   1188          * <p>Avoids jarring scrolling effect by ensuring that list elements
   1189          * made of a text view fit on a single line.</p>
   1190          *
   1191          * @param position the item index in the list to get a view for
   1192          * @return the view for the specified item
   1193          */
   1194         @Override
   1195         View obtainView(int position, boolean[] isScrap) {
   1196             View view = super.obtainView(position, isScrap);
   1197 
   1198             if (view instanceof TextView) {
   1199                 ((TextView) view).setHorizontallyScrolling(true);
   1200             }
   1201 
   1202             return view;
   1203         }
   1204 
   1205         @Override
   1206         public boolean isInTouchMode() {
   1207             // WARNING: Please read the comment where mListSelectionHidden is declared
   1208             return (mHijackFocus && mListSelectionHidden) || super.isInTouchMode();
   1209         }
   1210 
   1211         /**
   1212          * <p>Returns the focus state in the drop down.</p>
   1213          *
   1214          * @return true always if hijacking focus
   1215          */
   1216         @Override
   1217         public boolean hasWindowFocus() {
   1218             return mHijackFocus || super.hasWindowFocus();
   1219         }
   1220 
   1221         /**
   1222          * <p>Returns the focus state in the drop down.</p>
   1223          *
   1224          * @return true always if hijacking focus
   1225          */
   1226         @Override
   1227         public boolean isFocused() {
   1228             return mHijackFocus || super.isFocused();
   1229         }
   1230 
   1231         /**
   1232          * <p>Returns the focus state in the drop down.</p>
   1233          *
   1234          * @return true always if hijacking focus
   1235          */
   1236         @Override
   1237         public boolean hasFocus() {
   1238             return mHijackFocus || super.hasFocus();
   1239         }
   1240     }
   1241 
   1242     private class PopupDataSetObserver extends DataSetObserver {
   1243         @Override
   1244         public void onChanged() {
   1245             if (isShowing()) {
   1246                 // Resize the popup to fit new content
   1247                 show();
   1248             }
   1249         }
   1250 
   1251         @Override
   1252         public void onInvalidated() {
   1253             dismiss();
   1254         }
   1255     }
   1256 
   1257     private class ListSelectorHider implements Runnable {
   1258         public void run() {
   1259             clearListSelection();
   1260         }
   1261     }
   1262 
   1263     private class ResizePopupRunnable implements Runnable {
   1264         public void run() {
   1265             if (mDropDownList != null && mDropDownList.getCount() > mDropDownList.getChildCount() &&
   1266                     mDropDownList.getChildCount() <= mListItemExpandMaximum) {
   1267                 mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
   1268                 show();
   1269             }
   1270         }
   1271     }
   1272 
   1273     private class PopupTouchInterceptor implements OnTouchListener {
   1274         public boolean onTouch(View v, MotionEvent event) {
   1275             final int action = event.getAction();
   1276             final int x = (int) event.getX();
   1277             final int y = (int) event.getY();
   1278 
   1279             if (action == MotionEvent.ACTION_DOWN &&
   1280                     mPopup != null && mPopup.isShowing() &&
   1281                     (x >= 0 && x < mPopup.getWidth() && y >= 0 && y < mPopup.getHeight())) {
   1282                 mHandler.postDelayed(mResizePopupRunnable, EXPAND_LIST_TIMEOUT);
   1283             } else if (action == MotionEvent.ACTION_UP) {
   1284                 mHandler.removeCallbacks(mResizePopupRunnable);
   1285             }
   1286             return false;
   1287         }
   1288     }
   1289 
   1290     private class PopupScrollListener implements ListView.OnScrollListener {
   1291         public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
   1292                 int totalItemCount) {
   1293 
   1294         }
   1295 
   1296         public void onScrollStateChanged(AbsListView view, int scrollState) {
   1297             if (scrollState == SCROLL_STATE_TOUCH_SCROLL &&
   1298                     !isInputMethodNotNeeded() && mPopup.getContentView() != null) {
   1299                 mHandler.removeCallbacks(mResizePopupRunnable);
   1300                 mResizePopupRunnable.run();
   1301             }
   1302         }
   1303     }
   1304 }
   1305