FileDocCategorySizeDatePackage
AbsListView.javaAPI DocAndroid 1.5 API123877Wed May 06 22:41:56 BST 2009android.widget

AbsListView

public abstract class AbsListView extends AdapterView implements Filter.FilterListener, android.text.TextWatcher, ViewTreeObserver.OnTouchModeChangeListener, ViewTreeObserver.OnGlobalLayoutListener
Common code shared between ListView and GridView
attr
ref android.R.styleable#AbsListView_listSelector
attr
ref android.R.styleable#AbsListView_drawSelectorOnTop
attr
ref android.R.styleable#AbsListView_stackFromBottom
attr
ref android.R.styleable#AbsListView_scrollingCache
attr
ref android.R.styleable#AbsListView_textFilterEnabled
attr
ref android.R.styleable#AbsListView_transcriptMode
attr
ref android.R.styleable#AbsListView_cacheColorHint
attr
ref android.R.styleable#AbsListView_fastScrollEnabled
attr
ref android.R.styleable#AbsListView_smoothScrollbar

Fields Summary
public static final int
TRANSCRIPT_MODE_DISABLED
Disables the transcript mode.
public static final int
TRANSCRIPT_MODE_NORMAL
The list will automatically scroll to the bottom when a data set change notification is received and only if the last item is already visible on screen.
public static final int
TRANSCRIPT_MODE_ALWAYS_SCROLL
The list will automatically scroll to the bottom, no matter what items are currently visible.
static final int
TOUCH_MODE_REST
Indicates that we are not in the middle of a touch gesture
static final int
TOUCH_MODE_DOWN
Indicates we just received the touch event and we are waiting to see if the it is a tap or a scroll gesture.
static final int
TOUCH_MODE_TAP
Indicates the touch has been recognized as a tap and we are now waiting to see if the touch is a longpress
static final int
TOUCH_MODE_DONE_WAITING
Indicates we have waited for everything we can wait for, but the user's finger is still down
static final int
TOUCH_MODE_SCROLL
Indicates the touch gesture is a scroll
static final int
TOUCH_MODE_FLING
Indicates the view is in the process of being flung
static final int
TOUCH_MODE_FAST_SCROLL
Indicates that the user is currently dragging the fast scroll thumb
static final int
LAYOUT_NORMAL
Regular layout - usually an unsolicited layout from the view system
static final int
LAYOUT_FORCE_TOP
Show the first item
static final int
LAYOUT_SET_SELECTION
Force the selected item to be on somewhere on the screen
static final int
LAYOUT_FORCE_BOTTOM
Show the last item
static final int
LAYOUT_SPECIFIC
Make a mSelectedItem appear in a specific location and build the rest of the views from there. The top is specified by mSpecificTop.
static final int
LAYOUT_SYNC
Layout to sync as a result of a data change. Restore mSyncPosition to have its top at mSpecificTop
static final int
LAYOUT_MOVE_SELECTION
Layout as a result of using the navigation keys
int
mLayoutMode
Controls how the next layout will happen
AdapterDataSetObserver
mDataSetObserver
Should be used by subclasses to listen to changes in the dataset
ListAdapter
mAdapter
The adapter containing the data to be displayed by this view
boolean
mDrawSelectorOnTop
Indicates whether the list selector should be drawn on top of the children or behind
android.graphics.drawable.Drawable
mSelector
The drawable used to draw the selector
android.graphics.Rect
mSelectorRect
Defines the selector's location and dimension at drawing time
final RecycleBin
mRecycler
The data set used to store unused views that should be reused during the next layout to avoid creating new ones
int
mSelectionLeftPadding
The selection's left padding
int
mSelectionTopPadding
The selection's top padding
int
mSelectionRightPadding
The selection's right padding
int
mSelectionBottomPadding
The selection's bottom padding
android.graphics.Rect
mListPadding
This view's padding
int
mWidthMeasureSpec
Subclasses must retain their measure spec from onMeasure() into this member
android.view.View
mScrollUp
The top scroll indicator
android.view.View
mScrollDown
The down scroll indicator
boolean
mCachingStarted
When the view is scrolling, this flag is set to true to indicate subclasses that the drawing cache was enabled on the children
int
mMotionPosition
The position of the view that received the down motion event
int
mMotionViewOriginalTop
The offset to the top of the mMotionPosition view when the down motion event was received
int
mMotionViewNewTop
The desired offset to the top of the mMotionPosition view after a scroll
int
mMotionX
The X value associated with the the down motion event
int
mMotionY
The Y value associated with the the down motion event
int
mTouchMode
One of TOUCH_MODE_REST, TOUCH_MODE_DOWN, TOUCH_MODE_TAP, TOUCH_MODE_SCROLL, or TOUCH_MODE_DONE_WAITING
int
mLastY
Y value from on the previous motion event (if any)
int
mMotionCorrection
How far the finger moved before we started scrolling
private android.view.VelocityTracker
mVelocityTracker
Determines speed during touch scrolling
private FlingRunnable
mFlingRunnable
Handles one frame of a fling
int
mSelectedTop
The offset in pixels form the top of the AdapterView to the top of the currently selected view. Used to save and restore state.
boolean
mStackFromBottom
Indicates whether the list is stacked from the bottom edge or the top edge.
boolean
mScrollingCacheEnabled
When set to true, the list automatically discards the children's bitmap cache after scrolling.
boolean
mFastScrollEnabled
Whether or not to enable the fast scroll feature on this list
private OnScrollListener
mOnScrollListener
Optional callback to notify client when scroll position has changed
PopupWindow
mPopup
Keeps track of our accessory window
EditText
mTextFilter
Used with type filter window
private boolean
mSmoothScrollbarEnabled
Indicates whether to use pixels-based or position-based scrollbar properties.
private boolean
mTextFilterEnabled
Indicates that this view supports filtering
private boolean
mFiltered
Indicates that this view is currently displaying a filtered view of the data
private android.graphics.Rect
mTouchFrame
Rectangle used for hit testing children
int
mResurrectToPosition
The position to resurrect the selected position to.
private android.view.ContextMenu.ContextMenuInfo
mContextMenuInfo
private static final int
TOUCH_MODE_UNKNOWN
Used to request a layout when we changed touch mode
private static final int
TOUCH_MODE_ON
private static final int
TOUCH_MODE_OFF
private int
mLastTouchMode
private static final boolean
PROFILE_SCROLLING
private boolean
mScrollProfilingStarted
private static final boolean
PROFILE_FLINGING
private boolean
mFlingProfilingStarted
private CheckForLongPress
mPendingCheckForLongPress
The last CheckForLongPress runnable we posted, if any
private Runnable
mPendingCheckForTap
The last CheckForTap runnable we posted, if any
private CheckForKeyLongPress
mPendingCheckForKeyLongPress
The last CheckForKeyLongPress runnable we posted, if any
private PerformClick
mPerformClick
Acts upon click
private int
mTranscriptMode
This view is in transcript mode -- it shows the bottom of the list when the data changes
private int
mCacheColorHint
Indicates that this list is always drawn on top of a solid, single-color, opaque background
private boolean
mIsChildViewEnabled
The select child's view (from the adapter's getView) is enabled.
private int
mLastScrollState
The last scroll state reported to clients through {@link OnScrollListener}.
private FastScroller
mFastScroller
Helper object that renders and controls the fast scroll thumb.
private int
mTouchSlop
private float
mDensityScale
private android.view.inputmethod.InputConnection
mDefInputConnection
private android.view.inputmethod.InputConnectionWrapper
mPublicInputConnection
Constructors Summary
public AbsListView(android.content.Context context)


                                                                                           
             

                                                                                       
               
                 
    

       
        super(context);
        initAbsListView();

        setVerticalScrollBarEnabled(true);
        TypedArray a = context.obtainStyledAttributes(R.styleable.View);
        initializeScrollbars(a);
        a.recycle();
    
public AbsListView(android.content.Context context, android.util.AttributeSet attrs)

        this(context, attrs, com.android.internal.R.attr.absListViewStyle);
    
public AbsListView(android.content.Context context, android.util.AttributeSet attrs, int defStyle)

        super(context, attrs, defStyle);
        initAbsListView();

        TypedArray a = context.obtainStyledAttributes(attrs,
                com.android.internal.R.styleable.AbsListView, defStyle, 0);

        Drawable d = a.getDrawable(com.android.internal.R.styleable.AbsListView_listSelector);
        if (d != null) {
            setSelector(d);
        }

        mDrawSelectorOnTop = a.getBoolean(
                com.android.internal.R.styleable.AbsListView_drawSelectorOnTop, false);

        boolean stackFromBottom = a.getBoolean(R.styleable.AbsListView_stackFromBottom, false);
        setStackFromBottom(stackFromBottom);

        boolean scrollingCacheEnabled = a.getBoolean(R.styleable.AbsListView_scrollingCache, true);
        setScrollingCacheEnabled(scrollingCacheEnabled);

        boolean useTextFilter = a.getBoolean(R.styleable.AbsListView_textFilterEnabled, false);
        setTextFilterEnabled(useTextFilter);

        int transcriptMode = a.getInt(R.styleable.AbsListView_transcriptMode,
                TRANSCRIPT_MODE_DISABLED);
        setTranscriptMode(transcriptMode);

        int color = a.getColor(R.styleable.AbsListView_cacheColorHint, 0);
        setCacheColorHint(color);
        
        boolean enableFastScroll = a.getBoolean(R.styleable.AbsListView_fastScrollEnabled, false);
        setFastScrollEnabled(enableFastScroll);

        boolean smoothScrollbar = a.getBoolean(R.styleable.AbsListView_smoothScrollbar, true);
        setSmoothScrollbarEnabled(smoothScrollbar);
        
        a.recycle();
    
Methods Summary
private booleanacceptFilter()

        if (!mTextFilterEnabled || !(getAdapter() instanceof Filterable) ||
                ((Filterable) getAdapter()).getFilter() == null) {
            return false;
        }
        return true;
    
public voidaddTouchables(java.util.ArrayList views)
{@inheritDoc}

        final int count = getChildCount();
        final int firstPosition = mFirstPosition;
        final ListAdapter adapter = mAdapter;

        if (adapter == null) {
            return;
        }

        for (int i = 0; i < count; i++) {
            final View child = getChildAt(i);
            if (adapter.isEnabled(firstPosition + i)) {
                views.add(child);
            }
            child.addTouchables(views);
        }
    
public voidafterTextChanged(android.text.Editable s)
For our text watcher that is associated with the text filter. Does nothing.

    
public voidbeforeTextChanged(java.lang.CharSequence s, int start, int count, int after)
For our text watcher that is associated with the text filter. Does nothing.

    
public booleancheckInputConnectionProxy(android.view.View view)
For filtering we proxy an input connection to an internal text editor, and this allows the proxying to happen.

        return view == mTextFilter;
    
protected booleancheckLayoutParams(ViewGroup.LayoutParams p)

        return p instanceof AbsListView.LayoutParams;
    
private voidclearScrollingCache()

        if (mCachingStarted) {
            setChildrenDrawnWithCacheEnabled(false);
            if ((mPersistentDrawingCache & PERSISTENT_SCROLLING_CACHE) == 0) {
                setChildrenDrawingCacheEnabled(false);
            }
            if (!isAlwaysDrawnWithCacheEnabled()) {
                invalidate();
            }
            mCachingStarted = false;
        }
    
public voidclearTextFilter()
Clear the text filter.

        if (mFiltered) {
            mTextFilter.setText("");
            mFiltered = false;
            if (mPopup != null && mPopup.isShowing()) {
                dismissPopup();
            }
        }
    
protected intcomputeVerticalScrollExtent()

        final int count = getChildCount();
        if (count > 0) {
            if (mSmoothScrollbarEnabled) {
                int extent = count * 100;

                View view = getChildAt(0);
                final int top = view.getTop();
                int height = view.getHeight();
                if (height > 0) {
                    extent += (top * 100) / height;
                }

                view = getChildAt(count - 1);
                final int bottom = view.getBottom();
                height = view.getHeight();
                if (height > 0) {
                    extent -= ((bottom - getHeight()) * 100) / height;
                }

                return extent;
            } else {
                return 1;
            }
        }
        return 0;
    
protected intcomputeVerticalScrollOffset()

        final int firstPosition = mFirstPosition;
        final int childCount = getChildCount();
        if (firstPosition >= 0 && childCount > 0) {
            if (mSmoothScrollbarEnabled) {
                final View view = getChildAt(0);
                final int top = view.getTop();
                int height = view.getHeight();
                if (height > 0) {
                    return Math.max(firstPosition * 100 - (top * 100) / height, 0);
                }
            } else {
                int index;
                final int count = mItemCount;
                if (firstPosition == 0) {
                    index = 0;
                } else if (firstPosition + childCount == count) {
                    index = count;
                } else {
                    index = firstPosition + childCount / 2;
                }
                return (int) (firstPosition + childCount * (index / (float) count));
            }
        }
        return 0;
    
protected intcomputeVerticalScrollRange()

        return mSmoothScrollbarEnabled ? Math.max(mItemCount * 100, 0) : mItemCount;
    
android.view.ContextMenu.ContextMenuInfocreateContextMenuInfo(android.view.View view, int position, long id)
Creates the ContextMenuInfo returned from {@link #getContextMenuInfo()}. This methods knows the view, position and ID of the item that received the long press.

param
view The view that received the long press.
param
position The position of the item that received the long press.
param
id The ID of the item that received the long press.
return
The extra information that should be returned by {@link #getContextMenuInfo()}.

        return new AdapterContextMenuInfo(view, position, id);
    
private voidcreateScrollingCache()

        if (mScrollingCacheEnabled && !mCachingStarted) {
            setChildrenDrawnWithCacheEnabled(true);
            setChildrenDrawingCacheEnabled(true);
            mCachingStarted = true;
        }
    
private voidcreateTextFilter(boolean animateEntrance)
Creates the window for the text filter and populates it with an EditText field;

param
animateEntrance true if the window should appear with an animation

        if (mPopup == null) {
            Context c = getContext();
            PopupWindow p = new PopupWindow(c);
            LayoutInflater layoutInflater = (LayoutInflater)
                    c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            mTextFilter = (EditText) layoutInflater.inflate(
                    com.android.internal.R.layout.typing_filter, null);
            // For some reason setting this as the "real" input type changes
            // the text view in some way that it doesn't work, and I don't
            // want to figure out why this is.
            mTextFilter.setRawInputType(EditorInfo.TYPE_CLASS_TEXT
                    | EditorInfo.TYPE_TEXT_VARIATION_FILTER);
            mTextFilter.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
            mTextFilter.addTextChangedListener(this);
            p.setFocusable(false);
            p.setTouchable(false);
            p.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
            p.setContentView(mTextFilter);
            p.setWidth(LayoutParams.WRAP_CONTENT);
            p.setHeight(LayoutParams.WRAP_CONTENT);
            p.setBackgroundDrawable(null);
            mPopup = p;
            getViewTreeObserver().addOnGlobalLayoutListener(this);
        }
        if (animateEntrance) {
            mPopup.setAnimationStyle(com.android.internal.R.style.Animation_TypingFilter);
        } else {
            mPopup.setAnimationStyle(com.android.internal.R.style.Animation_TypingFilterRestore);
        }
    
voiddismissPopup()
Removes the filter window

        if (mPopup != null) {
            mPopup.dismiss();
        }
    
protected voiddispatchDraw(android.graphics.Canvas canvas)

        int saveCount = 0;
        final boolean clipToPadding = (mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
        if (clipToPadding) {
            saveCount = canvas.save();
            final int scrollX = mScrollX;
            final int scrollY = mScrollY;
            canvas.clipRect(scrollX + mPaddingLeft, scrollY + mPaddingTop,
                    scrollX + mRight - mLeft - mPaddingRight,
                    scrollY + mBottom - mTop - mPaddingBottom);
            mGroupFlags &= ~CLIP_TO_PADDING_MASK;
        }

        final boolean drawSelectorOnTop = mDrawSelectorOnTop;
        if (!drawSelectorOnTop) {
            drawSelector(canvas);
        }

        super.dispatchDraw(canvas);

        if (drawSelectorOnTop) {
            drawSelector(canvas);
        }

        if (clipToPadding) {
            canvas.restoreToCount(saveCount);
            mGroupFlags |= CLIP_TO_PADDING_MASK;
        }
    
protected voiddispatchSetPressed(boolean pressed)

        // Don't dispatch setPressed to our children. We call setPressed on ourselves to
        // get the selector in the right state, but we don't want to press each child.
    
public voiddraw(android.graphics.Canvas canvas)

        super.draw(canvas);
        if (mFastScroller != null) {
            mFastScroller.draw(canvas);
        }
    
private voiddrawSelector(android.graphics.Canvas canvas)

        if (shouldShowSelector() && mSelectorRect != null && !mSelectorRect.isEmpty()) {
            final Drawable selector = mSelector;
            selector.setBounds(mSelectorRect);
            selector.draw(canvas);
        }
    
protected voiddrawableStateChanged()

        super.drawableStateChanged();
        if (mSelector != null) {
            mSelector.setState(getDrawableState());
        }
    
abstract voidfillGap(boolean down)
Fills the gap left open by a touch-scroll. During a touch scroll, children that remain on screen are shifted and the other ones are discarded. The role of this method is to fill the gap thus created by performing a partial layout in the empty space.

param
down true if the scroll is going down, false if it is going up

abstract intfindMotionRow(int y)
Find the row closest to y. This row will be used as the motion row when scrolling

param
y Where the user touched
return
The position of the first (or only) item in the row closest to y

protected ViewGroup.LayoutParamsgenerateLayoutParams(ViewGroup.LayoutParams p)

        return new LayoutParams(p);
    
public android.widget.AbsListView$LayoutParamsgenerateLayoutParams(android.util.AttributeSet attrs)

        return new AbsListView.LayoutParams(getContext(), attrs);
    
protected floatgetBottomFadingEdgeStrength()

        final int count = getChildCount();
        final float fadeEdge = super.getBottomFadingEdgeStrength();
        if (count == 0) {
            return fadeEdge;
        } else {
            if (mFirstPosition + count - 1 < mItemCount - 1) {
                return 1.0f;
            }

            final int bottom = getChildAt(count - 1).getBottom();
            final int height = getHeight();
            final float fadeLength = (float) getVerticalFadingEdgeLength();
            return bottom > height - mPaddingBottom ?
                    (float) (bottom - height + mPaddingBottom) / fadeLength : fadeEdge;
        }
    
public intgetCacheColorHint()
When set to a non-zero value, the cache color hint indicates that this list is always drawn on top of a solid, single-color, opaque background

return
The cache color hint

        return mCacheColorHint;
    
protected android.view.ContextMenu.ContextMenuInfogetContextMenuInfo()

        return mContextMenuInfo;
    
static intgetDistance(android.graphics.Rect source, android.graphics.Rect dest, int direction)
What is the distance between the source and destination rectangles given the direction of focus navigation between them? The direction basically helps figure out more quickly what is self evident by the relationship between the rects...

param
source the source rectangle
param
dest the destination rectangle
param
direction the direction
return
the distance between the rectangles

        int sX, sY; // source x, y
        int dX, dY; // dest x, y
        switch (direction) {
        case View.FOCUS_RIGHT:
            sX = source.right;
            sY = source.top + source.height() / 2;
            dX = dest.left;
            dY = dest.top + dest.height() / 2;
            break;
        case View.FOCUS_DOWN:
            sX = source.left + source.width() / 2;
            sY = source.bottom;
            dX = dest.left + dest.width() / 2;
            dY = dest.top;
            break;
        case View.FOCUS_LEFT:
            sX = source.left;
            sY = source.top + source.height() / 2;
            dX = dest.right;
            dY = dest.top + dest.height() / 2;
            break;
        case View.FOCUS_UP:
            sX = source.left + source.width() / 2;
            sY = source.top;
            dX = dest.left + dest.width() / 2;
            dY = dest.bottom;
            break;
        default:
            throw new IllegalArgumentException("direction must be one of "
                    + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
        }
        int deltaX = dX - sX;
        int deltaY = dY - sY;
        return deltaY * deltaY + deltaX * deltaX;
    
public voidgetFocusedRect(android.graphics.Rect r)

        View view = getSelectedView();
        if (view != null) {
            // the focused rectangle of the selected view offset into the
            // coordinate space of this view.
            view.getFocusedRect(r);
            offsetDescendantRectToMyCoords(view, r);
        } else {
            // otherwise, just the norm
            super.getFocusedRect(r);
        }
    
intgetFooterViewsCount()
Returns the number of footer views in the list. Footer views are special views at the bottom of the list that should not be recycled during a layout.

return
The number of footer views, 0 in the default implementation.

        return 0;
    
intgetHeaderViewsCount()
Returns the number of header views in the list. Header views are special views at the top of the list that should not be recycled during a layout.

return
The number of header views, 0 in the default implementation.

        return 0;
    
public intgetListPaddingBottom()
List padding is the maximum of the normal view's padding and the padding of the selector.

see
android.view.View#getPaddingBottom()
see
#getSelector()
return
The bottom list padding.

        return mListPadding.bottom;
    
public intgetListPaddingLeft()
List padding is the maximum of the normal view's padding and the padding of the selector.

see
android.view.View#getPaddingLeft()
see
#getSelector()
return
The left list padding.

        return mListPadding.left;
    
public intgetListPaddingRight()
List padding is the maximum of the normal view's padding and the padding of the selector.

see
android.view.View#getPaddingRight()
see
#getSelector()
return
The right list padding.

        return mListPadding.right;
    
public intgetListPaddingTop()
List padding is the maximum of the normal view's padding and the padding of the selector.

see
android.view.View#getPaddingTop()
see
#getSelector()
return
The top list padding.

        return mListPadding.top;
    
public android.view.ViewgetSelectedView()

        if (mItemCount > 0 && mSelectedPosition >= 0) {
            return getChildAt(mSelectedPosition - mFirstPosition);
        } else {
            return null;
        }
    
public android.graphics.drawable.DrawablegetSelector()
Returns the selector {@link android.graphics.drawable.Drawable} that is used to draw the selection in the list.

return
the drawable used to display the selector

        return mSelector;
    
public intgetSolidColor()

        return mCacheColorHint;
    
public java.lang.CharSequencegetTextFilter()
Returns the list's text filter, if available.

return
the list's text filter or null if filtering isn't enabled

        if (mTextFilterEnabled && mTextFilter != null) {
            return mTextFilter.getText();
        }
        return null;
    
protected floatgetTopFadingEdgeStrength()

        final int count = getChildCount();
        final float fadeEdge = super.getTopFadingEdgeStrength();
        if (count == 0) {
            return fadeEdge;
        } else {
            if (mFirstPosition > 0) {
                return 1.0f;
            }

            final int top = getChildAt(0).getTop();
            final float fadeLength = (float) getVerticalFadingEdgeLength();
            return top < mPaddingTop ? (float) -(top - mPaddingTop) / fadeLength : fadeEdge;
        }
    
public intgetTranscriptMode()
Returns the current transcript mode.

return
{@link #TRANSCRIPT_MODE_DISABLED}, {@link #TRANSCRIPT_MODE_NORMAL} or {@link #TRANSCRIPT_MODE_ALWAYS_SCROLL}

        return mTranscriptMode;
    
protected voidhandleDataChanged()

        int count = mItemCount;
        if (count > 0) {

            int newPos;

            int selectablePos;

            // Find the row we are supposed to sync to
            if (mNeedSync) {
                // Update this first, since setNextSelectedPositionInt inspects it
                mNeedSync = false;

                if (mTranscriptMode == TRANSCRIPT_MODE_ALWAYS_SCROLL ||
                        (mTranscriptMode == TRANSCRIPT_MODE_NORMAL &&
                                mFirstPosition + getChildCount() >= mOldItemCount)) {
                    mLayoutMode = LAYOUT_FORCE_BOTTOM;
                    return;
                }

                switch (mSyncMode) {
                case SYNC_SELECTED_POSITION:
                    if (isInTouchMode()) {
                        // We saved our state when not in touch mode. (We know this because
                        // mSyncMode is SYNC_SELECTED_POSITION.) Now we are trying to
                        // restore in touch mode. Just leave mSyncPosition as it is (possibly
                        // adjusting if the available range changed) and return.
                        mLayoutMode = LAYOUT_SYNC;
                        mSyncPosition = Math.min(Math.max(0, mSyncPosition), count - 1);

                        return;
                    } else {
                        // See if we can find a position in the new data with the same
                        // id as the old selection. This will change mSyncPosition.
                        newPos = findSyncPosition();
                        if (newPos >= 0) {
                            // Found it. Now verify that new selection is still selectable
                            selectablePos = lookForSelectablePosition(newPos, true);
                            if (selectablePos == newPos) {
                                // Same row id is selected
                                mSyncPosition = newPos;

                                if (mSyncHeight == getHeight()) {
                                    // If we are at the same height as when we saved state, try
                                    // to restore the scroll position too.
                                    mLayoutMode = LAYOUT_SYNC;
                                } else {
                                    // We are not the same height as when the selection was saved, so
                                    // don't try to restore the exact position
                                    mLayoutMode = LAYOUT_SET_SELECTION;
                                }

                                // Restore selection
                                setNextSelectedPositionInt(newPos);
                                return;
                            }
                        }
                    }
                    break;
                case SYNC_FIRST_POSITION:
                    // Leave mSyncPosition as it is -- just pin to available range
                    mLayoutMode = LAYOUT_SYNC;
                    mSyncPosition = Math.min(Math.max(0, mSyncPosition), count - 1);

                    return;
                }
            }

            if (!isInTouchMode()) {
                // We couldn't find matching data -- try to use the same position
                newPos = getSelectedItemPosition();

                // Pin position to the available range
                if (newPos >= count) {
                    newPos = count - 1;
                }
                if (newPos < 0) {
                    newPos = 0;
                }

                // Make sure we select something selectable -- first look down
                selectablePos = lookForSelectablePosition(newPos, true);

                if (selectablePos >= 0) {
                    setNextSelectedPositionInt(selectablePos);
                    return;
                } else {
                    // Looking down didn't work -- try looking up
                    selectablePos = lookForSelectablePosition(newPos, false);
                    if (selectablePos >= 0) {
                        setNextSelectedPositionInt(selectablePos);
                        return;
                    }
                }
            } else {

                // We already know where we want to resurrect the selection
                if (mResurrectToPosition >= 0) {
                    return;
                }
            }

        }

        // Nothing is selected. Give up and reset everything.
        mLayoutMode = mStackFromBottom ? LAYOUT_FORCE_BOTTOM : LAYOUT_FORCE_TOP;
        mSelectedPosition = INVALID_POSITION;
        mSelectedRowId = INVALID_ROW_ID;
        mNextSelectedPosition = INVALID_POSITION;
        mNextSelectedRowId = INVALID_ROW_ID;
        mNeedSync = false;
        checkSelectionChanged();
    
public booleanhasTextFilter()
Returns if the ListView currently has a text filter.

        return mFiltered;
    
voidhideSelector()

        if (mSelectedPosition != INVALID_POSITION) {
            mResurrectToPosition = mSelectedPosition;
            if (mNextSelectedPosition >= 0 && mNextSelectedPosition != mSelectedPosition) {
                mResurrectToPosition = mNextSelectedPosition;
            }
            setSelectedPositionInt(INVALID_POSITION);
            setNextSelectedPositionInt(INVALID_POSITION);
            mSelectedTop = 0;
            mSelectorRect.setEmpty();
        }
    
private voidinitAbsListView()

        // Setting focusable in touch mode will set the focusable property to true
        setFocusableInTouchMode(true);
        setWillNotDraw(false);
        setAlwaysDrawnWithCacheEnabled(false);
        setScrollingCacheEnabled(true);

        mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
        mDensityScale = getContext().getResources().getDisplayMetrics().density;
    
public voidinvalidateViews()
Causes all the views to be rebuilt and redrawn.

        mDataChanged = true;
        rememberSyncState();
        requestLayout();
        invalidate();
    
voidinvokeOnItemScrollListener()
Notify our scroll listener (if there is one) of a change in scroll state

        if (mFastScroller != null) {
            mFastScroller.onScroll(this, mFirstPosition, getChildCount(), mItemCount);
        }
        if (mOnScrollListener != null) {
            mOnScrollListener.onScroll(this, mFirstPosition, getChildCount(), mItemCount);
        }
    
public booleanisFastScrollEnabled()
Returns the current state of the fast scroll feature.

see
#setFastScrollEnabled(boolean)
return
true if fast scroll is enabled, false otherwise

        return mFastScrollEnabled;
    
protected booleanisInFilterMode()

        return mFiltered;
    
public booleanisScrollingCacheEnabled()
Indicates whether the children's drawing cache is used during a scroll. By default, the drawing cache is enabled but this will consume more memory.

return
true if the scrolling cache is enabled, false otherwise
see
#setScrollingCacheEnabled(boolean)
see
View#setDrawingCacheEnabled(boolean)

        return mScrollingCacheEnabled;
    
public booleanisSmoothScrollbarEnabled()
Returns the current state of the fast scroll feature.

return
True if smooth scrollbar is enabled is enabled, false otherwise.
see
#setSmoothScrollbarEnabled(boolean)

        return mSmoothScrollbarEnabled;
    
public booleanisStackFromBottom()
Indicates whether the content of this view is pinned to, or stacked from, the bottom edge.

return
true if the content is stacked from the bottom edge, false otherwise

        return mStackFromBottom;
    
public booleanisTextFilterEnabled()
Indicates whether type filtering is enabled for this view

return
true if type filtering is enabled, false otherwise
see
#setTextFilterEnabled(boolean)
see
Filterable

        return mTextFilterEnabled;
    
protected booleanisVerticalScrollBarHidden()
If fast scroll is visible, then don't draw the vertical scrollbar.

hide

        return mFastScroller != null && mFastScroller.isVisible();
    
voidkeyPressed()
Sets the selector state to "pressed" and posts a CheckForKeyLongPress to see if this is a long press.

        Drawable selector = mSelector;
        Rect selectorRect = mSelectorRect;
        if (selector != null && (isFocused() || touchModeDrawsInPressedState())
                && selectorRect != null && !selectorRect.isEmpty()) {

            final View v = getChildAt(mSelectedPosition - mFirstPosition);

            if (v != null) {
                if (v.hasFocusable()) return;
                v.setPressed(true);
            }
            setPressed(true);

            final boolean longClickable = isLongClickable();
            Drawable d = selector.getCurrent();
            if (d != null && d instanceof TransitionDrawable) {
                if (longClickable) {
                    ((TransitionDrawable) d).startTransition(ViewConfiguration
                            .getLongPressTimeout());
                } else {
                    ((TransitionDrawable) d).resetTransition();
                }
            }
            if (longClickable && !mDataChanged) {
                if (mPendingCheckForKeyLongPress == null) {
                    mPendingCheckForKeyLongPress = new CheckForKeyLongPress();
                }
                mPendingCheckForKeyLongPress.rememberWindowAttachCount();
                postDelayed(mPendingCheckForKeyLongPress, ViewConfiguration.getLongPressTimeout());
            }
        }
    
protected voidlayoutChildren()

    
android.view.ViewobtainView(int position)
Get a view and have it show the data associated with the specified position. This is called when we have already discovered that the view is not available for reuse in the recycle bin. The only choices left are converting an old view or making a new one.

param
position The position to display
return
A view displaying the data associated with the specified position

        View scrapView;

        scrapView = mRecycler.getScrapView(position);

        View child;
        if (scrapView != null) {
            if (ViewDebug.TRACE_RECYCLER) {
                ViewDebug.trace(scrapView, ViewDebug.RecyclerTraceType.RECYCLE_FROM_SCRAP_HEAP,
                        position, -1);
            }

            child = mAdapter.getView(position, scrapView, this);

            if (ViewDebug.TRACE_RECYCLER) {
                ViewDebug.trace(child, ViewDebug.RecyclerTraceType.BIND_VIEW,
                        position, getChildCount());
            }

            if (child != scrapView) {
                mRecycler.addScrapView(scrapView);
                if (mCacheColorHint != 0) {
                    child.setDrawingCacheBackgroundColor(mCacheColorHint);
                }
                if (ViewDebug.TRACE_RECYCLER) {
                    ViewDebug.trace(scrapView, ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP,
                            position, -1);
                }
            }
        } else {
            child = mAdapter.getView(position, null, this);
            if (mCacheColorHint != 0) {
                child.setDrawingCacheBackgroundColor(mCacheColorHint);
            }
            if (ViewDebug.TRACE_RECYCLER) {
                ViewDebug.trace(child, ViewDebug.RecyclerTraceType.NEW_VIEW,
                        position, getChildCount());
            }
        }

        return child;
    
protected voidonAttachedToWindow()

        super.onAttachedToWindow();

        final ViewTreeObserver treeObserver = getViewTreeObserver();
        if (treeObserver != null) {
            treeObserver.addOnTouchModeChangeListener(this);
        }
    
protected int[]onCreateDrawableState(int extraSpace)

        // If the child view is enabled then do the default behavior.
        if (mIsChildViewEnabled) {
            // Common case
            return super.onCreateDrawableState(extraSpace);
        }

        // The selector uses this View's drawable state. The selected child view
        // is disabled, so we need to remove the enabled state from the drawable
        // states.
        final int enabledState = ENABLED_STATE_SET[0];

        // If we don't have any extra space, it will return one of the static state arrays,
        // and clearing the enabled state on those arrays is a bad thing!  If we specify
        // we need extra space, it will create+copy into a new array that safely mutable.
        int[] state = super.onCreateDrawableState(extraSpace + 1);
        int enabledPos = -1;
        for (int i = state.length - 1; i >= 0; i--) {
            if (state[i] == enabledState) {
                enabledPos = i;
                break;
            }
        }

        // Remove the enabled state
        if (enabledPos >= 0) {
            System.arraycopy(state, enabledPos + 1, state, enabledPos,
                    state.length - enabledPos - 1);
        }
        
        return state;
    
public android.view.inputmethod.InputConnectiononCreateInputConnection(android.view.inputmethod.EditorInfo outAttrs)
Return an InputConnection for editing of the filter text.

        if (isTextFilterEnabled()) {
            // XXX we need to have the text filter created, so we can get an
            // InputConnection to proxy to.  Unfortunately this means we pretty
            // much need to make it as soon as a list view gets focus.
            createTextFilter(false);
            if (mPublicInputConnection == null) {
                mDefInputConnection = new BaseInputConnection(this, false);
                mPublicInputConnection = new InputConnectionWrapper(
                        mTextFilter.onCreateInputConnection(outAttrs), true) {
                    @Override
                    public boolean reportFullscreenMode(boolean enabled) {
                        // Use our own input connection, since it is
                        // the "real" one the IME is talking with.
                        return mDefInputConnection.reportFullscreenMode(enabled);
                    }

                    @Override
                    public boolean performEditorAction(int editorAction) {
                        // The editor is off in its own window; we need to be
                        // the one that does this.
                        if (editorAction == EditorInfo.IME_ACTION_DONE) {
                            InputMethodManager imm = (InputMethodManager)
                                    getContext().getSystemService(
                                            Context.INPUT_METHOD_SERVICE);
                            if (imm != null) {
                                imm.hideSoftInputFromWindow(getWindowToken(), 0);
                            }
                            return true;
                        }
                        return false;
                    }

                    @Override
                    public boolean sendKeyEvent(KeyEvent event) {
                        // Use our own input connection, since the filter
                        // text view may not be shown in a window so has
                        // no ViewRoot to dispatch events with.
                        return mDefInputConnection.sendKeyEvent(event);
                    }
                };
            }
            outAttrs.inputType = EditorInfo.TYPE_CLASS_TEXT
                    | EditorInfo.TYPE_TEXT_VARIATION_FILTER;
            outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;
            return mPublicInputConnection;
        }
        return null;
    
protected voidonDetachedFromWindow()

        super.onDetachedFromWindow();

        final ViewTreeObserver treeObserver = getViewTreeObserver();
        if (treeObserver != null) {
            treeObserver.removeOnTouchModeChangeListener(this);
        }
    
public voidonFilterComplete(int count)

        if (mSelectedPosition < 0 && count > 0) {
            mResurrectToPosition = INVALID_POSITION;
            resurrectSelection();
        }
    
protected voidonFocusChanged(boolean gainFocus, int direction, android.graphics.Rect previouslyFocusedRect)

        super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
        if (gainFocus && mSelectedPosition < 0 && !isInTouchMode()) {
            resurrectSelection();
        }
    
public voidonGlobalLayout()

        if (isShown()) {
            // Show the popup if we are filtered
            if (mFiltered && mPopup != null && !mPopup.isShowing()) {
                showPopup();
            }
        } else {
            // Hide the popup when we are no longer visible
            if (mPopup.isShowing()) {
                dismissPopup();
            }
        }

    
public booleanonInterceptTouchEvent(android.view.MotionEvent ev)

        int action = ev.getAction();
        int x = (int) ev.getX();
        int y = (int) ev.getY();
        View v;
        
        if (mFastScroller != null) {
            boolean intercepted = mFastScroller.onInterceptTouchEvent(ev);
            if (intercepted) {
                return true;
            }
        }
        
        switch (action) {
        case MotionEvent.ACTION_DOWN: {
            int motionPosition = findMotionRow(y);
            if (mTouchMode != TOUCH_MODE_FLING && motionPosition >= 0) {
                // User clicked on an actual view (and was not stopping a fling).
                // Remember where the motion event started
                v = getChildAt(motionPosition - mFirstPosition);
                mMotionViewOriginalTop = v.getTop();
                mMotionX = x;
                mMotionY = y;
                mMotionPosition = motionPosition;
                mTouchMode = TOUCH_MODE_DOWN;
                clearScrollingCache();
            }
            mLastY = Integer.MIN_VALUE;
            break;
        }

        case MotionEvent.ACTION_MOVE: {
            switch (mTouchMode) {
            case TOUCH_MODE_DOWN:
                if (startScrollIfNeeded(y - mMotionY)) {
                    return true;
                }
                break;
            }
            break;
        }

        case MotionEvent.ACTION_UP: {
            mTouchMode = TOUCH_MODE_REST;
            reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
            break;
        }
        }

        return false;
    
public booleanonKeyUp(int keyCode, android.view.KeyEvent event)

        switch (keyCode) {
        case KeyEvent.KEYCODE_DPAD_CENTER:
        case KeyEvent.KEYCODE_ENTER:
            if (isPressed() && mSelectedPosition >= 0 && mAdapter != null &&
                    mSelectedPosition < mAdapter.getCount()) {
                final View view = getChildAt(mSelectedPosition - mFirstPosition);
                performItemClick(view, mSelectedPosition, mSelectedRowId);
                setPressed(false);
                if (view != null) view.setPressed(false);
                return true;
            }
        }
        return super.onKeyUp(keyCode, event);
    
protected voidonLayout(boolean changed, int l, int t, int r, int b)

        super.onLayout(changed, l, t, r, b);
        mInLayout = true;
        layoutChildren();
        mInLayout = false;
    
protected voidonMeasure(int widthMeasureSpec, int heightMeasureSpec)

        if (mSelector == null) {
            useDefaultSelector();
        }
        final Rect listPadding = mListPadding;
        listPadding.left = mSelectionLeftPadding + mPaddingLeft;
        listPadding.top = mSelectionTopPadding + mPaddingTop;
        listPadding.right = mSelectionRightPadding + mPaddingRight;
        listPadding.bottom = mSelectionBottomPadding + mPaddingBottom;
    
public voidonRestoreInstanceState(android.os.Parcelable state)

        SavedState ss = (SavedState) state;

        super.onRestoreInstanceState(ss.getSuperState());
        mDataChanged = true;

        mSyncHeight = ss.height;

        if (ss.selectedId >= 0) {
            mNeedSync = true;
            mSyncRowId = ss.selectedId;
            mSyncPosition = ss.position;
            mSpecificTop = ss.viewTop;
            mSyncMode = SYNC_SELECTED_POSITION;
        } else if (ss.firstId >= 0) {
            setSelectedPositionInt(INVALID_POSITION);
            // Do this before setting mNeedSync since setNextSelectedPosition looks at mNeedSync
            setNextSelectedPositionInt(INVALID_POSITION);
            mNeedSync = true;
            mSyncRowId = ss.firstId;
            mSyncPosition = ss.position;
            mSpecificTop = ss.viewTop;
            mSyncMode = SYNC_FIRST_POSITION;
        }

        setFilterText(ss.filter);

        requestLayout();
    
public android.os.ParcelableonSaveInstanceState()

    

    
       
        /*
         * This doesn't really make sense as the place to dismiss the
         * popup, but there don't seem to be any other useful hooks
         * that happen early enough to keep from getting complaints
         * about having leaked the window.
         */
        dismissPopup();

        Parcelable superState = super.onSaveInstanceState();

        SavedState ss = new SavedState(superState);

        boolean haveChildren = getChildCount() > 0;
        long selectedId = getSelectedItemId();
        ss.selectedId = selectedId;
        ss.height = getHeight();

        if (selectedId >= 0) {
            // Remember the selection
            ss.viewTop = mSelectedTop;
            ss.position = getSelectedItemPosition();
            ss.firstId = INVALID_POSITION;
        } else {
            if (haveChildren) {
                // Remember the position of the first child
                View v = getChildAt(0);
                ss.viewTop = v.getTop();
                ss.position = mFirstPosition;
                ss.firstId = mAdapter.getItemId(mFirstPosition);
            } else {
                ss.viewTop = 0;
                ss.firstId = INVALID_POSITION;
                ss.position = 0;
            }
        }

        ss.filter = null;
        if (mFiltered) {
            final EditText textFilter = mTextFilter;
            if (textFilter != null) {
                Editable filterText = textFilter.getText();
                if (filterText != null) {
                    ss.filter = filterText.toString();
                }
            }
        }

        return ss;
    
protected voidonSizeChanged(int w, int h, int oldw, int oldh)

        if (getChildCount() > 0) {
            mDataChanged = true;
            rememberSyncState();
        }
        if (mFastScroller != null) {
            mFastScroller.onSizeChanged(w, h, oldw, oldh);
        }
    
public voidonTextChanged(java.lang.CharSequence s, int start, int before, int count)
For our text watcher that is associated with the text filter. Performs the actual filtering as the text changes, and takes care of hiding and showing the popup displaying the currently entered filter text.

        if (mPopup != null && isTextFilterEnabled()) {
            int length = s.length();
            boolean showing = mPopup.isShowing();
            if (!showing && length > 0) {
                // Show the filter popup if necessary
                showPopup();
                mFiltered = true;
            } else if (showing && length == 0) {
                // Remove the filter popup if the user has cleared all text
                dismissPopup();
                mFiltered = false;
            }
            if (mAdapter instanceof Filterable) {
                Filter f = ((Filterable) mAdapter).getFilter();
                // Filter should not be null when we reach this part
                if (f != null) {
                    f.filter(s, this);
                } else {
                    throw new IllegalStateException("You cannot call onTextChanged with a non "
                            + "filterable adapter");
                }
            }
        }
    
public booleanonTouchEvent(android.view.MotionEvent ev)

        
        if (mFastScroller != null) {
            boolean intercepted = mFastScroller.onTouchEvent(ev);
            if (intercepted) {
                return true;
            }            
        }
        final int action = ev.getAction();
        final int x = (int) ev.getX();
        final int y = (int) ev.getY();

        View v;
        int deltaY;

        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(ev);

        switch (action) {
        case MotionEvent.ACTION_DOWN: {
            int motionPosition = pointToPosition(x, y);
            if (!mDataChanged) {
                if ((mTouchMode != TOUCH_MODE_FLING) && (motionPosition >= 0)
                        && (getAdapter().isEnabled(motionPosition))) {
                    // User clicked on an actual view (and was not stopping a fling). It might be a
                    // click or a scroll. Assume it is a click until proven otherwise
                    mTouchMode = TOUCH_MODE_DOWN;
                    // FIXME Debounce
                    if (mPendingCheckForTap == null) {
                        mPendingCheckForTap = new CheckForTap();
                    }
                    postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                } else {
                    if (ev.getEdgeFlags() != 0 && motionPosition < 0) {
                        // If we couldn't find a view to click on, but the down event was touching
                        // the edge, we will bail out and try again. This allows the edge correcting
                        // code in ViewRoot to try to find a nearby view to select
                        return false;
                    }
                    // User clicked on whitespace, or stopped a fling. It is a scroll.
                    createScrollingCache();
                    mTouchMode = TOUCH_MODE_SCROLL;
                    motionPosition = findMotionRow(y);
                    reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
                }
            }

            if (motionPosition >= 0) {
                // Remember where the motion event started
                v = getChildAt(motionPosition - mFirstPosition);
                mMotionViewOriginalTop = v.getTop();
                mMotionX = x;
                mMotionY = y;
                mMotionPosition = motionPosition;
            }
            mLastY = Integer.MIN_VALUE;
            break;
        }

        case MotionEvent.ACTION_MOVE: {
            deltaY = y - mMotionY;
            switch (mTouchMode) {
            case TOUCH_MODE_DOWN:
            case TOUCH_MODE_TAP:
            case TOUCH_MODE_DONE_WAITING:
                // Check if we have moved far enough that it looks more like a
                // scroll than a tap
                startScrollIfNeeded(deltaY);
                break;
            case TOUCH_MODE_SCROLL:
                if (PROFILE_SCROLLING) {
                    if (!mScrollProfilingStarted) {
                        Debug.startMethodTracing("AbsListViewScroll");
                        mScrollProfilingStarted = true;
                    }
                }

                if (y != mLastY) {
                    deltaY -= mMotionCorrection;
                    int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;
                    trackMotionScroll(deltaY, incrementalDeltaY);

                    // Check to see if we have bumped into the scroll limit
                    View motionView = this.getChildAt(mMotionPosition - mFirstPosition);
                    if (motionView != null) {
                        // Check if the top of the motion view is where it is
                        // supposed to be
                        if (motionView.getTop() != mMotionViewNewTop) {
                            // We did not scroll the full amount. Treat this essentially like the
                            // start of a new touch scroll
                            final int motionPosition = findMotionRow(y);

                            mMotionCorrection = 0;
                            motionView = getChildAt(motionPosition - mFirstPosition);
                            mMotionViewOriginalTop = motionView.getTop();
                            mMotionY = y;
                            mMotionPosition = motionPosition;
                        }
                    }
                    mLastY = y;
                }
                break;
            }

            break;
        }

        case MotionEvent.ACTION_UP: {
            switch (mTouchMode) {
            case TOUCH_MODE_DOWN:
            case TOUCH_MODE_TAP:
            case TOUCH_MODE_DONE_WAITING:
                final int motionPosition = mMotionPosition;
                final View child = getChildAt(motionPosition - mFirstPosition);
                if (child != null && !child.hasFocusable()) {
                    if (mTouchMode != TOUCH_MODE_DOWN) {
                        child.setPressed(false);
                    }

                    if (mPerformClick == null) {
                        mPerformClick = new PerformClick();
                    }

                    final AbsListView.PerformClick performClick = mPerformClick;
                    performClick.mChild = child;
                    performClick.mClickMotionPosition = motionPosition;
                    performClick.rememberWindowAttachCount();

                    mResurrectToPosition = motionPosition;

                    if (mTouchMode == TOUCH_MODE_DOWN || mTouchMode == TOUCH_MODE_TAP) {
                        final Handler handler = getHandler();
                        if (handler != null) {
                            handler.removeCallbacks(mTouchMode == TOUCH_MODE_DOWN ?
                                    mPendingCheckForTap : mPendingCheckForLongPress);
                        }
                        mLayoutMode = LAYOUT_NORMAL;
                        mTouchMode = TOUCH_MODE_TAP;
                        if (!mDataChanged) {
                            setSelectedPositionInt(mMotionPosition);
                            layoutChildren();
                            child.setPressed(true);
                            positionSelector(child);
                            setPressed(true);
                            if (mSelector != null) {
                                Drawable d = mSelector.getCurrent();
                                if (d != null && d instanceof TransitionDrawable) {
                                    ((TransitionDrawable)d).resetTransition();
                                }
                            }
                            postDelayed(new Runnable() {
                                public void run() {
                                    child.setPressed(false);
                                    setPressed(false);
                                    if (!mDataChanged) {
                                        post(performClick);
                                    }
                                    mTouchMode = TOUCH_MODE_REST;
                                }
                            }, ViewConfiguration.getPressedStateDuration());
                        }
                        return true;
                    } else {
                        if (!mDataChanged) {
                            post(performClick);
                        }
                    }
                }
                mTouchMode = TOUCH_MODE_REST;
                break;
            case TOUCH_MODE_SCROLL:
                final VelocityTracker velocityTracker = mVelocityTracker;
                velocityTracker.computeCurrentVelocity(1000);
                int initialVelocity = (int)velocityTracker.getYVelocity();

                if ((Math.abs(initialVelocity) >
                        ViewConfiguration.get(mContext).getScaledMinimumFlingVelocity()) &&
                        (getChildCount() > 0)) {
                    if (mFlingRunnable == null) {
                        mFlingRunnable = new FlingRunnable();
                    }
                    reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
                    mFlingRunnable.start(-initialVelocity);
                } else {
                    mTouchMode = TOUCH_MODE_REST;
                    reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
                }
            }

            setPressed(false);
            
            // Need to redraw since we probably aren't drawing the selector anymore
            invalidate();
            
            final Handler handler = getHandler();
            if (handler != null) {
                handler.removeCallbacks(mPendingCheckForLongPress);
            }

            if (mVelocityTracker != null) {
                mVelocityTracker.recycle();
                mVelocityTracker = null;
            }

            if (PROFILE_SCROLLING) {
                if (mScrollProfilingStarted) {
                    Debug.stopMethodTracing();
                    mScrollProfilingStarted = false;
                }
            }
            break;
        }

        case MotionEvent.ACTION_CANCEL: {
            mTouchMode = TOUCH_MODE_REST;
            setPressed(false);
            View motionView = this.getChildAt(mMotionPosition - mFirstPosition);
            if (motionView != null) {
                motionView.setPressed(false);
            }
            clearScrollingCache();

            final Handler handler = getHandler();
            if (handler != null) {
                handler.removeCallbacks(mPendingCheckForLongPress);
            }

            if (mVelocityTracker != null) {
                mVelocityTracker.recycle();
                mVelocityTracker = null;
            }
        }

        }

        return true;
    
public voidonTouchModeChanged(boolean isInTouchMode)

        if (isInTouchMode) {
            // Get rid of the selection when we enter touch mode
            hideSelector();
            // Layout, but only if we already have done so previously.
            // (Otherwise may clobber a LAYOUT_SYNC layout that was requested to restore
            // state.)
            if (getHeight() > 0 && getChildCount() > 0) {
                // We do not lose focus initiating a touch (since AbsListView is focusable in
                // touch mode). Force an initial layout to get rid of the selection.
                mLayoutMode = LAYOUT_NORMAL;
                layoutChildren();
            }
        }
    
public voidonWindowFocusChanged(boolean hasWindowFocus)

        super.onWindowFocusChanged(hasWindowFocus);

        final int touchMode = isInTouchMode() ? TOUCH_MODE_ON : TOUCH_MODE_OFF;

        if (!hasWindowFocus) {
            setChildrenDrawingCacheEnabled(false);
            removeCallbacks(mFlingRunnable);
            // Always hide the type filter
            dismissPopup();

            if (touchMode == TOUCH_MODE_OFF) {
                // Remember the last selected element
                mResurrectToPosition = mSelectedPosition;
            }
        } else {
            if (mFiltered) {
                // Show the type filter only if a filter is in effect
                showPopup();
            }

            // If we changed touch mode since the last time we had focus
            if (touchMode != mLastTouchMode && mLastTouchMode != TOUCH_MODE_UNKNOWN) {
                // If we come back in trackball mode, we bring the selection back
                if (touchMode == TOUCH_MODE_OFF) {
                    // This will trigger a layout
                    resurrectSelection();

                // If we come back in touch mode, then we want to hide the selector
                } else {
                    hideSelector();
                    mLayoutMode = LAYOUT_NORMAL;
                    layoutChildren();
                }
            }
        }

        mLastTouchMode = touchMode;
    
private booleanperformLongPress(android.view.View child, int longPressPosition, long longPressId)

        boolean handled = false;

        if (mOnItemLongClickListener != null) {
            handled = mOnItemLongClickListener.onItemLongClick(AbsListView.this, child,
                    longPressPosition, longPressId);
        }
        if (!handled) {
            mContextMenuInfo = createContextMenuInfo(child, longPressPosition, longPressId);
            handled = super.showContextMenuForChild(AbsListView.this);
        }
        if (handled) {
            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
        }
        return handled;
    
public intpointToPosition(int x, int y)
Maps a point to a position in the list.

param
x X in local coordinate
param
y Y in local coordinate
return
The position of the item which contains the specified point, or {@link #INVALID_POSITION} if the point does not intersect an item.

        Rect frame = mTouchFrame;
        if (frame == null) {
            mTouchFrame = new Rect();
            frame = mTouchFrame;
        }

        final int count = getChildCount();
        for (int i = count - 1; i >= 0; i--) {
            final View child = getChildAt(i);
            if (child.getVisibility() == View.VISIBLE) {
                child.getHitRect(frame);
                if (frame.contains(x, y)) {
                    return mFirstPosition + i;
                }
            }
        }
        return INVALID_POSITION;
    
public longpointToRowId(int x, int y)
Maps a point to a the rowId of the item which intersects that point.

param
x X in local coordinate
param
y Y in local coordinate
return
The rowId of the item which contains the specified point, or {@link #INVALID_ROW_ID} if the point does not intersect an item.

        int position = pointToPosition(x, y);
        if (position >= 0) {
            return mAdapter.getItemId(position);
        }
        return INVALID_ROW_ID;
    
private voidpositionPopup()

        int screenHeight = getResources().getDisplayMetrics().heightPixels;
        final int[] xy = new int[2];
        getLocationOnScreen(xy);
        // TODO: The 20 below should come from the theme and be expressed in dip
        // TODO: And the gravity should be defined in the theme as well
        final int bottomGap = screenHeight - xy[1] - getHeight() + (int) (mDensityScale * 20);
        if (!mPopup.isShowing()) {
            mPopup.showAtLocation(this, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL,
                    xy[0], bottomGap);
        } else {
            mPopup.update(xy[0], bottomGap, -1, -1);
        }
    
voidpositionSelector(android.view.View sel)

        final Rect selectorRect = mSelectorRect;
        selectorRect.set(sel.getLeft(), sel.getTop(), sel.getRight(), sel.getBottom());
        positionSelector(selectorRect.left, selectorRect.top, selectorRect.right,
                selectorRect.bottom);

        final boolean isChildViewEnabled = mIsChildViewEnabled;
        if (sel.isEnabled() != isChildViewEnabled) {
            mIsChildViewEnabled = !isChildViewEnabled;
            refreshDrawableState();
        }
    
private voidpositionSelector(int l, int t, int r, int b)

        mSelectorRect.set(l - mSelectionLeftPadding, t - mSelectionTopPadding, r
                + mSelectionRightPadding, b + mSelectionBottomPadding);
    
public voidreclaimViews(java.util.List views)
Move all views (excluding headers and footers) held by this AbsListView into the supplied List. This includes views displayed on the screen as well as views stored in AbsListView's internal view recycler.

param
views A list into which to put the reclaimed views

        int childCount = getChildCount();
        RecyclerListener listener = mRecycler.mRecyclerListener;

        // Reclaim views on screen
        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            AbsListView.LayoutParams lp = (AbsListView.LayoutParams)child.getLayoutParams();
            // Don't reclaim header or footer views, or views that should be ignored
            if (lp != null && mRecycler.shouldRecycleViewType(lp.viewType)) {
                views.add(child);
                if (listener != null) {
                    // Pretend they went through the scrap heap
                    listener.onMovedToScrapHeap(child);
                }
            }
        }
        mRecycler.reclaimScrapViews(views);
        removeAllViewsInLayout();
    
intreconcileSelectedPosition()

return
A position to select. First we try mSelectedPosition. If that has been clobbered by entering touch mode, we then try mResurrectToPosition. Values are pinned to the range of items available in the adapter

        int position = mSelectedPosition;
        if (position < 0) {
            position = mResurrectToPosition;
        }
        position = Math.max(0, position);
        position = Math.min(position, mItemCount - 1);
        return position;
    
voidreportScrollStateChange(int newState)
Fires an "on scroll state changed" event to the registered {@link android.widget.AbsListView.OnScrollListener}, if any. The state change is fired only if the specified state is different from the previously known state.

param
newState The new scroll state.

        if (newState != mLastScrollState) {
            if (mOnScrollListener != null) {
                mOnScrollListener.onScrollStateChanged(this, newState);
                mLastScrollState = newState;
            }
        }
    
public voidrequestLayout()

        if (!mBlockLayoutRequests && !mInLayout) {
            super.requestLayout();
        }
    
voidrequestLayoutIfNecessary()

        if (getChildCount() > 0) {
            resetList();
            requestLayout();
            invalidate();
        }
    
voidresetList()
The list is empty. Clear everything out.

        removeAllViewsInLayout();
        mFirstPosition = 0;
        mDataChanged = false;
        mNeedSync = false;
        mOldSelectedPosition = INVALID_POSITION;
        mOldSelectedRowId = INVALID_ROW_ID;
        setSelectedPositionInt(INVALID_POSITION);
        setNextSelectedPositionInt(INVALID_POSITION);
        mSelectedTop = 0;
        mSelectorRect.setEmpty();
        invalidate();
    
voidresetListAndClearViews()
The list is empty and we need to change the layout, so *really* clear everything out.

hide
- for AutoCompleteTextView & SearchDialog only

        rememberSyncState();
        removeAllViewsInLayout();
        mRecycler.clear();
        mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());
        requestLayout();
    
booleanresurrectSelection()
Attempt to bring the selection back if the user is switching from touch to trackball mode

return
Whether selection was set to something.

        final int childCount = getChildCount();

        if (childCount <= 0) {
            return false;
        }

        int selectedTop = 0;
        int selectedPos;
        int childrenTop = mListPadding.top;
        int childrenBottom = mBottom - mTop - mListPadding.bottom;
        final int firstPosition = mFirstPosition;
        final int toPosition = mResurrectToPosition;
        boolean down = true;

        if (toPosition >= firstPosition && toPosition < firstPosition + childCount) {
            selectedPos = toPosition;

            final View selected = getChildAt(selectedPos - mFirstPosition);
            selectedTop = selected.getTop();
            int selectedBottom = selected.getBottom();

            // We are scrolled, don't get in the fade
            if (selectedTop < childrenTop) {
                selectedTop = childrenTop + getVerticalFadingEdgeLength();
            } else if (selectedBottom > childrenBottom) {
                selectedTop = childrenBottom - selected.getMeasuredHeight()
                        - getVerticalFadingEdgeLength();
            }
        } else {
            if (toPosition < firstPosition) {
                // Default to selecting whatever is first
                selectedPos = firstPosition;
                for (int i = 0; i < childCount; i++) {
                    final View v = getChildAt(i);
                    final int top = v.getTop();

                    if (i == 0) {
                        // Remember the position of the first item
                        selectedTop = top;
                        // See if we are scrolled at all
                        if (firstPosition > 0 || top < childrenTop) {
                            // If we are scrolled, don't select anything that is
                            // in the fade region
                            childrenTop += getVerticalFadingEdgeLength();
                        }
                    }
                    if (top >= childrenTop) {
                        // Found a view whose top is fully visisble
                        selectedPos = firstPosition + i;
                        selectedTop = top;
                        break;
                    }
                }
            } else {
                final int itemCount = mItemCount;
                down = false;
                selectedPos = firstPosition + childCount - 1;

                for (int i = childCount - 1; i >= 0; i--) {
                    final View v = getChildAt(i);
                    final int top = v.getTop();
                    final int bottom = v.getBottom();

                    if (i == childCount - 1) {
                        selectedTop = top;
                        if (firstPosition + childCount < itemCount || bottom > childrenBottom) {
                            childrenBottom -= getVerticalFadingEdgeLength();
                        }
                    }

                    if (bottom <= childrenBottom) {
                        selectedPos = firstPosition + i;
                        selectedTop = top;
                        break;
                    }
                }
            }
        }

        mResurrectToPosition = INVALID_POSITION;
        removeCallbacks(mFlingRunnable);
        mTouchMode = TOUCH_MODE_REST;
        clearScrollingCache();
        mSpecificTop = selectedTop;
        selectedPos = lookForSelectablePosition(selectedPos, down);
        if (selectedPos >= firstPosition && selectedPos <= getLastVisiblePosition()) {
            mLayoutMode = LAYOUT_SPECIFIC;
            setSelectionInt(selectedPos);
            invokeOnItemScrollListener();
        } else {
            selectedPos = INVALID_POSITION;
        }
        reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);

        return selectedPos >= 0;
    
booleansendToTextFilter(int keyCode, int count, android.view.KeyEvent event)
Sends a key to the text filter window

param
keyCode The keycode for the event
param
event The actual key event
return
True if the text filter handled the event, false otherwise.

        if (!acceptFilter()) {
            return false;
        }

        boolean handled = false;
        boolean okToSend = true;
        switch (keyCode) {
        case KeyEvent.KEYCODE_DPAD_UP:
        case KeyEvent.KEYCODE_DPAD_DOWN:
        case KeyEvent.KEYCODE_DPAD_LEFT:
        case KeyEvent.KEYCODE_DPAD_RIGHT:
        case KeyEvent.KEYCODE_DPAD_CENTER:
        case KeyEvent.KEYCODE_ENTER:
            okToSend = false;
            break;
        case KeyEvent.KEYCODE_BACK:
            if (mFiltered && mPopup != null && mPopup.isShowing() &&
                    event.getAction() == KeyEvent.ACTION_DOWN) {
                handled = true;
                mTextFilter.setText("");
            }
            okToSend = false;
            break;
        case KeyEvent.KEYCODE_SPACE:
            // Only send spaces once we are filtered
            okToSend = mFiltered = true;
            break;
        }

        if (okToSend) {
            createTextFilter(true);

            KeyEvent forwardEvent = event;
            if (forwardEvent.getRepeatCount() > 0) {
                forwardEvent = KeyEvent.changeTimeRepeat(event, event.getEventTime(), 0);
            }

            int action = event.getAction();
            switch (action) {
                case KeyEvent.ACTION_DOWN:
                    handled = mTextFilter.onKeyDown(keyCode, forwardEvent);
                    break;

                case KeyEvent.ACTION_UP:
                    handled = mTextFilter.onKeyUp(keyCode, forwardEvent);
                    break;

                case KeyEvent.ACTION_MULTIPLE:
                    handled = mTextFilter.onKeyMultiple(keyCode, count, event);
                    break;
            }
        }
        return handled;
    
public voidsetCacheColorHint(int color)
When set to a non-zero value, the cache color hint indicates that this list is always drawn on top of a solid, single-color, opaque background

param
color The background color

        mCacheColorHint = color;
    
public voidsetDrawSelectorOnTop(boolean onTop)
Controls whether the selection highlight drawable should be drawn on top of the item or behind it.

param
onTop If true, the selector will be drawn on the item it is highlighting. The default is false.
attr
ref android.R.styleable#AbsListView_drawSelectorOnTop

        mDrawSelectorOnTop = onTop;
    
public voidsetFastScrollEnabled(boolean enabled)
Enables fast scrolling by letting the user quickly scroll through lists by dragging the fast scroll thumb. The adapter attached to the list may want to implement {@link SectionIndexer} if it wishes to display alphabet preview and jump between sections of the list.

see
SectionIndexer
see
#isFastScrollEnabled()
param
enabled whether or not to enable fast scrolling

        mFastScrollEnabled = enabled;
        if (enabled) {
            if (mFastScroller == null) {
                mFastScroller = new FastScroller(getContext(), this);
            }
        } else {
            if (mFastScroller != null) {
                mFastScroller.stop();
                mFastScroller = null;
            }
        }
    
public voidsetFilterText(java.lang.String filterText)
Sets the initial value for the text filter.

param
filterText The text to use for the filter.
see
#setTextFilterEnabled

        // TODO: Should we check for acceptFilter()?
        if (mTextFilterEnabled && !TextUtils.isEmpty(filterText)) {
            createTextFilter(false);
            // This is going to call our listener onTextChanged, but we might not
            // be ready to bring up a window yet
            mTextFilter.setText(filterText);
            mTextFilter.setSelection(filterText.length());
            if (mAdapter instanceof Filterable) {
                // if mPopup is non-null, then onTextChanged will do the filtering
                if (mPopup == null) {
                    Filter f = ((Filterable) mAdapter).getFilter();
                    f.filter(filterText);
                }
                // Set filtered to true so we will display the filter window when our main
                // window is ready
                mFiltered = true;
                mDataSetObserver.clearSavedState();
            }
        }
    
protected booleansetFrame(int left, int top, int right, int bottom)

hide

        final boolean changed = super.setFrame(left, top, right, bottom);

        // Reposition the popup when the frame has changed. This includes
        // translating the widget, not just changing its dimension. The
        // filter popup needs to follow the widget.
        if (mFiltered && changed && getWindowVisibility() == View.VISIBLE && mPopup != null &&
                mPopup.isShowing()) {
            positionPopup();
        }

        return changed;
    
public voidsetOnScrollListener(android.widget.AbsListView$OnScrollListener l)
Set the listener that will receive notifications every time the list scrolls.

param
l the scroll listener

        mOnScrollListener = l;
        invokeOnItemScrollListener();
    
public voidsetRecyclerListener(android.widget.AbsListView$RecyclerListener listener)
Sets the recycler listener to be notified whenever a View is set aside in the recycler for later reuse. This listener can be used to free resources associated to the View.

param
listener The recycler listener to be notified of views set aside in the recycler.
see
android.widget.AbsListView.RecycleBin
see
android.widget.AbsListView.RecyclerListener

        mRecycler.mRecyclerListener = listener;
    
public voidsetScrollIndicators(android.view.View up, android.view.View down)

        mScrollUp = up;
        mScrollDown = down;
    
public voidsetScrollingCacheEnabled(boolean enabled)
Enables or disables the children's drawing cache during a scroll. By default, the drawing cache is enabled but this will use more memory. When the scrolling cache is enabled, the caches are kept after the first scrolling. You can manually clear the cache by calling {@link android.view.ViewGroup#setChildrenDrawingCacheEnabled(boolean)}.

param
enabled true to enable the scroll cache, false otherwise
see
#isScrollingCacheEnabled()
see
View#setDrawingCacheEnabled(boolean)

        if (mScrollingCacheEnabled && !enabled) {
            clearScrollingCache();
        }
        mScrollingCacheEnabled = enabled;
    
abstract voidsetSelectionInt(int position)
Makes the item at the supplied position selected.

param
position the position of the new selection

public voidsetSelector(int resID)
Set a Drawable that should be used to highlight the currently selected item.

param
resID A Drawable resource to use as the selection highlight.
attr
ref android.R.styleable#AbsListView_listSelector

        setSelector(getResources().getDrawable(resID));
    
public voidsetSelector(android.graphics.drawable.Drawable sel)

        if (mSelector != null) {
            mSelector.setCallback(null);
            unscheduleDrawable(mSelector);
        }
        mSelector = sel;
        Rect padding = new Rect();
        sel.getPadding(padding);
        mSelectionLeftPadding = padding.left;
        mSelectionTopPadding = padding.top;
        mSelectionRightPadding = padding.right;
        mSelectionBottomPadding = padding.bottom;
        sel.setCallback(this);
        sel.setState(getDrawableState());
    
public voidsetSmoothScrollbarEnabled(boolean enabled)
When smooth scrollbar is enabled, the position and size of the scrollbar thumb is computed based on the number of visible pixels in the visible items. This however assumes that all list items have the same height. If you use a list in which items have different heights, the scrollbar will change appearance as the user scrolls through the list. To avoid this issue, you need to disable this property. When smooth scrollbar is disabled, the position and size of the scrollbar thumb is based solely on the number of items in the adapter and the position of the visible items inside the adapter. This provides a stable scrollbar as the user navigates through a list of items with varying heights.

param
enabled Whether or not to enable smooth scrollbar.
see
#setSmoothScrollbarEnabled(boolean)
attr
ref android.R.styleable#AbsListView_smoothScrollbar

        mSmoothScrollbarEnabled = enabled;
    
public voidsetStackFromBottom(boolean stackFromBottom)
When stack from bottom is set to true, the list fills its content starting from the bottom of the view.

param
stackFromBottom true to pin the view's content to the bottom edge, false to pin the view's content to the top edge

        if (mStackFromBottom != stackFromBottom) {
            mStackFromBottom = stackFromBottom;
            requestLayoutIfNecessary();
        }
    
public voidsetTextFilterEnabled(boolean textFilterEnabled)
Enables or disables the type filter window. If enabled, typing when this view has focus will filter the children to match the users input. Note that the {@link Adapter} used by this view must implement the {@link Filterable} interface.

param
textFilterEnabled true to enable type filtering, false otherwise
see
Filterable

        mTextFilterEnabled = textFilterEnabled;
    
public voidsetTranscriptMode(int mode)
Puts the list or grid into transcript mode. In this mode the list or grid will always scroll to the bottom to show new items.

param
mode the transcript mode to set
see
#TRANSCRIPT_MODE_DISABLED
see
#TRANSCRIPT_MODE_NORMAL
see
#TRANSCRIPT_MODE_ALWAYS_SCROLL

        mTranscriptMode = mode;
    
booleanshouldShowSelector()
Indicates whether this view is in a state where the selector should be drawn. This will happen if we have focus but are not in touch mode, or we are in the middle of displaying the pressed state for an item.

return
True if the selector should be shown

        return (hasFocus() && !isInTouchMode()) || touchModeDrawsInPressedState();
    
public booleanshowContextMenuForChild(android.view.View originalView)

        final int longPressPosition = getPositionForView(originalView);
        if (longPressPosition >= 0) {
            final long longPressId = mAdapter.getItemId(longPressPosition);
            boolean handled = false;

            if (mOnItemLongClickListener != null) {
                handled = mOnItemLongClickListener.onItemLongClick(AbsListView.this, originalView,
                        longPressPosition, longPressId);
            }
            if (!handled) {
                mContextMenuInfo = createContextMenuInfo(
                        getChildAt(longPressPosition - mFirstPosition),
                        longPressPosition, longPressId);
                handled = super.showContextMenuForChild(originalView);
            }

            return handled;
        }
        return false;
    
private voidshowPopup()
Shows the filter window

        // Make sure we have a window before showing the popup
        if (getWindowVisibility() == View.VISIBLE) {
            createTextFilter(true);
            positionPopup();
            // Make sure we get focus if we are showing the popup
            checkFocus();
        }
    
private booleanstartScrollIfNeeded(int deltaY)

        // Check if we have moved far enough that it looks more like a
        // scroll than a tap
        final int distance = Math.abs(deltaY);
        if (distance > mTouchSlop) {
            createScrollingCache();
            mTouchMode = TOUCH_MODE_SCROLL;
            mMotionCorrection = deltaY;
            final Handler handler = getHandler();
            // Handler should not be null unless the AbsListView is not attached to a
            // window, which would make it very hard to scroll it... but the monkeys
            // say it's possible.
            if (handler != null) {
                handler.removeCallbacks(mPendingCheckForLongPress);
            }
            setPressed(false);
            View motionView = getChildAt(mMotionPosition - mFirstPosition);
            if (motionView != null) {
                motionView.setPressed(false);
            }
            reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
            // Time to start stealing events! Once we've stolen them, don't let anyone
            // steal from us
            requestDisallowInterceptTouchEvent(true);
            return true;
        }

        return false;
    
booleantouchModeDrawsInPressedState()

return
True if the current touch mode requires that we draw the selector in the pressed state.

        // FIXME use isPressed for this
        switch (mTouchMode) {
        case TOUCH_MODE_TAP:
        case TOUCH_MODE_DONE_WAITING:
            return true;
        default:
            return false;
        }
    
voidtrackMotionScroll(int deltaY, int incrementalDeltaY)
Track a motion scroll

param
deltaY Amount to offset mMotionView. This is the accumulated delta since the motion began. Positive numbers mean the user's finger is moving down the screen.
param
incrementalDeltaY Change in deltaY from the previous event.

        final int childCount = getChildCount();
        if (childCount == 0) {
            return;
        }

        final int firstTop = getChildAt(0).getTop();
        final int lastBottom = getChildAt(childCount - 1).getBottom();

        final Rect listPadding = mListPadding;

         // FIXME account for grid vertical spacing too?
        final int spaceAbove = listPadding.top - firstTop;
        final int end = getHeight() - listPadding.bottom;
        final int spaceBelow = lastBottom - end;

        final int height = getHeight() - mPaddingBottom - mPaddingTop;
        if (deltaY < 0) {
            deltaY = Math.max(-(height - 1), deltaY);
        } else {
            deltaY = Math.min(height - 1, deltaY);
        }

        if (incrementalDeltaY < 0) {
            incrementalDeltaY = Math.max(-(height - 1), incrementalDeltaY);
        } else {
            incrementalDeltaY = Math.min(height - 1, incrementalDeltaY);
        }

        final int absIncrementalDeltaY = Math.abs(incrementalDeltaY);

        if (spaceAbove >= absIncrementalDeltaY && spaceBelow >= absIncrementalDeltaY) {
            hideSelector();
            offsetChildrenTopAndBottom(incrementalDeltaY);
            invalidate();
            mMotionViewNewTop = mMotionViewOriginalTop + deltaY;
        } else {
            final int firstPosition = mFirstPosition;

            if (firstPosition == 0 && firstTop >= listPadding.top && deltaY > 0) {
                // Don't need to move views down if the top of the first position is already visible
                return;
            }

            if (firstPosition + childCount == mItemCount && lastBottom <= end && deltaY < 0) {
                // Don't need to move views up if the bottom of the last position is already visible
                return;
            }

            final boolean down = incrementalDeltaY < 0;

            hideSelector();

            final int headerViewsCount = getHeaderViewsCount();
            final int footerViewsStart = mItemCount - getFooterViewsCount();

            int start = 0;
            int count = 0;

            if (down) {
                final int top = listPadding.top - incrementalDeltaY;
                for (int i = 0; i < childCount; i++) {
                    final View child = getChildAt(i);
                    if (child.getBottom() >= top) {
                        break;
                    } else {
                        count++;
                        int position = firstPosition + i;
                        if (position >= headerViewsCount && position < footerViewsStart) {
                            mRecycler.addScrapView(child);

                            if (ViewDebug.TRACE_RECYCLER) {
                                ViewDebug.trace(child,
                                        ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP,
                                        firstPosition + i, -1);
                            }
                        }
                    }
                }
            } else {
                final int bottom = getHeight() - listPadding.bottom - incrementalDeltaY;
                for (int i = childCount - 1; i >= 0; i--) {
                    final View child = getChildAt(i);
                    if (child.getTop() <= bottom) {
                        break;
                    } else {
                        start = i;
                        count++;
                        int position = firstPosition + i;
                        if (position >= headerViewsCount && position < footerViewsStart) {
                            mRecycler.addScrapView(child);

                            if (ViewDebug.TRACE_RECYCLER) {
                                ViewDebug.trace(child,
                                        ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP,
                                        firstPosition + i, -1);
                            }
                        }
                    }
                }
            }

            mMotionViewNewTop = mMotionViewOriginalTop + deltaY;

            mBlockLayoutRequests = true;
            detachViewsFromParent(start, count);
            offsetChildrenTopAndBottom(incrementalDeltaY);

            if (down) {
                mFirstPosition += count;
            }

            invalidate();
            fillGap(down);
            mBlockLayoutRequests = false;

            invokeOnItemScrollListener();
        }
    
voidupdateScrollIndicators()

        if (mScrollUp != null) {
            boolean canScrollUp;
            // 0th element is not visible
            canScrollUp = mFirstPosition > 0;

            // ... Or top of 0th element is not visible
            if (!canScrollUp) {
                if (getChildCount() > 0) {
                    View child = getChildAt(0);
                    canScrollUp = child.getTop() < mListPadding.top;
                }
            }

            mScrollUp.setVisibility(canScrollUp ? View.VISIBLE : View.INVISIBLE);
        }

        if (mScrollDown != null) {
            boolean canScrollDown;
            int count = getChildCount();

            // Last item is not visible
            canScrollDown = (mFirstPosition + count) < mItemCount;

            // ... Or bottom of the last element is not visible
            if (!canScrollDown && count > 0) {
                View child = getChildAt(count - 1);
                canScrollDown = child.getBottom() > mBottom - mListPadding.bottom;
            }

            mScrollDown.setVisibility(canScrollDown ? View.VISIBLE : View.INVISIBLE);
        }
    
private voiduseDefaultSelector()

        setSelector(getResources().getDrawable(
                com.android.internal.R.drawable.list_selector_background));
    
public booleanverifyDrawable(android.graphics.drawable.Drawable dr)

        return mSelector == dr || super.verifyDrawable(dr);