AbsListView.java revision 499cb9f516062b654952d282f211bee44c31a3c2
14f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project/* 24f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Copyright (C) 2006 The Android Open Source Project 34f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 44f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 54f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * you may not use this file except in compliance with the License. 64f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * You may obtain a copy of the License at 74f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 84f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 94f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 104f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 114f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 124f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 134f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * See the License for the specific language governing permissions and 144f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * limitations under the License. 154f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 164f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 174f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectpackage android.widget; 184f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 194f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport com.android.internal.R; 204f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 214f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.content.Context; 224f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.content.Intent; 234f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.content.res.TypedArray; 244f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.graphics.Canvas; 254f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.graphics.Rect; 264f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.graphics.drawable.Drawable; 274f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.graphics.drawable.TransitionDrawable; 282bef93cc20155c3a59cdbb22c564c4b385b2c160Bruce Beareimport android.os.Debug; 2996dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewellimport android.os.Handler; 309b6c850d24df82451862b81f059361b586f5ef0bJean-Baptiste Queruimport android.os.Parcel; 314f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.os.Parcelable; 324f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.text.Editable; 334f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.text.TextUtils; 344f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.text.TextWatcher; 354f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.util.AttributeSet; 364f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.util.Log; 374f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.view.Gravity; 384f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.view.HapticFeedbackConstants; 394f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.view.KeyEvent; 404f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.view.LayoutInflater; 414f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.view.MotionEvent; 424f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.view.VelocityTracker; 434f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.view.View; 444f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.view.ViewConfiguration; 454f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.view.ViewDebug; 464f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.view.ViewGroup; 474f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.view.ViewTreeObserver; 484f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.view.ContextMenu.ContextMenuInfo; 494f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.view.inputmethod.BaseInputConnection; 504f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.view.inputmethod.EditorInfo; 514f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.view.inputmethod.InputConnection; 524f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.view.inputmethod.InputConnectionWrapper; 534f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport android.view.inputmethod.InputMethodManager; 544f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 554f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport java.util.ArrayList; 564f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectimport java.util.List; 574f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 584f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project/** 594f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Base class that can be used to implement virtualized lists of items. A list does 604f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * not have a spatial definition here. For instance, subclases of this class can 614f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * display the content of the list in a grid, in a carousel, as stack, etc. 624f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 634f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @attr ref android.R.styleable#AbsListView_listSelector 644f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @attr ref android.R.styleable#AbsListView_drawSelectorOnTop 654f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @attr ref android.R.styleable#AbsListView_stackFromBottom 664f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @attr ref android.R.styleable#AbsListView_scrollingCache 674f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @attr ref android.R.styleable#AbsListView_textFilterEnabled 684f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @attr ref android.R.styleable#AbsListView_transcriptMode 694f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @attr ref android.R.styleable#AbsListView_cacheColorHint 704f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @attr ref android.R.styleable#AbsListView_fastScrollEnabled 714f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @attr ref android.R.styleable#AbsListView_smoothScrollbar 724f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 734f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Projectpublic abstract class AbsListView extends AdapterView<ListAdapter> implements TextWatcher, 744f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project ViewTreeObserver.OnGlobalLayoutListener, Filter.FilterListener, 754f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project ViewTreeObserver.OnTouchModeChangeListener, 764f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project RemoteViewsAdapter.RemoteAdapterConnectionCallback { 774f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 784f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 794f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Disables the transcript mode. 804f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 814f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @see #setTranscriptMode(int) 824f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 834f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public static final int TRANSCRIPT_MODE_DISABLED = 0; 844f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 854f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The list will automatically scroll to the bottom when a data set change 864f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * notification is received and only if the last item is already visible 874f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * on screen. 884f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 894f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @see #setTranscriptMode(int) 904f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 914f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public static final int TRANSCRIPT_MODE_NORMAL = 1; 924f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 934f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The list will automatically scroll to the bottom, no matter what items 944f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * are currently visible. 954f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 964f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @see #setTranscriptMode(int) 974f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 984f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public static final int TRANSCRIPT_MODE_ALWAYS_SCROLL = 2; 994f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 1004f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 1014f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Indicates that we are not in the middle of a touch gesture 1024f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 1034f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project static final int TOUCH_MODE_REST = -1; 1044f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 1054f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 1064f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Indicates we just received the touch event and we are waiting to see if the it is a tap or a 1074f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * scroll gesture. 1084f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 1094f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project static final int TOUCH_MODE_DOWN = 0; 1104f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 1114f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 1124f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Indicates the touch has been recognized as a tap and we are now waiting to see if the touch 1134f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * is a longpress 1144f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 1154f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project static final int TOUCH_MODE_TAP = 1; 1164f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 1174f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 1184f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Indicates we have waited for everything we can wait for, but the user's finger is still down 1194f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 1204f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project static final int TOUCH_MODE_DONE_WAITING = 2; 1214f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 1224f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 1234f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Indicates the touch gesture is a scroll 1244f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 1254f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project static final int TOUCH_MODE_SCROLL = 3; 1264f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 1274f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 1284f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Indicates the view is in the process of being flung 1294f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 1304f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project static final int TOUCH_MODE_FLING = 4; 1314f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 1324f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 1334f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Regular layout - usually an unsolicited layout from the view system 1344f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 1354f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project static final int LAYOUT_NORMAL = 0; 1364f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 1374f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 1384f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Show the first item 1394f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 1404f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project static final int LAYOUT_FORCE_TOP = 1; 1414f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 1424f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 1434f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Force the selected item to be on somewhere on the screen 1444f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 1454f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project static final int LAYOUT_SET_SELECTION = 2; 1464f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 1474f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 1484f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Show the last item 1494f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 1504f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project static final int LAYOUT_FORCE_BOTTOM = 3; 1514f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 1524f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 1534f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Make a mSelectedItem appear in a specific location and build the rest of 1544f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * the views from there. The top is specified by mSpecificTop. 1554f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 1564f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project static final int LAYOUT_SPECIFIC = 4; 1574f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 1584f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 1594f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Layout to sync as a result of a data change. Restore mSyncPosition to have its top 1604f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * at mSpecificTop 1614f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 1624f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project static final int LAYOUT_SYNC = 5; 1634f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 1644f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 1654f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Layout as a result of using the navigation keys 1664f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 1674f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project static final int LAYOUT_MOVE_SELECTION = 6; 1684f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 1694f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 1704f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Controls how the next layout will happen 1714f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 1724f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int mLayoutMode = LAYOUT_NORMAL; 1734f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 1744f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 1754f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Should be used by subclasses to listen to changes in the dataset 1764f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 1774f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project AdapterDataSetObserver mDataSetObserver; 1784f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 1794f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 1804f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The adapter containing the data to be displayed by this view 1814f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 1824f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project ListAdapter mAdapter; 1834f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 1844f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 1854f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The remote adapter containing the data to be displayed by this view to be set 1864f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 1874f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private RemoteViewsAdapter mRemoteAdapter; 1884f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 1894f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 1904f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Indicates whether the list selector should be drawn on top of the children or behind 1914f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 1924f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project boolean mDrawSelectorOnTop = false; 1934f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 1944f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 1954f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The drawable used to draw the selector 1964f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 1974f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project Drawable mSelector; 1984f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 1994f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 2004f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Defines the selector's location and dimension at drawing time 2014f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 2024f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project Rect mSelectorRect = new Rect(); 2034f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 2044f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 2054f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The data set used to store unused views that should be reused during the next layout 2064f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * to avoid creating new ones 2074f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 2084f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final RecycleBin mRecycler = new RecycleBin(); 2094f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 2104f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 2114f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The selection's left padding 2124f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 2134f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int mSelectionLeftPadding = 0; 2144f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 2154f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 2164f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The selection's top padding 2174f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 2184f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int mSelectionTopPadding = 0; 2194f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 2204f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 2214f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The selection's right padding 2224f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 2234f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int mSelectionRightPadding = 0; 2244f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 2254f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 2264f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The selection's bottom padding 2274f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 2284f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int mSelectionBottomPadding = 0; 2294f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 2304f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 2314f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * This view's padding 2324f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 2334f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project Rect mListPadding = new Rect(); 2344f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 2354f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 2364f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Subclasses must retain their measure spec from onMeasure() into this member 2374f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 2384f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int mWidthMeasureSpec = 0; 2394f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 2404f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 2414f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The top scroll indicator 2424f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 2434f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project View mScrollUp; 2444f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 2454f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 2464f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The down scroll indicator 2474f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 2484f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project View mScrollDown; 2494f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 2504f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 2514f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * When the view is scrolling, this flag is set to true to indicate subclasses that 2524f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * the drawing cache was enabled on the children 2534f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 2544f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project boolean mCachingStarted; 2554f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 2564f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 2574f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The position of the view that received the down motion event 2584f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 2594f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int mMotionPosition; 2604f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 2614f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 2624f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The offset to the top of the mMotionPosition view when the down motion event was received 2634f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 2644f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int mMotionViewOriginalTop; 2654f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 2664f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 2674f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The desired offset to the top of the mMotionPosition view after a scroll 2684f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 2694f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int mMotionViewNewTop; 2704f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 2714f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 2724f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The X value associated with the the down motion event 2734f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 2744f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int mMotionX; 2754f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 2764f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 2774f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The Y value associated with the the down motion event 2784f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 2794f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int mMotionY; 2804f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 2814f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 2824f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * One of TOUCH_MODE_REST, TOUCH_MODE_DOWN, TOUCH_MODE_TAP, TOUCH_MODE_SCROLL, or 2834f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * TOUCH_MODE_DONE_WAITING 2844f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 2854f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int mTouchMode = TOUCH_MODE_REST; 2864f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 2874f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 2884f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Y value from on the previous motion event (if any) 2894f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 2904f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int mLastY; 2914f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 2924f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 2934f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * How far the finger moved before we started scrolling 2944f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 2954f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int mMotionCorrection; 2964f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 2974f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 2984f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Determines speed during touch scrolling 2994f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 3004f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private VelocityTracker mVelocityTracker; 3014f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 3024f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 3034f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Handles one frame of a fling 3044f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 3054f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private FlingRunnable mFlingRunnable; 3064f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 3074f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 3084f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Handles scrolling between positions within the list. 3094f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 3104f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private PositionScroller mPositionScroller; 3114f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 3124f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 3134f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The offset in pixels form the top of the AdapterView to the top 3144f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * of the currently selected view. Used to save and restore state. 3154f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 3164f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int mSelectedTop = 0; 3174f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 3184f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 3194f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Indicates whether the list is stacked from the bottom edge or 3204f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * the top edge. 3214f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 3224f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project boolean mStackFromBottom; 3234f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 3244f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 3254f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * When set to true, the list automatically discards the children's 3264f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * bitmap cache after scrolling. 3274f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 3284f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project boolean mScrollingCacheEnabled; 3294f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 3304f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 3314f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Whether or not to enable the fast scroll feature on this list 3324f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 3334f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project boolean mFastScrollEnabled; 3344f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 3354f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 3364f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Optional callback to notify client when scroll position has changed 3374f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 3384f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private OnScrollListener mOnScrollListener; 3394f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 3404f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 3414f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Keeps track of our accessory window 3424f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 3434f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project PopupWindow mPopup; 3444f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 3454f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 3464f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Used with type filter window 3474f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 3484f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project EditText mTextFilter; 3494f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 3504f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 3514f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Indicates whether to use pixels-based or position-based scrollbar 3524f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * properties. 3534f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 3544f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private boolean mSmoothScrollbarEnabled = true; 3554f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 3564f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 3574f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Indicates that this view supports filtering 3584f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 3594f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private boolean mTextFilterEnabled; 3604f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 3614f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 3624f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Indicates that this view is currently displaying a filtered view of the data 3634f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 3644f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private boolean mFiltered; 3654f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 3664f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 3674f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Rectangle used for hit testing children 3684f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 3694f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private Rect mTouchFrame; 3704f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 3714f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 3724f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The position to resurrect the selected position to. 3734f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 3744f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int mResurrectToPosition = INVALID_POSITION; 3754f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 3764f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private ContextMenuInfo mContextMenuInfo = null; 3774f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 3784f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 3794f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Used to request a layout when we changed touch mode 3804f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 3814f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private static final int TOUCH_MODE_UNKNOWN = -1; 3824f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private static final int TOUCH_MODE_ON = 0; 3834f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private static final int TOUCH_MODE_OFF = 1; 3844f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 3854f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private int mLastTouchMode = TOUCH_MODE_UNKNOWN; 3864f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 3874f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private static final boolean PROFILE_SCROLLING = false; 3884f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private boolean mScrollProfilingStarted = false; 3894f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 3904f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private static final boolean PROFILE_FLINGING = false; 3914f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private boolean mFlingProfilingStarted = false; 3924f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 3934f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 3944f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The last CheckForLongPress runnable we posted, if any 3954f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 3964f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private CheckForLongPress mPendingCheckForLongPress; 3974f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 3984f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 3994f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The last CheckForTap runnable we posted, if any 4004f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 4014f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private Runnable mPendingCheckForTap; 4024f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 4034f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 4044f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The last CheckForKeyLongPress runnable we posted, if any 4054f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 4064f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private CheckForKeyLongPress mPendingCheckForKeyLongPress; 4074f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 4084f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 4094f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Acts upon click 4104f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 4114f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private AbsListView.PerformClick mPerformClick; 4124f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 4134f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 4144f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * This view is in transcript mode -- it shows the bottom of the list when the data 4154f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * changes 4164f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 4174f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private int mTranscriptMode; 4184f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 4194f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 4204f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Indicates that this list is always drawn on top of a solid, single-color, opaque 4214f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * background 4224f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 4234f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private int mCacheColorHint; 4244f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 4254f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 4264f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The select child's view (from the adapter's getView) is enabled. 4274f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 4284f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private boolean mIsChildViewEnabled; 4294f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 4304f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 4314f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The last scroll state reported to clients through {@link OnScrollListener}. 4324f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 4334f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private int mLastScrollState = OnScrollListener.SCROLL_STATE_IDLE; 4344f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 4354f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 4364f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Helper object that renders and controls the fast scroll thumb. 4374f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 4384f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private FastScroller mFastScroller; 4394f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 4404f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private boolean mGlobalLayoutListenerAddedFilter; 4414f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 4424f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private int mTouchSlop; 4434f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private float mDensityScale; 4444f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 4454f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private InputConnection mDefInputConnection; 4464f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private InputConnectionWrapper mPublicInputConnection; 4474f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 4484f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private Runnable mClearScrollingCache; 4494f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private int mMinimumVelocity; 4504f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private int mMaximumVelocity; 4514f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 4524f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final boolean[] mIsScrap = new boolean[1]; 4534f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 4544f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project // True when the popup should be hidden because of a call to 4554f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project // dispatchDisplayHint() 4564f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private boolean mPopupHidden; 4574f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 4584f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 4594f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * ID of the active pointer. This is used to retain consistency during 4604f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * drags/flings if multiple pointers are used. 4614f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 4624f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private int mActivePointerId = INVALID_POINTER; 4634f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 4644f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 4654f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Sentinel value for no current active pointer. 4664f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Used by {@link #mActivePointerId}. 4672bc2b792782b304b15d8c48b54916a9b3fa3a7acPaul Lind */ 4682bc2b792782b304b15d8c48b54916a9b3fa3a7acPaul Lind private static final int INVALID_POINTER = -1; 4692bc2b792782b304b15d8c48b54916a9b3fa3a7acPaul Lind 4704f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 4714f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Interface definition for a callback to be invoked when the list or grid 4724f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * has been scrolled. 4734f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 4744f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public interface OnScrollListener { 4754f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 4764f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 4774f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The view is not scrolling. Note navigating the list using the trackball counts as 4782bc2b792782b304b15d8c48b54916a9b3fa3a7acPaul Lind * being in the idle state since these transitions are not animated. 4792bc2b792782b304b15d8c48b54916a9b3fa3a7acPaul Lind */ 4802bc2b792782b304b15d8c48b54916a9b3fa3a7acPaul Lind public static int SCROLL_STATE_IDLE = 0; 4814f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 4824f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 4834f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The user is scrolling using touch, and their finger is still on the screen 4844f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 4854f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public static int SCROLL_STATE_TOUCH_SCROLL = 1; 4864f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 4874f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 4884f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The user had previously been scrolling using touch and had performed a fling. The 4894f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * animation is now coasting to a stop 4904f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 4914f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public static int SCROLL_STATE_FLING = 2; 4924f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 4934f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 4944f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Callback method to be invoked while the list view or grid view is being scrolled. If the 4954f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * view is being scrolled, this method will be called before the next frame of the scroll is 4964f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * rendered. In particular, it will be called before any calls to 4974f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * {@link Adapter#getView(int, View, ViewGroup)}. 4984f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 4994f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @param view The view whose scroll state is being reported 5004f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 5014f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @param scrollState The current scroll state. One of {@link #SCROLL_STATE_IDLE}, 5024f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * {@link #SCROLL_STATE_TOUCH_SCROLL} or {@link #SCROLL_STATE_IDLE}. 5034f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 5044f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public void onScrollStateChanged(AbsListView view, int scrollState); 5054f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 5064f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 5074f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Callback method to be invoked when the list or grid has been scrolled. This will be 5084f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * called after the scroll has completed 5094f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @param view The view whose scroll state is being reported 5104f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @param firstVisibleItem the index of the first visible cell (ignore if 5114f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * visibleItemCount == 0) 5124f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @param visibleItemCount the number of visible cells 5134f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @param totalItemCount the number of items in the list adaptor 5144f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 5154f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, 5164f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int totalItemCount); 5174f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 5184f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 5192bc2b792782b304b15d8c48b54916a9b3fa3a7acPaul Lind public AbsListView(Context context) { 5202bc2b792782b304b15d8c48b54916a9b3fa3a7acPaul Lind super(context); 5212bc2b792782b304b15d8c48b54916a9b3fa3a7acPaul Lind initAbsListView(); 5224f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 5234f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project setVerticalScrollBarEnabled(true); 5244f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project TypedArray a = context.obtainStyledAttributes(R.styleable.View); 5254f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project initializeScrollbars(a); 5264f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project a.recycle(); 5274f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 5284f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 5294f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public AbsListView(Context context, AttributeSet attrs) { 5304f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project this(context, attrs, com.android.internal.R.attr.absListViewStyle); 5314f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 5324f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 5334f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public AbsListView(Context context, AttributeSet attrs, int defStyle) { 5344f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project super(context, attrs, defStyle); 5354f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project initAbsListView(); 5364f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 5374f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project TypedArray a = context.obtainStyledAttributes(attrs, 5384f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project com.android.internal.R.styleable.AbsListView, defStyle, 0); 5394f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 5404f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project Drawable d = a.getDrawable(com.android.internal.R.styleable.AbsListView_listSelector); 5414f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (d != null) { 5424f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project setSelector(d); 5434f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 5444f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 5454f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mDrawSelectorOnTop = a.getBoolean( 5464f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project com.android.internal.R.styleable.AbsListView_drawSelectorOnTop, false); 5474f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 5484f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project boolean stackFromBottom = a.getBoolean(R.styleable.AbsListView_stackFromBottom, false); 5494f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project setStackFromBottom(stackFromBottom); 5504f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 5514f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project boolean scrollingCacheEnabled = a.getBoolean(R.styleable.AbsListView_scrollingCache, true); 5524f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project setScrollingCacheEnabled(scrollingCacheEnabled); 5534f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 5544f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project boolean useTextFilter = a.getBoolean(R.styleable.AbsListView_textFilterEnabled, false); 5554f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project setTextFilterEnabled(useTextFilter); 5564f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 5574f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int transcriptMode = a.getInt(R.styleable.AbsListView_transcriptMode, 5584f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project TRANSCRIPT_MODE_DISABLED); 5594f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project setTranscriptMode(transcriptMode); 5604f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 5614f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int color = a.getColor(R.styleable.AbsListView_cacheColorHint, 0); 5624f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project setCacheColorHint(color); 5634f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 5644f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project boolean enableFastScroll = a.getBoolean(R.styleable.AbsListView_fastScrollEnabled, false); 5654f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project setFastScrollEnabled(enableFastScroll); 5664f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 5674f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project boolean smoothScrollbar = a.getBoolean(R.styleable.AbsListView_smoothScrollbar, true); 5684f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project setSmoothScrollbarEnabled(smoothScrollbar); 5694f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 5704f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final int adapterId = a.getResourceId(R.styleable.AbsListView_adapter, 0); 5714f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (adapterId != 0) { 5724f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final Context c = context; 5734f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project post(new Runnable() { 5744f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public void run() { 5754f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project setAdapter(Adapters.loadAdapter(c, adapterId)); 5764f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 5774f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project }); 5784f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 5794f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 5804f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project a.recycle(); 5814f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 58296dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell 5834f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private void initAbsListView() { 5844f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project // Setting focusable in touch mode will set the focusable property to true 5854f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project setClickable(true); 5864f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project setFocusableInTouchMode(true); 5874f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project setWillNotDraw(false); 5884f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project setAlwaysDrawnWithCacheEnabled(false); 5894f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project setScrollingCacheEnabled(true); 5904f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 5914f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final ViewConfiguration configuration = ViewConfiguration.get(mContext); 5924f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mTouchSlop = configuration.getScaledTouchSlop(); 5934f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); 5944f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); 5954f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mDensityScale = getContext().getResources().getDisplayMetrics().density; 5964f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 5974f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 5984f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 5994f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Enables fast scrolling by letting the user quickly scroll through lists by 6004f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * dragging the fast scroll thumb. The adapter attached to the list may want 6014f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * to implement {@link SectionIndexer} if it wishes to display alphabet preview and 6024f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * jump between sections of the list. 6034f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @see SectionIndexer 6044f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @see #isFastScrollEnabled() 6052bc2b792782b304b15d8c48b54916a9b3fa3a7acPaul Lind * @param enabled whether or not to enable fast scrolling 6062bc2b792782b304b15d8c48b54916a9b3fa3a7acPaul Lind */ 6072bc2b792782b304b15d8c48b54916a9b3fa3a7acPaul Lind public void setFastScrollEnabled(boolean enabled) { 6082bc2b792782b304b15d8c48b54916a9b3fa3a7acPaul Lind mFastScrollEnabled = enabled; 6094f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (enabled) { 6104f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (mFastScroller == null) { 6114f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mFastScroller = new FastScroller(getContext(), this); 6124f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 6134f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } else { 6144f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (mFastScroller != null) { 6154f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mFastScroller.stop(); 6164f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mFastScroller = null; 6174f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 6184f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 6194f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 6204f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 6214f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 6224f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Returns the current state of the fast scroll feature. 6234f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @see #setFastScrollEnabled(boolean) 6244f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @return true if fast scroll is enabled, false otherwise 6254f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 6264f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project @ViewDebug.ExportedProperty 6272bc2b792782b304b15d8c48b54916a9b3fa3a7acPaul Lind public boolean isFastScrollEnabled() { 6282bc2b792782b304b15d8c48b54916a9b3fa3a7acPaul Lind return mFastScrollEnabled; 6292bc2b792782b304b15d8c48b54916a9b3fa3a7acPaul Lind } 6302bc2b792782b304b15d8c48b54916a9b3fa3a7acPaul Lind 6314f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 6324f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * If fast scroll is visible, then don't draw the vertical scrollbar. 6334f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @hide 6344f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 6354f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project @Override 6364f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project protected boolean isVerticalScrollBarHidden() { 6374f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return mFastScroller != null && mFastScroller.isVisible(); 6384f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 6394f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 6404f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 6414f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * When smooth scrollbar is enabled, the position and size of the scrollbar thumb 6424f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * is computed based on the number of visible pixels in the visible items. This 6434f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * however assumes that all list items have the same height. If you use a list in 6444f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * which items have different heights, the scrollbar will change appearance as the 6454f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * user scrolls through the list. To avoid this issue, you need to disable this 6464f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * property. 6474f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 6484f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * When smooth scrollbar is disabled, the position and size of the scrollbar thumb 6494f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * is based solely on the number of items in the adapter and the position of the 6504f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * visible items inside the adapter. This provides a stable scrollbar as the user 6514f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * navigates through a list of items with varying heights. 6524f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 6534f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @param enabled Whether or not to enable smooth scrollbar. 6544f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 6554f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @see #setSmoothScrollbarEnabled(boolean) 6564f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @attr ref android.R.styleable#AbsListView_smoothScrollbar 6574f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 6584f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public void setSmoothScrollbarEnabled(boolean enabled) { 6594f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mSmoothScrollbarEnabled = enabled; 6604f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 6614f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 6624f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 6634f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Returns the current state of the fast scroll feature. 6644f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 6654f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @return True if smooth scrollbar is enabled is enabled, false otherwise. 6664f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 6674f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @see #setSmoothScrollbarEnabled(boolean) 6684f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 6694f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project @ViewDebug.ExportedProperty 6704f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public boolean isSmoothScrollbarEnabled() { 6714f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return mSmoothScrollbarEnabled; 6724f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 6734f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 6744f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 6754f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Set the listener that will receive notifications every time the list scrolls. 6764f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 6774f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @param l the scroll listener 6784f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 6794f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public void setOnScrollListener(OnScrollListener l) { 6804f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mOnScrollListener = l; 6814f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project invokeOnItemScrollListener(); 6824f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 6834f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 6844f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 6854f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Notify our scroll listener (if there is one) of a change in scroll state 6864f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 6874f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project void invokeOnItemScrollListener() { 6884f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (mFastScroller != null) { 6894f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mFastScroller.onScroll(this, mFirstPosition, getChildCount(), mItemCount); 6904f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 6914f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (mOnScrollListener != null) { 6924f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mOnScrollListener.onScroll(this, mFirstPosition, getChildCount(), mItemCount); 6934f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 6944f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 6954f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 6964f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 6974f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Indicates whether the children's drawing cache is used during a scroll. 6984f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * By default, the drawing cache is enabled but this will consume more memory. 6994f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 7004f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @return true if the scrolling cache is enabled, false otherwise 7014f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 7024f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @see #setScrollingCacheEnabled(boolean) 7034f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @see View#setDrawingCacheEnabled(boolean) 7044f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 7054f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project @ViewDebug.ExportedProperty 7064f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public boolean isScrollingCacheEnabled() { 7074f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return mScrollingCacheEnabled; 7084f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 7094f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 7104f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 7114f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Enables or disables the children's drawing cache during a scroll. 7124f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * By default, the drawing cache is enabled but this will use more memory. 7134f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 7144f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * When the scrolling cache is enabled, the caches are kept after the 7154f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * first scrolling. You can manually clear the cache by calling 7164f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * {@link android.view.ViewGroup#setChildrenDrawingCacheEnabled(boolean)}. 7174f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 7184f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @param enabled true to enable the scroll cache, false otherwise 7194f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 7204f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @see #isScrollingCacheEnabled() 7214f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @see View#setDrawingCacheEnabled(boolean) 7224f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 7234f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public void setScrollingCacheEnabled(boolean enabled) { 7244f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (mScrollingCacheEnabled && !enabled) { 7254f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project clearScrollingCache(); 7264f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 7274f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mScrollingCacheEnabled = enabled; 7284f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 7294f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 7304f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 7314f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Enables or disables the type filter window. If enabled, typing when 7324f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * this view has focus will filter the children to match the users input. 7334f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Note that the {@link Adapter} used by this view must implement the 7344f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * {@link Filterable} interface. 7354f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 7364f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @param textFilterEnabled true to enable type filtering, false otherwise 7374f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 7384f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @see Filterable 7394f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 7404f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public void setTextFilterEnabled(boolean textFilterEnabled) { 7414f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mTextFilterEnabled = textFilterEnabled; 7424f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 7434f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 7444f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 7454f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Indicates whether type filtering is enabled for this view 7464f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 7474f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @return true if type filtering is enabled, false otherwise 7484f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 7494f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @see #setTextFilterEnabled(boolean) 7504f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @see Filterable 7514f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 7524f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project @ViewDebug.ExportedProperty 7534f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public boolean isTextFilterEnabled() { 7544f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return mTextFilterEnabled; 7554f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 7564f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 7574f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project @Override 7584f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public void getFocusedRect(Rect r) { 7594f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project View view = getSelectedView(); 7604f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (view != null && view.getParent() == this) { 7614f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project // the focused rectangle of the selected view offset into the 7624f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project // coordinate space of this view. 7634f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project view.getFocusedRect(r); 7644f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project offsetDescendantRectToMyCoords(view, r); 7654f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } else { 7664f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project // otherwise, just the norm 7674f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project super.getFocusedRect(r); 7684f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 7694f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 7704f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 7714f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private void useDefaultSelector() { 7724f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project setSelector(getResources().getDrawable( 7734f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project com.android.internal.R.drawable.list_selector_background)); 7744f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 7754f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 7764f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 7774f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Indicates whether the content of this view is pinned to, or stacked from, 7784f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * the bottom edge. 7794f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 7804f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @return true if the content is stacked from the bottom edge, false otherwise 7814f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 7824f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project @ViewDebug.ExportedProperty 7834f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public boolean isStackFromBottom() { 7844f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return mStackFromBottom; 7854f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 7864f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 7874f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 7884f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * When stack from bottom is set to true, the list fills its content starting from 7894f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * the bottom of the view. 7904f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 7914f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @param stackFromBottom true to pin the view's content to the bottom edge, 7924f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * false to pin the view's content to the top edge 7934f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 7944f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public void setStackFromBottom(boolean stackFromBottom) { 7954f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (mStackFromBottom != stackFromBottom) { 7964f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mStackFromBottom = stackFromBottom; 7974f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project requestLayoutIfNecessary(); 79801dda204cd28fe181691b4a44a51be7e5666d0c8Steve Block } 7994f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 8004f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 8014f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project void requestLayoutIfNecessary() { 8024f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (getChildCount() > 0) { 8034f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project resetList(); 8044f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project requestLayout(); 8054f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project invalidate(); 8064f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 8074f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 8084f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 8094f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project static class SavedState extends BaseSavedState { 8104f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project long selectedId; 8114f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project long firstId; 8124f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int viewTop; 8134f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int position; 8144f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int height; 8154f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project String filter; 8164f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 8174f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 8184f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Constructor called from {@link AbsListView#onSaveInstanceState()} 8194f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 8204f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project SavedState(Parcelable superState) { 8214f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project super(superState); 8224f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 8234f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 8244f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 8254f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Constructor called from {@link #CREATOR} 8264f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 8274f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project private SavedState(Parcel in) { 8284f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project super(in); 8294f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project selectedId = in.readLong(); 8304f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project firstId = in.readLong(); 8314f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project viewTop = in.readInt(); 8324f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project position = in.readInt(); 8334f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project height = in.readInt(); 8344f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project filter = in.readString(); 8354f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 8364f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 8374f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project @Override 8384f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public void writeToParcel(Parcel out, int flags) { 8394f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project super.writeToParcel(out, flags); 8404f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project out.writeLong(selectedId); 8414f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project out.writeLong(firstId); 8424f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project out.writeInt(viewTop); 8434f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project out.writeInt(position); 8444f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project out.writeInt(height); 8454f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project out.writeString(filter); 8464f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 8474f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 8484f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project @Override 8494f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public String toString() { 8504f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return "AbsListView.SavedState{" 8514f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project + Integer.toHexString(System.identityHashCode(this)) 8524f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project + " selectedId=" + selectedId 8534f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project + " firstId=" + firstId 8544f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project + " viewTop=" + viewTop 8554f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project + " position=" + position 8564f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project + " height=" + height 8574f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project + " filter=" + filter + "}"; 8584f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 8594f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 8604f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public static final Parcelable.Creator<SavedState> CREATOR 8614f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project = new Parcelable.Creator<SavedState>() { 8624f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public SavedState createFromParcel(Parcel in) { 8634f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return new SavedState(in); 8644f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 8654f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 8664f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public SavedState[] newArray(int size) { 8674f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return new SavedState[size]; 8684f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 8694f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project }; 8704f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 8714f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 8724f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project @Override 8734f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public Parcelable onSaveInstanceState() { 8744f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /* 8754f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * This doesn't really make sense as the place to dismiss the 8764f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * popups, but there don't seem to be any other useful hooks 8774f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * that happen early enough to keep from getting complaints 8784f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * about having leaked the window. 8794f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 8804f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project dismissPopup(); 8814f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 8824f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project Parcelable superState = super.onSaveInstanceState(); 8834f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 8844f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project SavedState ss = new SavedState(superState); 8854f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 8864f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project boolean haveChildren = getChildCount() > 0; 8874f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project long selectedId = getSelectedItemId(); 8884f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project ss.selectedId = selectedId; 8894f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project ss.height = getHeight(); 8904f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 89196dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell if (selectedId >= 0) { 89296dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell // Remember the selection 89396dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell ss.viewTop = mSelectedTop; 89496dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell ss.position = getSelectedItemPosition(); 89596dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell ss.firstId = INVALID_POSITION; 89696dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell } else { 89796dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell if (haveChildren) { 89896dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell // Remember the position of the first child 89996dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell View v = getChildAt(0); 90096dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell ss.viewTop = v.getTop(); 90196dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell ss.position = mFirstPosition; 90296dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell ss.firstId = mAdapter.getItemId(mFirstPosition); 90396dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell } else { 90496dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell ss.viewTop = 0; 90596dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell ss.firstId = INVALID_POSITION; 90696dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell ss.position = 0; 90796dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell } 90896dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell } 90996dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell 91096dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell ss.filter = null; 91196dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell if (mFiltered) { 91296dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell final EditText textFilter = mTextFilter; 91396dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell if (textFilter != null) { 91496dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell Editable filterText = textFilter.getText(); 91596dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell if (filterText != null) { 91696dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell ss.filter = filterText.toString(); 91796dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell } 91896dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell } 91996dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell } 92096dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell 92196dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell return ss; 92296dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell } 92396dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell 92496dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell @Override 92596dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell public void onRestoreInstanceState(Parcelable state) { 92696dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell SavedState ss = (SavedState) state; 92796dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell 92896dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell super.onRestoreInstanceState(ss.getSuperState()); 92996dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell mDataChanged = true; 93096dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell 93196dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell mSyncHeight = ss.height; 93296dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell 93396dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell if (ss.selectedId >= 0) { 93496dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell mNeedSync = true; 93596dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell mSyncRowId = ss.selectedId; 93696dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell mSyncPosition = ss.position; 93796dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell mSpecificTop = ss.viewTop; 93896dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell mSyncMode = SYNC_SELECTED_POSITION; 93996dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell } else if (ss.firstId >= 0) { 94096dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell setSelectedPositionInt(INVALID_POSITION); 94196dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell // Do this before setting mNeedSync since setNextSelectedPosition looks at mNeedSync 94296dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell setNextSelectedPositionInt(INVALID_POSITION); 94396dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell mNeedSync = true; 94496dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell mSyncRowId = ss.firstId; 94596dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell mSyncPosition = ss.position; 94696dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell mSpecificTop = ss.viewTop; 94796dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell mSyncMode = SYNC_FIRST_POSITION; 94896dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell } 94996dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell 95096dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell setFilterText(ss.filter); 95196dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell 95296dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell requestLayout(); 95396dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell } 95496dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell 95596dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell private boolean acceptFilter() { 95696dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell return mTextFilterEnabled && getAdapter() instanceof Filterable && 95796dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell ((Filterable) getAdapter()).getFilter() != null; 95896dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell } 95996dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell 96096dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell /** 96196dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell * Sets the initial value for the text filter. 96296dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell * @param filterText The text to use for the filter. 96396dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell * 96496dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell * @see #setTextFilterEnabled 96596dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell */ 96696dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell public void setFilterText(String filterText) { 96796dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell // TODO: Should we check for acceptFilter()? 96896dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell if (mTextFilterEnabled && !TextUtils.isEmpty(filterText)) { 96996dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell createTextFilter(false); 97096dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell // This is going to call our listener onTextChanged, but we might not 97196dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell // be ready to bring up a window yet 97296dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell mTextFilter.setText(filterText); 97396dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell mTextFilter.setSelection(filterText.length()); 97496dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell if (mAdapter instanceof Filterable) { 97596dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell // if mPopup is non-null, then onTextChanged will do the filtering 97696dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell if (mPopup == null) { 97796dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell Filter f = ((Filterable) mAdapter).getFilter(); 97896dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell f.filter(filterText); 97996dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell } 98096dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell // Set filtered to true so we will display the filter window when our main 98196dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell // window is ready 98296dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell mFiltered = true; 98396dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell mDataSetObserver.clearSavedState(); 98496dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell } 98596dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell } 98696dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell } 98796dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell 98896dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell /** 98996dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell * Returns the list's text filter, if available. 99096dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell * @return the list's text filter or null if filtering isn't enabled 9914f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 9924f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public CharSequence getTextFilter() { 9934f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (mTextFilterEnabled && mTextFilter != null) { 9944f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return mTextFilter.getText(); 9954f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 9964f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return null; 9974f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 9984f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 9994f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project @Override 10004f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) { 10014f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); 10024f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (gainFocus && mSelectedPosition < 0 && !isInTouchMode()) { 10034f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project resurrectSelection(); 10044f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 10054f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 10064f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 10074f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project @Override 10084f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public void requestLayout() { 10094f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (!mBlockLayoutRequests && !mInLayout) { 10104f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project super.requestLayout(); 10114f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 10124f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 10134f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 10144f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 10154f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * The list is empty. Clear everything out. 10164f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 10174f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project void resetList() { 10184f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project removeAllViewsInLayout(); 10194f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mFirstPosition = 0; 10204f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mDataChanged = false; 10214f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mNeedSync = false; 10224f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mOldSelectedPosition = INVALID_POSITION; 10234f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mOldSelectedRowId = INVALID_ROW_ID; 10244f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project setSelectedPositionInt(INVALID_POSITION); 10254f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project setNextSelectedPositionInt(INVALID_POSITION); 10264f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mSelectedTop = 0; 10274f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mSelectorRect.setEmpty(); 10284f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project invalidate(); 10294f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 10304f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 10314f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project @Override 10324f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project protected int computeVerticalScrollExtent() { 10334f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final int count = getChildCount(); 10344f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (count > 0) { 10354f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (mSmoothScrollbarEnabled) { 10364f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int extent = count * 100; 10374f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 10384f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project View view = getChildAt(0); 10394f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final int top = view.getTop(); 10404f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int height = view.getHeight(); 10414f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (height > 0) { 10424f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project extent += (top * 100) / height; 10434f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 10444f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 10454f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project view = getChildAt(count - 1); 10464f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final int bottom = view.getBottom(); 10474f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project height = view.getHeight(); 10484f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (height > 0) { 10494f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project extent -= ((bottom - getHeight()) * 100) / height; 10504f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 10514f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 10524f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return extent; 10534f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } else { 10544f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return 1; 10554f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 10564f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 10574f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return 0; 10584f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 10594f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 10604f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project @Override 10614f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project protected int computeVerticalScrollOffset() { 10624f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final int firstPosition = mFirstPosition; 10634f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final int childCount = getChildCount(); 10644f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (firstPosition >= 0 && childCount > 0) { 10654f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (mSmoothScrollbarEnabled) { 10664f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final View view = getChildAt(0); 10674f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final int top = view.getTop(); 10684f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int height = view.getHeight(); 10694f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (height > 0) { 10704f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return Math.max(firstPosition * 100 - (top * 100) / height + 10714f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project (int)((float)mScrollY / getHeight() * mItemCount * 100), 0); 10724f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 10734f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } else { 10744f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int index; 10754f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final int count = mItemCount; 10764f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (firstPosition == 0) { 10774f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project index = 0; 107896dbb4fc58fe2dcf4390e073dbb42cc77ef2f0b5Martyn Capewell } else if (firstPosition + childCount == count) { 10794f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project index = count; 10804f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } else { 10814f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project index = firstPosition + childCount / 2; 10824f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 10834f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return (int) (firstPosition + childCount * (index / (float) count)); 10844f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 10854f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 10864f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return 0; 10874f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 10884f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 10894f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project @Override 10904f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project protected int computeVerticalScrollRange() { 10914f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int result; 10924f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (mSmoothScrollbarEnabled) { 10934f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project result = Math.max(mItemCount * 100, 0); 10944f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } else { 10954f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project result = mItemCount; 10964f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 10974f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return result; 10982bc2b792782b304b15d8c48b54916a9b3fa3a7acPaul Lind } 10994f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 11004f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project @Override 11014f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project protected float getTopFadingEdgeStrength() { 11024f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final int count = getChildCount(); 11034f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final float fadeEdge = super.getTopFadingEdgeStrength(); 11044f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (count == 0) { 11054f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return fadeEdge; 11064f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } else { 11074f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (mFirstPosition > 0) { 11084f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return 1.0f; 11094f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 11104f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 11114f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final int top = getChildAt(0).getTop(); 11124f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final float fadeLength = (float) getVerticalFadingEdgeLength(); 11134f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return top < mPaddingTop ? (float) -(top - mPaddingTop) / fadeLength : fadeEdge; 11144f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 11154f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 11164f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 11174f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project @Override 11184f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project protected float getBottomFadingEdgeStrength() { 11194f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final int count = getChildCount(); 11204f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final float fadeEdge = super.getBottomFadingEdgeStrength(); 11214f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (count == 0) { 11224f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return fadeEdge; 11234f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } else { 11244f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (mFirstPosition + count - 1 < mItemCount - 1) { 112535237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project return 1.0f; 112635237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project } 112735237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project 11284f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final int bottom = getChildAt(count - 1).getBottom(); 11294f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final int height = getHeight(); 11304f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final float fadeLength = (float) getVerticalFadingEdgeLength(); 11314f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return bottom > height - mPaddingBottom ? 11324f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project (float) (bottom - height + mPaddingBottom) / fadeLength : fadeEdge; 11334f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 11344f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 11354f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 11364f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project @Override 11374f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 11384f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (mSelector == null) { 11394f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project useDefaultSelector(); 11404f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 11414f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final Rect listPadding = mListPadding; 11424f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project listPadding.left = mSelectionLeftPadding + mPaddingLeft; 11434f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project listPadding.top = mSelectionTopPadding + mPaddingTop; 11444f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project listPadding.right = mSelectionRightPadding + mPaddingRight; 11454f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project listPadding.bottom = mSelectionBottomPadding + mPaddingBottom; 11464f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 11474f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 11484f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 11494f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Subclasses should NOT override this method but 11504f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * {@link #layoutChildren()} instead. 11514f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 11524f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project @Override 11534f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project protected void onLayout(boolean changed, int l, int t, int r, int b) { 11544f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project super.onLayout(changed, l, t, r, b); 11554f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mInLayout = true; 11564f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (changed) { 11574f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int childCount = getChildCount(); 11584f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project for (int i = 0; i < childCount; i++) { 11594f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project getChildAt(i).forceLayout(); 11604f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 11614f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mRecycler.markChildrenDirty(); 11624f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 11634f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 11644f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project layoutChildren(); 11654f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mInLayout = false; 11664f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 11674f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 11684f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 11694f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @hide 11704f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 11714f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project @Override 11724f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project protected boolean setFrame(int left, int top, int right, int bottom) { 11734f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final boolean changed = super.setFrame(left, top, right, bottom); 11744f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 11754f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (changed) { 11764f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project // Reposition the popup when the frame has changed. This includes 11774f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project // translating the widget, not just changing its dimension. The 11784f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project // filter popup needs to follow the widget. 11794f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final boolean visible = getWindowVisibility() == View.VISIBLE; 11804f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (mFiltered && visible && mPopup != null && mPopup.isShowing()) { 11814f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project positionPopup(); 11824f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 11834f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 11844f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 11854f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return changed; 11864f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 11874f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 11884f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 11894f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Subclasses must override this method to layout their children. 11904f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 11914f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project protected void layoutChildren() { 11924f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 11934f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 11944f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project void updateScrollIndicators() { 11954f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (mScrollUp != null) { 11964f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project boolean canScrollUp; 11974f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project // 0th element is not visible 11984f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project canScrollUp = mFirstPosition > 0; 11994f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 12004f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project // ... Or top of 0th element is not visible 12014f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (!canScrollUp) { 12024f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (getChildCount() > 0) { 12034f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project View child = getChildAt(0); 12044f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project canScrollUp = child.getTop() < mListPadding.top; 12054f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 12064f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 12074f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 12084f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mScrollUp.setVisibility(canScrollUp ? View.VISIBLE : View.INVISIBLE); 12094f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 12104f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 12114f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (mScrollDown != null) { 12124f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project boolean canScrollDown; 12134f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project int count = getChildCount(); 12144f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 12154f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project // Last item is not visible 12164f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project canScrollDown = (mFirstPosition + count) < mItemCount; 12174f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 12184f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project // ... Or bottom of the last element is not visible 12194f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (!canScrollDown && count > 0) { 12204f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project View child = getChildAt(count - 1); 12214f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project canScrollDown = child.getBottom() > mBottom - mListPadding.bottom; 12224f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 12234f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 12244f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mScrollDown.setVisibility(canScrollDown ? View.VISIBLE : View.INVISIBLE); 12254f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 12264f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 12274f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 12284f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project @Override 12294f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project @ViewDebug.ExportedProperty 12304f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public View getSelectedView() { 12314f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (mItemCount > 0 && mSelectedPosition >= 0) { 12324f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return getChildAt(mSelectedPosition - mFirstPosition); 12334f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } else { 12344f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return null; 12354f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 12364f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 12374f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 12384f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 12394f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * List padding is the maximum of the normal view's padding and the padding of the selector. 12404f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 12414f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @see android.view.View#getPaddingTop() 12424f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @see #getSelector() 12434f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 12444f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @return The top list padding. 12454f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 12464f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public int getListPaddingTop() { 12474f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return mListPadding.top; 12484f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 12494f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 12504f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 12514f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * List padding is the maximum of the normal view's padding and the padding of the selector. 12524f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 12534f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @see android.view.View#getPaddingBottom() 12544f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @see #getSelector() 12554f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 12564f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @return The bottom list padding. 12574f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 12584f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public int getListPaddingBottom() { 12594f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return mListPadding.bottom; 12604f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 12614f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 12624f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 12634f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * List padding is the maximum of the normal view's padding and the padding of the selector. 12644f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 12654f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @see android.view.View#getPaddingLeft() 12664f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @see #getSelector() 12674f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 12684f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @return The left list padding. 12694f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 12704f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public int getListPaddingLeft() { 12714f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return mListPadding.left; 12724f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 12734f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 12744f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 12754f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * List padding is the maximum of the normal view's padding and the padding of the selector. 12764f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 12774f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @see android.view.View#getPaddingRight() 12784f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @see #getSelector() 12794f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 12804f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @return The right list padding. 12814f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 12824f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project public int getListPaddingRight() { 12834f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project return mListPadding.right; 12844f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 12854f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 12864f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project /** 12874f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * Get a view and have it show the data associated with the specified 12884f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * position. This is called when we have already discovered that the view is 12894f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * not available for reuse in the recycle bin. The only choices left are 12904f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * converting an old view or making a new one. 12914f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 12924f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @param position The position to display 12934f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @param isScrap Array of at least 1 boolean, the first entry will become true if 12944f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * the returned view was taken from the scrap heap, false if otherwise. 12954f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * 12964f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project * @return A view displaying the data associated with the specified position 12974f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project */ 12984f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project View obtainView(int position, boolean[] isScrap) { 12994f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project isScrap[0] = false; 13004f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project View scrapView; 13014f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 13024f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project scrapView = mRecycler.getScrapView(position); 13034f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 13044f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project View child; 13054f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (scrapView != null) { 13064f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (ViewDebug.TRACE_RECYCLER) { 13074f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project ViewDebug.trace(scrapView, ViewDebug.RecyclerTraceType.RECYCLE_FROM_SCRAP_HEAP, 13084f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project position, -1); 13094f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 13104f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 13114f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project child = mAdapter.getView(position, scrapView, this); 13124f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 13134f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (ViewDebug.TRACE_RECYCLER) { 13144f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project ViewDebug.trace(child, ViewDebug.RecyclerTraceType.BIND_VIEW, 13154f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project position, getChildCount()); 13164f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 13174f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project 13184f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (child != scrapView) { 13194f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project mRecycler.addScrapView(scrapView); 13204f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (mCacheColorHint != 0) { 13214f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project child.setDrawingCacheBackgroundColor(mCacheColorHint); 13224f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 13234f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project if (ViewDebug.TRACE_RECYCLER) { 13244f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project ViewDebug.trace(scrapView, ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP, 13254f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project position, -1); 13264f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } 13274f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project } else { 13284f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project isScrap[0] = true; 13294f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project child.dispatchFinishTemporaryDetach(); 133035237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project } 133135237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project } else { 133235237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project child = mAdapter.getView(position, null, this); 133335237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project if (mCacheColorHint != 0) { 133435237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project child.setDrawingCacheBackgroundColor(mCacheColorHint); 133535237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project } 133635237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project if (ViewDebug.TRACE_RECYCLER) { 133735237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project ViewDebug.trace(child, ViewDebug.RecyclerTraceType.NEW_VIEW, 133835237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project position, getChildCount()); 133935237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project } 134035237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project } 134135237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project 134235237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project return child; 134335237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project } 134435237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project 134535237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project void positionSelector(View sel) { 134635237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project final Rect selectorRect = mSelectorRect; 134735237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project selectorRect.set(sel.getLeft(), sel.getTop(), sel.getRight(), sel.getBottom()); 134835237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project positionSelector(selectorRect.left, selectorRect.top, selectorRect.right, 134935237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project selectorRect.bottom); 135035237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project 135135237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project final boolean isChildViewEnabled = mIsChildViewEnabled; 135235237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project if (sel.isEnabled() != isChildViewEnabled) { 135335237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project mIsChildViewEnabled = !isChildViewEnabled; 135435237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project refreshDrawableState(); 135535237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project } 135635237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project } 135735237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project 135835237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project private void positionSelector(int l, int t, int r, int b) { 135935237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project mSelectorRect.set(l - mSelectionLeftPadding, t - mSelectionTopPadding, r 136035237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project + mSelectionRightPadding, b + mSelectionBottomPadding); 136135237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project } 136235237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project 136335237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project @Override 136435237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project protected void dispatchDraw(Canvas canvas) { 136535237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project int saveCount = 0; 136635237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project final boolean clipToPadding = (mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK; 136735237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project if (clipToPadding) { 136835237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project saveCount = canvas.save(); 136935237d135807af84bf9b0e5b8d7f8633e58db6f5The Android Open Source Project final int scrollX = mScrollX; 13704f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project final int scrollY = mScrollY; 13714f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project canvas.clipRect(scrollX + mPaddingLeft, scrollY + mPaddingTop, 13724f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project scrollX + mRight - mLeft - mPaddingRight, 13734f6e8d7a00cbeda1e70cc15be9c4af1018bdad5The Android Open Source Project scrollY + mBottom - mTop - mPaddingBottom); 1374 mGroupFlags &= ~CLIP_TO_PADDING_MASK; 1375 } 1376 1377 final boolean drawSelectorOnTop = mDrawSelectorOnTop; 1378 if (!drawSelectorOnTop) { 1379 drawSelector(canvas); 1380 } 1381 1382 super.dispatchDraw(canvas); 1383 1384 if (drawSelectorOnTop) { 1385 drawSelector(canvas); 1386 } 1387 1388 if (clipToPadding) { 1389 canvas.restoreToCount(saveCount); 1390 mGroupFlags |= CLIP_TO_PADDING_MASK; 1391 } 1392 } 1393 1394 @Override 1395 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 1396 if (getChildCount() > 0) { 1397 mDataChanged = true; 1398 rememberSyncState(); 1399 } 1400 1401 if (mFastScroller != null) { 1402 mFastScroller.onSizeChanged(w, h, oldw, oldh); 1403 } 1404 } 1405 1406 /** 1407 * @return True if the current touch mode requires that we draw the selector in the pressed 1408 * state. 1409 */ 1410 boolean touchModeDrawsInPressedState() { 1411 // FIXME use isPressed for this 1412 switch (mTouchMode) { 1413 case TOUCH_MODE_TAP: 1414 case TOUCH_MODE_DONE_WAITING: 1415 return true; 1416 default: 1417 return false; 1418 } 1419 } 1420 1421 /** 1422 * Indicates whether this view is in a state where the selector should be drawn. This will 1423 * happen if we have focus but are not in touch mode, or we are in the middle of displaying 1424 * the pressed state for an item. 1425 * 1426 * @return True if the selector should be shown 1427 */ 1428 boolean shouldShowSelector() { 1429 return (hasFocus() && !isInTouchMode()) || touchModeDrawsInPressedState(); 1430 } 1431 1432 private void drawSelector(Canvas canvas) { 1433 if (shouldShowSelector() && mSelectorRect != null && !mSelectorRect.isEmpty()) { 1434 final Drawable selector = mSelector; 1435 selector.setBounds(mSelectorRect); 1436 selector.draw(canvas); 1437 } 1438 } 1439 1440 /** 1441 * Controls whether the selection highlight drawable should be drawn on top of the item or 1442 * behind it. 1443 * 1444 * @param onTop If true, the selector will be drawn on the item it is highlighting. The default 1445 * is false. 1446 * 1447 * @attr ref android.R.styleable#AbsListView_drawSelectorOnTop 1448 */ 1449 public void setDrawSelectorOnTop(boolean onTop) { 1450 mDrawSelectorOnTop = onTop; 1451 } 1452 1453 /** 1454 * Set a Drawable that should be used to highlight the currently selected item. 1455 * 1456 * @param resID A Drawable resource to use as the selection highlight. 1457 * 1458 * @attr ref android.R.styleable#AbsListView_listSelector 1459 */ 1460 public void setSelector(int resID) { 1461 setSelector(getResources().getDrawable(resID)); 1462 } 1463 1464 public void setSelector(Drawable sel) { 1465 if (mSelector != null) { 1466 mSelector.setCallback(null); 1467 unscheduleDrawable(mSelector); 1468 } 1469 mSelector = sel; 1470 Rect padding = new Rect(); 1471 sel.getPadding(padding); 1472 mSelectionLeftPadding = padding.left; 1473 mSelectionTopPadding = padding.top; 1474 mSelectionRightPadding = padding.right; 1475 mSelectionBottomPadding = padding.bottom; 1476 sel.setCallback(this); 1477 sel.setState(getDrawableState()); 1478 } 1479 1480 /** 1481 * Returns the selector {@link android.graphics.drawable.Drawable} that is used to draw the 1482 * selection in the list. 1483 * 1484 * @return the drawable used to display the selector 1485 */ 1486 public Drawable getSelector() { 1487 return mSelector; 1488 } 1489 1490 /** 1491 * Sets the selector state to "pressed" and posts a CheckForKeyLongPress to see if 1492 * this is a long press. 1493 */ 1494 void keyPressed() { 1495 if (!isEnabled() || !isClickable()) { 1496 return; 1497 } 1498 1499 Drawable selector = mSelector; 1500 Rect selectorRect = mSelectorRect; 1501 if (selector != null && (isFocused() || touchModeDrawsInPressedState()) 1502 && selectorRect != null && !selectorRect.isEmpty()) { 1503 1504 final View v = getChildAt(mSelectedPosition - mFirstPosition); 1505 1506 if (v != null) { 1507 if (v.hasFocusable()) return; 1508 v.setPressed(true); 1509 } 1510 setPressed(true); 1511 1512 final boolean longClickable = isLongClickable(); 1513 Drawable d = selector.getCurrent(); 1514 if (d != null && d instanceof TransitionDrawable) { 1515 if (longClickable) { 1516 ((TransitionDrawable) d).startTransition( 1517 ViewConfiguration.getLongPressTimeout()); 1518 } else { 1519 ((TransitionDrawable) d).resetTransition(); 1520 } 1521 } 1522 if (longClickable && !mDataChanged) { 1523 if (mPendingCheckForKeyLongPress == null) { 1524 mPendingCheckForKeyLongPress = new CheckForKeyLongPress(); 1525 } 1526 mPendingCheckForKeyLongPress.rememberWindowAttachCount(); 1527 postDelayed(mPendingCheckForKeyLongPress, ViewConfiguration.getLongPressTimeout()); 1528 } 1529 } 1530 } 1531 1532 public void setScrollIndicators(View up, View down) { 1533 mScrollUp = up; 1534 mScrollDown = down; 1535 } 1536 1537 @Override 1538 protected void drawableStateChanged() { 1539 super.drawableStateChanged(); 1540 if (mSelector != null) { 1541 mSelector.setState(getDrawableState()); 1542 } 1543 } 1544 1545 @Override 1546 protected int[] onCreateDrawableState(int extraSpace) { 1547 // If the child view is enabled then do the default behavior. 1548 if (mIsChildViewEnabled) { 1549 // Common case 1550 return super.onCreateDrawableState(extraSpace); 1551 } 1552 1553 // The selector uses this View's drawable state. The selected child view 1554 // is disabled, so we need to remove the enabled state from the drawable 1555 // states. 1556 final int enabledState = ENABLED_STATE_SET[0]; 1557 1558 // If we don't have any extra space, it will return one of the static state arrays, 1559 // and clearing the enabled state on those arrays is a bad thing! If we specify 1560 // we need extra space, it will create+copy into a new array that safely mutable. 1561 int[] state = super.onCreateDrawableState(extraSpace + 1); 1562 int enabledPos = -1; 1563 for (int i = state.length - 1; i >= 0; i--) { 1564 if (state[i] == enabledState) { 1565 enabledPos = i; 1566 break; 1567 } 1568 } 1569 1570 // Remove the enabled state 1571 if (enabledPos >= 0) { 1572 System.arraycopy(state, enabledPos + 1, state, enabledPos, 1573 state.length - enabledPos - 1); 1574 } 1575 1576 return state; 1577 } 1578 1579 @Override 1580 public boolean verifyDrawable(Drawable dr) { 1581 return mSelector == dr || super.verifyDrawable(dr); 1582 } 1583 1584 @Override 1585 protected void onAttachedToWindow() { 1586 super.onAttachedToWindow(); 1587 1588 final ViewTreeObserver treeObserver = getViewTreeObserver(); 1589 if (treeObserver != null) { 1590 treeObserver.addOnTouchModeChangeListener(this); 1591 if (mTextFilterEnabled && mPopup != null && !mGlobalLayoutListenerAddedFilter) { 1592 treeObserver.addOnGlobalLayoutListener(this); 1593 } 1594 } 1595 1596 if (mAdapter != null && mDataSetObserver == null) { 1597 mDataSetObserver = new AdapterDataSetObserver(); 1598 mAdapter.registerDataSetObserver(mDataSetObserver); 1599 } 1600 } 1601 1602 @Override 1603 protected void onDetachedFromWindow() { 1604 super.onDetachedFromWindow(); 1605 1606 // Dismiss the popup in case onSaveInstanceState() was not invoked 1607 dismissPopup(); 1608 1609 // Detach any view left in the scrap heap 1610 mRecycler.clear(); 1611 1612 final ViewTreeObserver treeObserver = getViewTreeObserver(); 1613 if (treeObserver != null) { 1614 treeObserver.removeOnTouchModeChangeListener(this); 1615 if (mTextFilterEnabled && mPopup != null) { 1616 treeObserver.removeGlobalOnLayoutListener(this); 1617 mGlobalLayoutListenerAddedFilter = false; 1618 } 1619 } 1620 1621 if (mAdapter != null) { 1622 mAdapter.unregisterDataSetObserver(mDataSetObserver); 1623 mDataSetObserver = null; 1624 } 1625 } 1626 1627 @Override 1628 public void onWindowFocusChanged(boolean hasWindowFocus) { 1629 super.onWindowFocusChanged(hasWindowFocus); 1630 1631 final int touchMode = isInTouchMode() ? TOUCH_MODE_ON : TOUCH_MODE_OFF; 1632 1633 if (!hasWindowFocus) { 1634 setChildrenDrawingCacheEnabled(false); 1635 if (mFlingRunnable != null) { 1636 removeCallbacks(mFlingRunnable); 1637 // let the fling runnable report it's new state which 1638 // should be idle 1639 mFlingRunnable.endFling(); 1640 if (mScrollY != 0) { 1641 mScrollY = 0; 1642 invalidate(); 1643 } 1644 } 1645 // Always hide the type filter 1646 dismissPopup(); 1647 1648 if (touchMode == TOUCH_MODE_OFF) { 1649 // Remember the last selected element 1650 mResurrectToPosition = mSelectedPosition; 1651 } 1652 } else { 1653 if (mFiltered && !mPopupHidden) { 1654 // Show the type filter only if a filter is in effect 1655 showPopup(); 1656 } 1657 1658 // If we changed touch mode since the last time we had focus 1659 if (touchMode != mLastTouchMode && mLastTouchMode != TOUCH_MODE_UNKNOWN) { 1660 // If we come back in trackball mode, we bring the selection back 1661 if (touchMode == TOUCH_MODE_OFF) { 1662 // This will trigger a layout 1663 resurrectSelection(); 1664 1665 // If we come back in touch mode, then we want to hide the selector 1666 } else { 1667 hideSelector(); 1668 mLayoutMode = LAYOUT_NORMAL; 1669 layoutChildren(); 1670 } 1671 } 1672 } 1673 1674 mLastTouchMode = touchMode; 1675 } 1676 1677 /** 1678 * Creates the ContextMenuInfo returned from {@link #getContextMenuInfo()}. This 1679 * methods knows the view, position and ID of the item that received the 1680 * long press. 1681 * 1682 * @param view The view that received the long press. 1683 * @param position The position of the item that received the long press. 1684 * @param id The ID of the item that received the long press. 1685 * @return The extra information that should be returned by 1686 * {@link #getContextMenuInfo()}. 1687 */ 1688 ContextMenuInfo createContextMenuInfo(View view, int position, long id) { 1689 return new AdapterContextMenuInfo(view, position, id); 1690 } 1691 1692 /** 1693 * A base class for Runnables that will check that their view is still attached to 1694 * the original window as when the Runnable was created. 1695 * 1696 */ 1697 private class WindowRunnnable { 1698 private int mOriginalAttachCount; 1699 1700 public void rememberWindowAttachCount() { 1701 mOriginalAttachCount = getWindowAttachCount(); 1702 } 1703 1704 public boolean sameWindow() { 1705 return hasWindowFocus() && getWindowAttachCount() == mOriginalAttachCount; 1706 } 1707 } 1708 1709 private class PerformClick extends WindowRunnnable implements Runnable { 1710 View mChild; 1711 int mClickMotionPosition; 1712 1713 public void run() { 1714 // The data has changed since we posted this action in the event queue, 1715 // bail out before bad things happen 1716 if (mDataChanged) return; 1717 1718 final ListAdapter adapter = mAdapter; 1719 final int motionPosition = mClickMotionPosition; 1720 if (adapter != null && mItemCount > 0 && 1721 motionPosition != INVALID_POSITION && 1722 motionPosition < adapter.getCount() && sameWindow()) { 1723 performItemClick(mChild, motionPosition, adapter.getItemId(motionPosition)); 1724 } 1725 } 1726 } 1727 1728 private class CheckForLongPress extends WindowRunnnable implements Runnable { 1729 public void run() { 1730 final int motionPosition = mMotionPosition; 1731 final View child = getChildAt(motionPosition - mFirstPosition); 1732 if (child != null) { 1733 final int longPressPosition = mMotionPosition; 1734 final long longPressId = mAdapter.getItemId(mMotionPosition); 1735 1736 boolean handled = false; 1737 if (sameWindow() && !mDataChanged) { 1738 handled = performLongPress(child, longPressPosition, longPressId); 1739 } 1740 if (handled) { 1741 mTouchMode = TOUCH_MODE_REST; 1742 setPressed(false); 1743 child.setPressed(false); 1744 } else { 1745 mTouchMode = TOUCH_MODE_DONE_WAITING; 1746 } 1747 1748 } 1749 } 1750 } 1751 1752 private class CheckForKeyLongPress extends WindowRunnnable implements Runnable { 1753 public void run() { 1754 if (isPressed() && mSelectedPosition >= 0) { 1755 int index = mSelectedPosition - mFirstPosition; 1756 View v = getChildAt(index); 1757 1758 if (!mDataChanged) { 1759 boolean handled = false; 1760 if (sameWindow()) { 1761 handled = performLongPress(v, mSelectedPosition, mSelectedRowId); 1762 } 1763 if (handled) { 1764 setPressed(false); 1765 v.setPressed(false); 1766 } 1767 } else { 1768 setPressed(false); 1769 if (v != null) v.setPressed(false); 1770 } 1771 } 1772 } 1773 } 1774 1775 private boolean performLongPress(final View child, 1776 final int longPressPosition, final long longPressId) { 1777 boolean handled = false; 1778 1779 if (mOnItemLongClickListener != null) { 1780 handled = mOnItemLongClickListener.onItemLongClick(AbsListView.this, child, 1781 longPressPosition, longPressId); 1782 } 1783 if (!handled) { 1784 mContextMenuInfo = createContextMenuInfo(child, longPressPosition, longPressId); 1785 handled = super.showContextMenuForChild(AbsListView.this); 1786 } 1787 if (handled) { 1788 performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); 1789 } 1790 return handled; 1791 } 1792 1793 @Override 1794 protected ContextMenuInfo getContextMenuInfo() { 1795 return mContextMenuInfo; 1796 } 1797 1798 @Override 1799 public boolean showContextMenuForChild(View originalView) { 1800 final int longPressPosition = getPositionForView(originalView); 1801 if (longPressPosition >= 0) { 1802 final long longPressId = mAdapter.getItemId(longPressPosition); 1803 boolean handled = false; 1804 1805 if (mOnItemLongClickListener != null) { 1806 handled = mOnItemLongClickListener.onItemLongClick(AbsListView.this, originalView, 1807 longPressPosition, longPressId); 1808 } 1809 if (!handled) { 1810 mContextMenuInfo = createContextMenuInfo( 1811 getChildAt(longPressPosition - mFirstPosition), 1812 longPressPosition, longPressId); 1813 handled = super.showContextMenuForChild(originalView); 1814 } 1815 1816 return handled; 1817 } 1818 return false; 1819 } 1820 1821 @Override 1822 public boolean onKeyDown(int keyCode, KeyEvent event) { 1823 return false; 1824 } 1825 1826 @Override 1827 public boolean onKeyUp(int keyCode, KeyEvent event) { 1828 switch (keyCode) { 1829 case KeyEvent.KEYCODE_DPAD_CENTER: 1830 case KeyEvent.KEYCODE_ENTER: 1831 if (!isEnabled()) { 1832 return true; 1833 } 1834 if (isClickable() && isPressed() && 1835 mSelectedPosition >= 0 && mAdapter != null && 1836 mSelectedPosition < mAdapter.getCount()) { 1837 1838 final View view = getChildAt(mSelectedPosition - mFirstPosition); 1839 if (view != null) { 1840 performItemClick(view, mSelectedPosition, mSelectedRowId); 1841 view.setPressed(false); 1842 } 1843 setPressed(false); 1844 return true; 1845 } 1846 break; 1847 } 1848 return super.onKeyUp(keyCode, event); 1849 } 1850 1851 @Override 1852 protected void dispatchSetPressed(boolean pressed) { 1853 // Don't dispatch setPressed to our children. We call setPressed on ourselves to 1854 // get the selector in the right state, but we don't want to press each child. 1855 } 1856 1857 /** 1858 * Maps a point to a position in the list. 1859 * 1860 * @param x X in local coordinate 1861 * @param y Y in local coordinate 1862 * @return The position of the item which contains the specified point, or 1863 * {@link #INVALID_POSITION} if the point does not intersect an item. 1864 */ 1865 public int pointToPosition(int x, int y) { 1866 Rect frame = mTouchFrame; 1867 if (frame == null) { 1868 mTouchFrame = new Rect(); 1869 frame = mTouchFrame; 1870 } 1871 1872 final int count = getChildCount(); 1873 for (int i = count - 1; i >= 0; i--) { 1874 final View child = getChildAt(i); 1875 if (child.getVisibility() == View.VISIBLE) { 1876 child.getHitRect(frame); 1877 if (frame.contains(x, y)) { 1878 return mFirstPosition + i; 1879 } 1880 } 1881 } 1882 return INVALID_POSITION; 1883 } 1884 1885 1886 /** 1887 * Maps a point to a the rowId of the item which intersects that point. 1888 * 1889 * @param x X in local coordinate 1890 * @param y Y in local coordinate 1891 * @return The rowId of the item which contains the specified point, or {@link #INVALID_ROW_ID} 1892 * if the point does not intersect an item. 1893 */ 1894 public long pointToRowId(int x, int y) { 1895 int position = pointToPosition(x, y); 1896 if (position >= 0) { 1897 return mAdapter.getItemId(position); 1898 } 1899 return INVALID_ROW_ID; 1900 } 1901 1902 final class CheckForTap implements Runnable { 1903 public void run() { 1904 if (mTouchMode == TOUCH_MODE_DOWN) { 1905 mTouchMode = TOUCH_MODE_TAP; 1906 final View child = getChildAt(mMotionPosition - mFirstPosition); 1907 if (child != null && !child.hasFocusable()) { 1908 mLayoutMode = LAYOUT_NORMAL; 1909 1910 if (!mDataChanged) { 1911 layoutChildren(); 1912 child.setPressed(true); 1913 positionSelector(child); 1914 setPressed(true); 1915 1916 final int longPressTimeout = ViewConfiguration.getLongPressTimeout(); 1917 final boolean longClickable = isLongClickable(); 1918 1919 if (mSelector != null) { 1920 Drawable d = mSelector.getCurrent(); 1921 if (d != null && d instanceof TransitionDrawable) { 1922 if (longClickable) { 1923 ((TransitionDrawable) d).startTransition(longPressTimeout); 1924 } else { 1925 ((TransitionDrawable) d).resetTransition(); 1926 } 1927 } 1928 } 1929 1930 if (longClickable) { 1931 if (mPendingCheckForLongPress == null) { 1932 mPendingCheckForLongPress = new CheckForLongPress(); 1933 } 1934 mPendingCheckForLongPress.rememberWindowAttachCount(); 1935 postDelayed(mPendingCheckForLongPress, longPressTimeout); 1936 } else { 1937 mTouchMode = TOUCH_MODE_DONE_WAITING; 1938 } 1939 } else { 1940 mTouchMode = TOUCH_MODE_DONE_WAITING; 1941 } 1942 } 1943 } 1944 } 1945 } 1946 1947 private boolean startScrollIfNeeded(int deltaY) { 1948 // Check if we have moved far enough that it looks more like a 1949 // scroll than a tap 1950 final int distance = Math.abs(deltaY); 1951 if (distance > mTouchSlop) { 1952 createScrollingCache(); 1953 mTouchMode = TOUCH_MODE_SCROLL; 1954 mMotionCorrection = deltaY; 1955 final Handler handler = getHandler(); 1956 // Handler should not be null unless the AbsListView is not attached to a 1957 // window, which would make it very hard to scroll it... but the monkeys 1958 // say it's possible. 1959 if (handler != null) { 1960 handler.removeCallbacks(mPendingCheckForLongPress); 1961 } 1962 setPressed(false); 1963 View motionView = getChildAt(mMotionPosition - mFirstPosition); 1964 if (motionView != null) { 1965 motionView.setPressed(false); 1966 } 1967 reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); 1968 // Time to start stealing events! Once we've stolen them, don't let anyone 1969 // steal from us 1970 requestDisallowInterceptTouchEvent(true); 1971 return true; 1972 } 1973 1974 return false; 1975 } 1976 1977 public void onTouchModeChanged(boolean isInTouchMode) { 1978 if (isInTouchMode) { 1979 // Get rid of the selection when we enter touch mode 1980 hideSelector(); 1981 // Layout, but only if we already have done so previously. 1982 // (Otherwise may clobber a LAYOUT_SYNC layout that was requested to restore 1983 // state.) 1984 if (getHeight() > 0 && getChildCount() > 0) { 1985 // We do not lose focus initiating a touch (since AbsListView is focusable in 1986 // touch mode). Force an initial layout to get rid of the selection. 1987 layoutChildren(); 1988 } 1989 } 1990 } 1991 1992 @Override 1993 public boolean onTouchEvent(MotionEvent ev) { 1994 if (!isEnabled()) { 1995 // A disabled view that is clickable still consumes the touch 1996 // events, it just doesn't respond to them. 1997 return isClickable() || isLongClickable(); 1998 } 1999 2000 if (mFastScroller != null) { 2001 boolean intercepted = mFastScroller.onTouchEvent(ev); 2002 if (intercepted) { 2003 return true; 2004 } 2005 } 2006 2007 final int action = ev.getAction(); 2008 2009 View v; 2010 int deltaY; 2011 2012 if (mVelocityTracker == null) { 2013 mVelocityTracker = VelocityTracker.obtain(); 2014 } 2015 mVelocityTracker.addMovement(ev); 2016 2017 switch (action & MotionEvent.ACTION_MASK) { 2018 case MotionEvent.ACTION_DOWN: { 2019 mActivePointerId = ev.getPointerId(0); 2020 final int x = (int) ev.getX(); 2021 final int y = (int) ev.getY(); 2022 int motionPosition = pointToPosition(x, y); 2023 if (!mDataChanged) { 2024 if ((mTouchMode != TOUCH_MODE_FLING) && (motionPosition >= 0) 2025 && (getAdapter().isEnabled(motionPosition))) { 2026 // User clicked on an actual view (and was not stopping a fling). It might be a 2027 // click or a scroll. Assume it is a click until proven otherwise 2028 mTouchMode = TOUCH_MODE_DOWN; 2029 // FIXME Debounce 2030 if (mPendingCheckForTap == null) { 2031 mPendingCheckForTap = new CheckForTap(); 2032 } 2033 postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); 2034 } else { 2035 if (ev.getEdgeFlags() != 0 && motionPosition < 0) { 2036 // If we couldn't find a view to click on, but the down event was touching 2037 // the edge, we will bail out and try again. This allows the edge correcting 2038 // code in ViewRoot to try to find a nearby view to select 2039 return false; 2040 } 2041 2042 if (mTouchMode == TOUCH_MODE_FLING) { 2043 // Stopped a fling. It is a scroll. 2044 createScrollingCache(); 2045 mTouchMode = TOUCH_MODE_SCROLL; 2046 mMotionCorrection = 0; 2047 motionPosition = findMotionRow(y); 2048 reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); 2049 } 2050 } 2051 } 2052 2053 if (motionPosition >= 0) { 2054 // Remember where the motion event started 2055 v = getChildAt(motionPosition - mFirstPosition); 2056 mMotionViewOriginalTop = v.getTop(); 2057 } 2058 mMotionX = x; 2059 mMotionY = y; 2060 mMotionPosition = motionPosition; 2061 mLastY = Integer.MIN_VALUE; 2062 break; 2063 } 2064 2065 case MotionEvent.ACTION_MOVE: { 2066 final int pointerIndex = ev.findPointerIndex(mActivePointerId); 2067 final int y = (int) ev.getY(pointerIndex); 2068 deltaY = y - mMotionY; 2069 switch (mTouchMode) { 2070 case TOUCH_MODE_DOWN: 2071 case TOUCH_MODE_TAP: 2072 case TOUCH_MODE_DONE_WAITING: 2073 // Check if we have moved far enough that it looks more like a 2074 // scroll than a tap 2075 startScrollIfNeeded(deltaY); 2076 break; 2077 case TOUCH_MODE_SCROLL: 2078 if (PROFILE_SCROLLING) { 2079 if (!mScrollProfilingStarted) { 2080 Debug.startMethodTracing("AbsListViewScroll"); 2081 mScrollProfilingStarted = true; 2082 } 2083 } 2084 2085 if (y != mLastY) { 2086 // We may be here after stopping a fling and continuing to scroll. 2087 // If so, we haven't disallowed intercepting touch events yet. 2088 // Make sure that we do so in case we're in a parent that can intercept. 2089 if ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) == 0 && 2090 Math.abs(deltaY) > mTouchSlop) { 2091 requestDisallowInterceptTouchEvent(true); 2092 } 2093 2094 deltaY -= mMotionCorrection; 2095 int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY; 2096 2097 // No need to do all this work if we're not going to move anyway 2098 boolean atEdge = false; 2099 if (incrementalDeltaY != 0) { 2100 atEdge = trackMotionScroll(deltaY, incrementalDeltaY); 2101 } 2102 2103 // Check to see if we have bumped into the scroll limit 2104 if (atEdge && getChildCount() > 0) { 2105 // Treat this like we're starting a new scroll from the current 2106 // position. This will let the user start scrolling back into 2107 // content immediately rather than needing to scroll back to the 2108 // point where they hit the limit first. 2109 int motionPosition = findMotionRow(y); 2110 if (motionPosition >= 0) { 2111 final View motionView = getChildAt(motionPosition - mFirstPosition); 2112 mMotionViewOriginalTop = motionView.getTop(); 2113 } 2114 mMotionY = y; 2115 mMotionPosition = motionPosition; 2116 invalidate(); 2117 } 2118 mLastY = y; 2119 } 2120 break; 2121 } 2122 2123 break; 2124 } 2125 2126 case MotionEvent.ACTION_UP: { 2127 switch (mTouchMode) { 2128 case TOUCH_MODE_DOWN: 2129 case TOUCH_MODE_TAP: 2130 case TOUCH_MODE_DONE_WAITING: 2131 final int motionPosition = mMotionPosition; 2132 final View child = getChildAt(motionPosition - mFirstPosition); 2133 if (child != null && !child.hasFocusable()) { 2134 if (mTouchMode != TOUCH_MODE_DOWN) { 2135 child.setPressed(false); 2136 } 2137 2138 if (mPerformClick == null) { 2139 mPerformClick = new PerformClick(); 2140 } 2141 2142 final AbsListView.PerformClick performClick = mPerformClick; 2143 performClick.mChild = child; 2144 performClick.mClickMotionPosition = motionPosition; 2145 performClick.rememberWindowAttachCount(); 2146 2147 mResurrectToPosition = motionPosition; 2148 2149 if (mTouchMode == TOUCH_MODE_DOWN || mTouchMode == TOUCH_MODE_TAP) { 2150 final Handler handler = getHandler(); 2151 if (handler != null) { 2152 handler.removeCallbacks(mTouchMode == TOUCH_MODE_DOWN ? 2153 mPendingCheckForTap : mPendingCheckForLongPress); 2154 } 2155 mLayoutMode = LAYOUT_NORMAL; 2156 if (!mDataChanged && mAdapter.isEnabled(motionPosition)) { 2157 mTouchMode = TOUCH_MODE_TAP; 2158 setSelectedPositionInt(mMotionPosition); 2159 layoutChildren(); 2160 child.setPressed(true); 2161 positionSelector(child); 2162 setPressed(true); 2163 if (mSelector != null) { 2164 Drawable d = mSelector.getCurrent(); 2165 if (d != null && d instanceof TransitionDrawable) { 2166 ((TransitionDrawable) d).resetTransition(); 2167 } 2168 } 2169 postDelayed(new Runnable() { 2170 public void run() { 2171 child.setPressed(false); 2172 setPressed(false); 2173 if (!mDataChanged) { 2174 post(performClick); 2175 } 2176 mTouchMode = TOUCH_MODE_REST; 2177 } 2178 }, ViewConfiguration.getPressedStateDuration()); 2179 } else { 2180 mTouchMode = TOUCH_MODE_REST; 2181 } 2182 return true; 2183 } else if (!mDataChanged && mAdapter.isEnabled(motionPosition)) { 2184 post(performClick); 2185 } 2186 } 2187 mTouchMode = TOUCH_MODE_REST; 2188 break; 2189 case TOUCH_MODE_SCROLL: 2190 final int childCount = getChildCount(); 2191 if (childCount > 0) { 2192 if (mFirstPosition == 0 && getChildAt(0).getTop() >= mListPadding.top && 2193 mFirstPosition + childCount < mItemCount && 2194 getChildAt(childCount - 1).getBottom() <= 2195 getHeight() - mListPadding.bottom) { 2196 mTouchMode = TOUCH_MODE_REST; 2197 reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); 2198 } else { 2199 final VelocityTracker velocityTracker = mVelocityTracker; 2200 velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); 2201 final int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId); 2202 2203 if (Math.abs(initialVelocity) > mMinimumVelocity) { 2204 if (mFlingRunnable == null) { 2205 mFlingRunnable = new FlingRunnable(); 2206 } 2207 reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING); 2208 2209 mFlingRunnable.start(-initialVelocity); 2210 } else { 2211 mTouchMode = TOUCH_MODE_REST; 2212 reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); 2213 } 2214 } 2215 } else { 2216 mTouchMode = TOUCH_MODE_REST; 2217 reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); 2218 } 2219 break; 2220 } 2221 2222 setPressed(false); 2223 2224 // Need to redraw since we probably aren't drawing the selector anymore 2225 invalidate(); 2226 2227 final Handler handler = getHandler(); 2228 if (handler != null) { 2229 handler.removeCallbacks(mPendingCheckForLongPress); 2230 } 2231 2232 if (mVelocityTracker != null) { 2233 mVelocityTracker.recycle(); 2234 mVelocityTracker = null; 2235 } 2236 2237 mActivePointerId = INVALID_POINTER; 2238 2239 if (PROFILE_SCROLLING) { 2240 if (mScrollProfilingStarted) { 2241 Debug.stopMethodTracing(); 2242 mScrollProfilingStarted = false; 2243 } 2244 } 2245 break; 2246 } 2247 2248 case MotionEvent.ACTION_CANCEL: { 2249 mTouchMode = TOUCH_MODE_REST; 2250 setPressed(false); 2251 View motionView = this.getChildAt(mMotionPosition - mFirstPosition); 2252 if (motionView != null) { 2253 motionView.setPressed(false); 2254 } 2255 clearScrollingCache(); 2256 2257 final Handler handler = getHandler(); 2258 if (handler != null) { 2259 handler.removeCallbacks(mPendingCheckForLongPress); 2260 } 2261 2262 if (mVelocityTracker != null) { 2263 mVelocityTracker.recycle(); 2264 mVelocityTracker = null; 2265 } 2266 2267 mActivePointerId = INVALID_POINTER; 2268 break; 2269 } 2270 2271 case MotionEvent.ACTION_POINTER_UP: { 2272 onSecondaryPointerUp(ev); 2273 final int x = mMotionX; 2274 final int y = mMotionY; 2275 final int motionPosition = pointToPosition(x, y); 2276 if (motionPosition >= 0) { 2277 // Remember where the motion event started 2278 v = getChildAt(motionPosition - mFirstPosition); 2279 mMotionViewOriginalTop = v.getTop(); 2280 mMotionPosition = motionPosition; 2281 } 2282 mLastY = y; 2283 break; 2284 } 2285 } 2286 2287 return true; 2288 } 2289 2290 @Override 2291 public void draw(Canvas canvas) { 2292 super.draw(canvas); 2293 if (mFastScroller != null) { 2294 mFastScroller.draw(canvas); 2295 } 2296 } 2297 2298 @Override 2299 public boolean onInterceptTouchEvent(MotionEvent ev) { 2300 int action = ev.getAction(); 2301 View v; 2302 2303 if (mFastScroller != null) { 2304 boolean intercepted = mFastScroller.onInterceptTouchEvent(ev); 2305 if (intercepted) { 2306 return true; 2307 } 2308 } 2309 2310 switch (action & MotionEvent.ACTION_MASK) { 2311 case MotionEvent.ACTION_DOWN: { 2312 int touchMode = mTouchMode; 2313 2314 final int x = (int) ev.getX(); 2315 final int y = (int) ev.getY(); 2316 mActivePointerId = ev.getPointerId(0); 2317 2318 int motionPosition = findMotionRow(y); 2319 if (touchMode != TOUCH_MODE_FLING && motionPosition >= 0) { 2320 // User clicked on an actual view (and was not stopping a fling). 2321 // Remember where the motion event started 2322 v = getChildAt(motionPosition - mFirstPosition); 2323 mMotionViewOriginalTop = v.getTop(); 2324 mMotionX = x; 2325 mMotionY = y; 2326 mMotionPosition = motionPosition; 2327 mTouchMode = TOUCH_MODE_DOWN; 2328 clearScrollingCache(); 2329 } 2330 mLastY = Integer.MIN_VALUE; 2331 if (touchMode == TOUCH_MODE_FLING) { 2332 return true; 2333 } 2334 break; 2335 } 2336 2337 case MotionEvent.ACTION_MOVE: { 2338 switch (mTouchMode) { 2339 case TOUCH_MODE_DOWN: 2340 final int pointerIndex = ev.findPointerIndex(mActivePointerId); 2341 final int y = (int) ev.getY(pointerIndex); 2342 if (startScrollIfNeeded(y - mMotionY)) { 2343 return true; 2344 } 2345 break; 2346 } 2347 break; 2348 } 2349 2350 case MotionEvent.ACTION_UP: { 2351 mTouchMode = TOUCH_MODE_REST; 2352 mActivePointerId = INVALID_POINTER; 2353 reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); 2354 break; 2355 } 2356 2357 case MotionEvent.ACTION_POINTER_UP: { 2358 onSecondaryPointerUp(ev); 2359 break; 2360 } 2361 } 2362 2363 return false; 2364 } 2365 2366 private void onSecondaryPointerUp(MotionEvent ev) { 2367 final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> 2368 MotionEvent.ACTION_POINTER_INDEX_SHIFT; 2369 final int pointerId = ev.getPointerId(pointerIndex); 2370 if (pointerId == mActivePointerId) { 2371 // This was our active pointer going up. Choose a new 2372 // active pointer and adjust accordingly. 2373 // TODO: Make this decision more intelligent. 2374 final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 2375 mMotionX = (int) ev.getX(newPointerIndex); 2376 mMotionY = (int) ev.getY(newPointerIndex); 2377 mActivePointerId = ev.getPointerId(newPointerIndex); 2378 if (mVelocityTracker != null) { 2379 mVelocityTracker.clear(); 2380 } 2381 } 2382 } 2383 2384 /** 2385 * {@inheritDoc} 2386 */ 2387 @Override 2388 public void addTouchables(ArrayList<View> views) { 2389 final int count = getChildCount(); 2390 final int firstPosition = mFirstPosition; 2391 final ListAdapter adapter = mAdapter; 2392 2393 if (adapter == null) { 2394 return; 2395 } 2396 2397 for (int i = 0; i < count; i++) { 2398 final View child = getChildAt(i); 2399 if (adapter.isEnabled(firstPosition + i)) { 2400 views.add(child); 2401 } 2402 child.addTouchables(views); 2403 } 2404 } 2405 2406 /** 2407 * Fires an "on scroll state changed" event to the registered 2408 * {@link android.widget.AbsListView.OnScrollListener}, if any. The state change 2409 * is fired only if the specified state is different from the previously known state. 2410 * 2411 * @param newState The new scroll state. 2412 */ 2413 void reportScrollStateChange(int newState) { 2414 if (newState != mLastScrollState) { 2415 if (mOnScrollListener != null) { 2416 mOnScrollListener.onScrollStateChanged(this, newState); 2417 mLastScrollState = newState; 2418 } 2419 } 2420 } 2421 2422 /** 2423 * Responsible for fling behavior. Use {@link #start(int)} to 2424 * initiate a fling. Each frame of the fling is handled in {@link #run()}. 2425 * A FlingRunnable will keep re-posting itself until the fling is done. 2426 * 2427 */ 2428 private class FlingRunnable implements Runnable { 2429 /** 2430 * Tracks the decay of a fling scroll 2431 */ 2432 private final Scroller mScroller; 2433 2434 /** 2435 * Y value reported by mScroller on the previous fling 2436 */ 2437 private int mLastFlingY; 2438 2439 FlingRunnable() { 2440 mScroller = new Scroller(getContext()); 2441 } 2442 2443 void start(int initialVelocity) { 2444 int initialY = initialVelocity < 0 ? Integer.MAX_VALUE : 0; 2445 mLastFlingY = initialY; 2446 mScroller.fling(0, initialY, 0, initialVelocity, 2447 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE); 2448 mTouchMode = TOUCH_MODE_FLING; 2449 post(this); 2450 2451 if (PROFILE_FLINGING) { 2452 if (!mFlingProfilingStarted) { 2453 Debug.startMethodTracing("AbsListViewFling"); 2454 mFlingProfilingStarted = true; 2455 } 2456 } 2457 } 2458 2459 void startScroll(int distance, int duration) { 2460 int initialY = distance < 0 ? Integer.MAX_VALUE : 0; 2461 mLastFlingY = initialY; 2462 mScroller.startScroll(0, initialY, 0, distance, duration); 2463 mTouchMode = TOUCH_MODE_FLING; 2464 post(this); 2465 } 2466 2467 private void endFling() { 2468 mTouchMode = TOUCH_MODE_REST; 2469 2470 reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); 2471 clearScrollingCache(); 2472 2473 removeCallbacks(this); 2474 2475 if (mPositionScroller != null) { 2476 removeCallbacks(mPositionScroller); 2477 } 2478 } 2479 2480 public void run() { 2481 switch (mTouchMode) { 2482 default: 2483 return; 2484 2485 case TOUCH_MODE_FLING: { 2486 if (mItemCount == 0 || getChildCount() == 0) { 2487 endFling(); 2488 return; 2489 } 2490 2491 final Scroller scroller = mScroller; 2492 boolean more = scroller.computeScrollOffset(); 2493 final int y = scroller.getCurrY(); 2494 2495 // Flip sign to convert finger direction to list items direction 2496 // (e.g. finger moving down means list is moving towards the top) 2497 int delta = mLastFlingY - y; 2498 2499 // Pretend that each frame of a fling scroll is a touch scroll 2500 if (delta > 0) { 2501 // List is moving towards the top. Use first view as mMotionPosition 2502 mMotionPosition = mFirstPosition; 2503 final View firstView = getChildAt(0); 2504 mMotionViewOriginalTop = firstView.getTop(); 2505 2506 // Don't fling more than 1 screen 2507 delta = Math.min(getHeight() - mPaddingBottom - mPaddingTop - 1, delta); 2508 } else { 2509 // List is moving towards the bottom. Use last view as mMotionPosition 2510 int offsetToLast = getChildCount() - 1; 2511 mMotionPosition = mFirstPosition + offsetToLast; 2512 2513 final View lastView = getChildAt(offsetToLast); 2514 mMotionViewOriginalTop = lastView.getTop(); 2515 2516 // Don't fling more than 1 screen 2517 delta = Math.max(-(getHeight() - mPaddingBottom - mPaddingTop - 1), delta); 2518 } 2519 2520 final boolean atEnd = trackMotionScroll(delta, delta); 2521 2522 if (more && !atEnd) { 2523 invalidate(); 2524 mLastFlingY = y; 2525 post(this); 2526 } else { 2527 endFling(); 2528 2529 if (PROFILE_FLINGING) { 2530 if (mFlingProfilingStarted) { 2531 Debug.stopMethodTracing(); 2532 mFlingProfilingStarted = false; 2533 } 2534 } 2535 } 2536 break; 2537 } 2538 } 2539 2540 } 2541 } 2542 2543 2544 class PositionScroller implements Runnable { 2545 private static final int SCROLL_DURATION = 400; 2546 2547 private static final int MOVE_DOWN_POS = 1; 2548 private static final int MOVE_UP_POS = 2; 2549 private static final int MOVE_DOWN_BOUND = 3; 2550 private static final int MOVE_UP_BOUND = 4; 2551 private static final int MOVE_OFFSET = 5; 2552 2553 private int mMode; 2554 private int mTargetPos; 2555 private int mBoundPos; 2556 private int mLastSeenPos; 2557 private int mScrollDuration; 2558 private final int mExtraScroll; 2559 2560 private int mOffsetFromTop; 2561 2562 PositionScroller() { 2563 mExtraScroll = ViewConfiguration.get(mContext).getScaledFadingEdgeLength(); 2564 } 2565 2566 void start(int position) { 2567 final int firstPos = mFirstPosition; 2568 final int lastPos = firstPos + getChildCount() - 1; 2569 2570 int viewTravelCount = 0; 2571 if (position <= firstPos) { 2572 viewTravelCount = firstPos - position + 1; 2573 mMode = MOVE_UP_POS; 2574 } else if (position >= lastPos) { 2575 viewTravelCount = position - lastPos + 1; 2576 mMode = MOVE_DOWN_POS; 2577 } else { 2578 // Already on screen, nothing to do 2579 return; 2580 } 2581 2582 if (viewTravelCount > 0) { 2583 mScrollDuration = SCROLL_DURATION / viewTravelCount; 2584 } else { 2585 mScrollDuration = SCROLL_DURATION; 2586 } 2587 mTargetPos = position; 2588 mBoundPos = INVALID_POSITION; 2589 mLastSeenPos = INVALID_POSITION; 2590 2591 post(this); 2592 } 2593 2594 void start(int position, int boundPosition) { 2595 if (boundPosition == INVALID_POSITION) { 2596 start(position); 2597 return; 2598 } 2599 2600 final int firstPos = mFirstPosition; 2601 final int lastPos = firstPos + getChildCount() - 1; 2602 2603 int viewTravelCount = 0; 2604 if (position <= firstPos) { 2605 final int boundPosFromLast = lastPos - boundPosition; 2606 if (boundPosFromLast < 1) { 2607 // Moving would shift our bound position off the screen. Abort. 2608 return; 2609 } 2610 2611 final int posTravel = firstPos - position + 1; 2612 final int boundTravel = boundPosFromLast - 1; 2613 if (boundTravel < posTravel) { 2614 viewTravelCount = boundTravel; 2615 mMode = MOVE_UP_BOUND; 2616 } else { 2617 viewTravelCount = posTravel; 2618 mMode = MOVE_UP_POS; 2619 } 2620 } else if (position >= lastPos) { 2621 final int boundPosFromFirst = boundPosition - firstPos; 2622 if (boundPosFromFirst < 1) { 2623 // Moving would shift our bound position off the screen. Abort. 2624 return; 2625 } 2626 2627 final int posTravel = position - lastPos + 1; 2628 final int boundTravel = boundPosFromFirst - 1; 2629 if (boundTravel < posTravel) { 2630 viewTravelCount = boundTravel; 2631 mMode = MOVE_DOWN_BOUND; 2632 } else { 2633 viewTravelCount = posTravel; 2634 mMode = MOVE_DOWN_POS; 2635 } 2636 } else { 2637 // Already on screen, nothing to do 2638 return; 2639 } 2640 2641 if (viewTravelCount > 0) { 2642 mScrollDuration = SCROLL_DURATION / viewTravelCount; 2643 } else { 2644 mScrollDuration = SCROLL_DURATION; 2645 } 2646 mTargetPos = position; 2647 mBoundPos = boundPosition; 2648 mLastSeenPos = INVALID_POSITION; 2649 2650 post(this); 2651 } 2652 2653 void startWithOffset(int position, int offset) { 2654 mTargetPos = position; 2655 mOffsetFromTop = offset; 2656 mBoundPos = INVALID_POSITION; 2657 mLastSeenPos = INVALID_POSITION; 2658 mMode = MOVE_OFFSET; 2659 2660 final int firstPos = mFirstPosition; 2661 final int childCount = getChildCount(); 2662 final int lastPos = firstPos + childCount - 1; 2663 2664 int viewTravelCount = 0; 2665 if (position < firstPos) { 2666 viewTravelCount = firstPos - position; 2667 } else if (position > lastPos) { 2668 viewTravelCount = position - lastPos; 2669 } else { 2670 // On-screen, just scroll. 2671 final int targetTop = getChildAt(position - firstPos).getTop(); 2672 smoothScrollBy(targetTop - offset, SCROLL_DURATION); 2673 return; 2674 } 2675 2676 // Estimate how many screens we should travel 2677 final float screenTravelCount = viewTravelCount / childCount; 2678 mScrollDuration = (int) (SCROLL_DURATION / screenTravelCount); 2679 mLastSeenPos = INVALID_POSITION; 2680 post(this); 2681 } 2682 2683 void stop() { 2684 removeCallbacks(this); 2685 } 2686 2687 public void run() { 2688 if (mTouchMode != TOUCH_MODE_FLING && mLastSeenPos != INVALID_POSITION) { 2689 return; 2690 } 2691 2692 final int listHeight = getHeight(); 2693 final int firstPos = mFirstPosition; 2694 2695 switch (mMode) { 2696 case MOVE_DOWN_POS: { 2697 final int lastViewIndex = getChildCount() - 1; 2698 final int lastPos = firstPos + lastViewIndex; 2699 2700 if (lastViewIndex < 0) { 2701 return; 2702 } 2703 2704 if (lastPos == mLastSeenPos) { 2705 // No new views, let things keep going. 2706 post(this); 2707 return; 2708 } 2709 2710 final View lastView = getChildAt(lastViewIndex); 2711 final int lastViewHeight = lastView.getHeight(); 2712 final int lastViewTop = lastView.getTop(); 2713 final int lastViewPixelsShowing = listHeight - lastViewTop; 2714 final int extraScroll = lastPos < mItemCount - 1 ? mExtraScroll : mListPadding.bottom; 2715 2716 smoothScrollBy(lastViewHeight - lastViewPixelsShowing + extraScroll, 2717 mScrollDuration); 2718 2719 mLastSeenPos = lastPos; 2720 if (lastPos < mTargetPos) { 2721 post(this); 2722 } 2723 break; 2724 } 2725 2726 case MOVE_DOWN_BOUND: { 2727 final int nextViewIndex = 1; 2728 final int childCount = getChildCount(); 2729 2730 if (firstPos == mBoundPos || childCount <= nextViewIndex 2731 || firstPos + childCount >= mItemCount) { 2732 return; 2733 } 2734 final int nextPos = firstPos + nextViewIndex; 2735 2736 if (nextPos == mLastSeenPos) { 2737 // No new views, let things keep going. 2738 post(this); 2739 return; 2740 } 2741 2742 final View nextView = getChildAt(nextViewIndex); 2743 final int nextViewHeight = nextView.getHeight(); 2744 final int nextViewTop = nextView.getTop(); 2745 final int extraScroll = mExtraScroll; 2746 if (nextPos < mBoundPos) { 2747 smoothScrollBy(Math.max(0, nextViewHeight + nextViewTop - extraScroll), 2748 mScrollDuration); 2749 2750 mLastSeenPos = nextPos; 2751 2752 post(this); 2753 } else { 2754 if (nextViewTop > extraScroll) { 2755 smoothScrollBy(nextViewTop - extraScroll, mScrollDuration); 2756 } 2757 } 2758 break; 2759 } 2760 2761 case MOVE_UP_POS: { 2762 if (firstPos == mLastSeenPos) { 2763 // No new views, let things keep going. 2764 post(this); 2765 return; 2766 } 2767 2768 final View firstView = getChildAt(0); 2769 if (firstView == null) { 2770 return; 2771 } 2772 final int firstViewTop = firstView.getTop(); 2773 final int extraScroll = firstPos > 0 ? mExtraScroll : mListPadding.top; 2774 2775 smoothScrollBy(firstViewTop - extraScroll, mScrollDuration); 2776 2777 mLastSeenPos = firstPos; 2778 2779 if (firstPos > mTargetPos) { 2780 post(this); 2781 } 2782 break; 2783 } 2784 2785 case MOVE_UP_BOUND: { 2786 final int lastViewIndex = getChildCount() - 2; 2787 if (lastViewIndex < 0) { 2788 return; 2789 } 2790 final int lastPos = firstPos + lastViewIndex; 2791 2792 if (lastPos == mLastSeenPos) { 2793 // No new views, let things keep going. 2794 post(this); 2795 return; 2796 } 2797 2798 final View lastView = getChildAt(lastViewIndex); 2799 final int lastViewHeight = lastView.getHeight(); 2800 final int lastViewTop = lastView.getTop(); 2801 final int lastViewPixelsShowing = listHeight - lastViewTop; 2802 mLastSeenPos = lastPos; 2803 if (lastPos > mBoundPos) { 2804 smoothScrollBy(-(lastViewPixelsShowing - mExtraScroll), mScrollDuration); 2805 post(this); 2806 } else { 2807 final int bottom = listHeight - mExtraScroll; 2808 final int lastViewBottom = lastViewTop + lastViewHeight; 2809 if (bottom > lastViewBottom) { 2810 smoothScrollBy(-(bottom - lastViewBottom), mScrollDuration); 2811 } 2812 } 2813 break; 2814 } 2815 2816 case MOVE_OFFSET: { 2817 final int childCount = getChildCount(); 2818 2819 mLastSeenPos = firstPos; 2820 final int position = mTargetPos; 2821 final int lastPos = firstPos + childCount - 1; 2822 2823 if (position < firstPos) { 2824 smoothScrollBy(-getHeight(), mScrollDuration); 2825 post(this); 2826 } else if (position > lastPos) { 2827 smoothScrollBy(getHeight(), mScrollDuration); 2828 post(this); 2829 } else { 2830 // On-screen, just scroll. 2831 final int targetTop = getChildAt(position - firstPos).getTop(); 2832 smoothScrollBy(targetTop - mOffsetFromTop, mScrollDuration); 2833 } 2834 break; 2835 } 2836 2837 default: 2838 break; 2839 } 2840 } 2841 } 2842 2843 /** 2844 * Smoothly scroll to the specified adapter position. The view will 2845 * scroll such that the indicated position is displayed. 2846 * @param position Scroll to this adapter position. 2847 */ 2848 public void smoothScrollToPosition(int position) { 2849 if (mPositionScroller == null) { 2850 mPositionScroller = new PositionScroller(); 2851 } 2852 mPositionScroller.start(position); 2853 } 2854 2855 /** 2856 * Smoothly scroll to the specified adapter position. The view will scroll 2857 * such that the indicated position is displayed <code>offset</code> pixels from 2858 * the top edge of the view. If this is impossible, (e.g. the offset would scroll 2859 * the first or last item beyond the boundaries of the list) it will get as close 2860 * as possible. 2861 * 2862 * @param position Position to scroll to 2863 * @param offset Desired distance in pixels of <code>position</code> from the top 2864 * of the view when scrolling is finished 2865 */ 2866 public void smoothScrollToPositionFromTop(int position, int offset) { 2867 if (mPositionScroller == null) { 2868 mPositionScroller = new PositionScroller(); 2869 } 2870 mPositionScroller.startWithOffset(position, offset); 2871 } 2872 2873 /** 2874 * Smoothly scroll to the specified adapter position. The view will 2875 * scroll such that the indicated position is displayed, but it will 2876 * stop early if scrolling further would scroll boundPosition out of 2877 * view. 2878 * @param position Scroll to this adapter position. 2879 * @param boundPosition Do not scroll if it would move this adapter 2880 * position out of view. 2881 */ 2882 public void smoothScrollToPosition(int position, int boundPosition) { 2883 if (mPositionScroller == null) { 2884 mPositionScroller = new PositionScroller(); 2885 } 2886 mPositionScroller.start(position, boundPosition); 2887 } 2888 2889 /** 2890 * Smoothly scroll by distance pixels over duration milliseconds. 2891 * @param distance Distance to scroll in pixels. 2892 * @param duration Duration of the scroll animation in milliseconds. 2893 */ 2894 public void smoothScrollBy(int distance, int duration) { 2895 if (mFlingRunnable == null) { 2896 mFlingRunnable = new FlingRunnable(); 2897 } else { 2898 mFlingRunnable.endFling(); 2899 } 2900 mFlingRunnable.startScroll(distance, duration); 2901 } 2902 2903 /** 2904 * Allows RemoteViews to scroll relatively to a position. 2905 */ 2906 void smoothScrollByOffset(int position) { 2907 int index = -1; 2908 if (position < 0) { 2909 index = getFirstVisiblePosition(); 2910 } else if (position > 0) { 2911 index = getLastVisiblePosition(); 2912 } 2913 2914 if (index > -1) { 2915 View child = getChildAt(index - getFirstVisiblePosition()); 2916 if (child != null) { 2917 Rect visibleRect = new Rect(); 2918 if (child.getGlobalVisibleRect(visibleRect)) { 2919 // the child is partially visible 2920 int childRectArea = child.getWidth() * child.getHeight(); 2921 int visibleRectArea = visibleRect.width() * visibleRect.height(); 2922 float visibleArea = (visibleRectArea / (float) childRectArea); 2923 final float visibleThreshold = 0.75f; 2924 if ((position < 0) && (visibleArea < visibleThreshold)) { 2925 // the top index is not perceivably visible so offset 2926 // to account for showing that top index as well 2927 ++index; 2928 } else if ((position > 0) && (visibleArea < visibleThreshold)) { 2929 // the bottom index is not perceivably visible so offset 2930 // to account for showing that bottom index as well 2931 --index; 2932 } 2933 } 2934 smoothScrollToPosition(Math.max(0, Math.min(getCount(), index + position))); 2935 } 2936 } 2937 } 2938 2939 private void createScrollingCache() { 2940 if (mScrollingCacheEnabled && !mCachingStarted) { 2941 setChildrenDrawnWithCacheEnabled(true); 2942 setChildrenDrawingCacheEnabled(true); 2943 mCachingStarted = true; 2944 } 2945 } 2946 2947 private void clearScrollingCache() { 2948 if (mClearScrollingCache == null) { 2949 mClearScrollingCache = new Runnable() { 2950 public void run() { 2951 if (mCachingStarted) { 2952 mCachingStarted = false; 2953 setChildrenDrawnWithCacheEnabled(false); 2954 if ((mPersistentDrawingCache & PERSISTENT_SCROLLING_CACHE) == 0) { 2955 setChildrenDrawingCacheEnabled(false); 2956 } 2957 if (!isAlwaysDrawnWithCacheEnabled()) { 2958 invalidate(); 2959 } 2960 } 2961 } 2962 }; 2963 } 2964 post(mClearScrollingCache); 2965 } 2966 2967 /** 2968 * Track a motion scroll 2969 * 2970 * @param deltaY Amount to offset mMotionView. This is the accumulated delta since the motion 2971 * began. Positive numbers mean the user's finger is moving down the screen. 2972 * @param incrementalDeltaY Change in deltaY from the previous event. 2973 * @return true if we're already at the beginning/end of the list and have nothing to do. 2974 */ 2975 boolean trackMotionScroll(int deltaY, int incrementalDeltaY) { 2976 final int childCount = getChildCount(); 2977 if (childCount == 0) { 2978 return true; 2979 } 2980 2981 final int firstTop = getChildAt(0).getTop(); 2982 final int lastBottom = getChildAt(childCount - 1).getBottom(); 2983 2984 final Rect listPadding = mListPadding; 2985 2986 // FIXME account for grid vertical spacing too? 2987 final int spaceAbove = listPadding.top - firstTop; 2988 final int end = getHeight() - listPadding.bottom; 2989 final int spaceBelow = lastBottom - end; 2990 2991 final int height = getHeight() - mPaddingBottom - mPaddingTop; 2992 if (deltaY < 0) { 2993 deltaY = Math.max(-(height - 1), deltaY); 2994 } else { 2995 deltaY = Math.min(height - 1, deltaY); 2996 } 2997 2998 if (incrementalDeltaY < 0) { 2999 incrementalDeltaY = Math.max(-(height - 1), incrementalDeltaY); 3000 } else { 3001 incrementalDeltaY = Math.min(height - 1, incrementalDeltaY); 3002 } 3003 3004 final int firstPosition = mFirstPosition; 3005 3006 if (firstPosition == 0 && firstTop >= listPadding.top && deltaY >= 0) { 3007 // Don't need to move views down if the top of the first position 3008 // is already visible 3009 return true; 3010 } 3011 3012 if (firstPosition + childCount == mItemCount && lastBottom <= end && deltaY <= 0) { 3013 // Don't need to move views up if the bottom of the last position 3014 // is already visible 3015 return true; 3016 } 3017 3018 final boolean down = incrementalDeltaY < 0; 3019 3020 final boolean inTouchMode = isInTouchMode(); 3021 if (inTouchMode) { 3022 hideSelector(); 3023 } 3024 3025 final int headerViewsCount = getHeaderViewsCount(); 3026 final int footerViewsStart = mItemCount - getFooterViewsCount(); 3027 3028 int start = 0; 3029 int count = 0; 3030 3031 if (down) { 3032 final int top = listPadding.top - incrementalDeltaY; 3033 for (int i = 0; i < childCount; i++) { 3034 final View child = getChildAt(i); 3035 if (child.getBottom() >= top) { 3036 break; 3037 } else { 3038 count++; 3039 int position = firstPosition + i; 3040 if (position >= headerViewsCount && position < footerViewsStart) { 3041 mRecycler.addScrapView(child); 3042 3043 if (ViewDebug.TRACE_RECYCLER) { 3044 ViewDebug.trace(child, 3045 ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP, 3046 firstPosition + i, -1); 3047 } 3048 } 3049 } 3050 } 3051 } else { 3052 final int bottom = getHeight() - listPadding.bottom - incrementalDeltaY; 3053 for (int i = childCount - 1; i >= 0; i--) { 3054 final View child = getChildAt(i); 3055 if (child.getTop() <= bottom) { 3056 break; 3057 } else { 3058 start = i; 3059 count++; 3060 int position = firstPosition + i; 3061 if (position >= headerViewsCount && position < footerViewsStart) { 3062 mRecycler.addScrapView(child); 3063 3064 if (ViewDebug.TRACE_RECYCLER) { 3065 ViewDebug.trace(child, 3066 ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP, 3067 firstPosition + i, -1); 3068 } 3069 } 3070 } 3071 } 3072 } 3073 3074 mMotionViewNewTop = mMotionViewOriginalTop + deltaY; 3075 3076 mBlockLayoutRequests = true; 3077 3078 if (count > 0) { 3079 detachViewsFromParent(start, count); 3080 } 3081 offsetChildrenTopAndBottom(incrementalDeltaY); 3082 3083 if (down) { 3084 mFirstPosition += count; 3085 } 3086 3087 invalidate(); 3088 3089 final int absIncrementalDeltaY = Math.abs(incrementalDeltaY); 3090 if (spaceAbove < absIncrementalDeltaY || spaceBelow < absIncrementalDeltaY) { 3091 fillGap(down); 3092 } 3093 3094 if (!inTouchMode && mSelectedPosition != INVALID_POSITION) { 3095 final int childIndex = mSelectedPosition - mFirstPosition; 3096 if (childIndex >= 0 && childIndex < getChildCount()) { 3097 positionSelector(getChildAt(childIndex)); 3098 } 3099 } 3100 3101 mBlockLayoutRequests = false; 3102 3103 invokeOnItemScrollListener(); 3104 awakenScrollBars(); 3105 3106 return false; 3107 } 3108 3109 /** 3110 * Returns the number of header views in the list. Header views are special views 3111 * at the top of the list that should not be recycled during a layout. 3112 * 3113 * @return The number of header views, 0 in the default implementation. 3114 */ 3115 int getHeaderViewsCount() { 3116 return 0; 3117 } 3118 3119 /** 3120 * Returns the number of footer views in the list. Footer views are special views 3121 * at the bottom of the list that should not be recycled during a layout. 3122 * 3123 * @return The number of footer views, 0 in the default implementation. 3124 */ 3125 int getFooterViewsCount() { 3126 return 0; 3127 } 3128 3129 /** 3130 * Fills the gap left open by a touch-scroll. During a touch scroll, children that 3131 * remain on screen are shifted and the other ones are discarded. The role of this 3132 * method is to fill the gap thus created by performing a partial layout in the 3133 * empty space. 3134 * 3135 * @param down true if the scroll is going down, false if it is going up 3136 */ 3137 abstract void fillGap(boolean down); 3138 3139 void hideSelector() { 3140 if (mSelectedPosition != INVALID_POSITION) { 3141 if (mLayoutMode != LAYOUT_SPECIFIC) { 3142 mResurrectToPosition = mSelectedPosition; 3143 } 3144 if (mNextSelectedPosition >= 0 && mNextSelectedPosition != mSelectedPosition) { 3145 mResurrectToPosition = mNextSelectedPosition; 3146 } 3147 setSelectedPositionInt(INVALID_POSITION); 3148 setNextSelectedPositionInt(INVALID_POSITION); 3149 mSelectedTop = 0; 3150 mSelectorRect.setEmpty(); 3151 } 3152 } 3153 3154 /** 3155 * @return A position to select. First we try mSelectedPosition. If that has been clobbered by 3156 * entering touch mode, we then try mResurrectToPosition. Values are pinned to the range 3157 * of items available in the adapter 3158 */ 3159 int reconcileSelectedPosition() { 3160 int position = mSelectedPosition; 3161 if (position < 0) { 3162 position = mResurrectToPosition; 3163 } 3164 position = Math.max(0, position); 3165 position = Math.min(position, mItemCount - 1); 3166 return position; 3167 } 3168 3169 /** 3170 * Find the row closest to y. This row will be used as the motion row when scrolling 3171 * 3172 * @param y Where the user touched 3173 * @return The position of the first (or only) item in the row containing y 3174 */ 3175 abstract int findMotionRow(int y); 3176 3177 /** 3178 * Find the row closest to y. This row will be used as the motion row when scrolling. 3179 * 3180 * @param y Where the user touched 3181 * @return The position of the first (or only) item in the row closest to y 3182 */ 3183 int findClosestMotionRow(int y) { 3184 final int childCount = getChildCount(); 3185 if (childCount == 0) { 3186 return INVALID_POSITION; 3187 } 3188 3189 final int motionRow = findMotionRow(y); 3190 return motionRow != INVALID_POSITION ? motionRow : mFirstPosition + childCount - 1; 3191 } 3192 3193 /** 3194 * Causes all the views to be rebuilt and redrawn. 3195 */ 3196 public void invalidateViews() { 3197 mDataChanged = true; 3198 rememberSyncState(); 3199 requestLayout(); 3200 invalidate(); 3201 } 3202 3203 /** 3204 * Makes the item at the supplied position selected. 3205 * 3206 * @param position the position of the new selection 3207 */ 3208 abstract void setSelectionInt(int position); 3209 3210 /** 3211 * Attempt to bring the selection back if the user is switching from touch 3212 * to trackball mode 3213 * @return Whether selection was set to something. 3214 */ 3215 boolean resurrectSelection() { 3216 final int childCount = getChildCount(); 3217 3218 if (childCount <= 0) { 3219 return false; 3220 } 3221 3222 int selectedTop = 0; 3223 int selectedPos; 3224 int childrenTop = mListPadding.top; 3225 int childrenBottom = mBottom - mTop - mListPadding.bottom; 3226 final int firstPosition = mFirstPosition; 3227 final int toPosition = mResurrectToPosition; 3228 boolean down = true; 3229 3230 if (toPosition >= firstPosition && toPosition < firstPosition + childCount) { 3231 selectedPos = toPosition; 3232 3233 final View selected = getChildAt(selectedPos - mFirstPosition); 3234 selectedTop = selected.getTop(); 3235 int selectedBottom = selected.getBottom(); 3236 3237 // We are scrolled, don't get in the fade 3238 if (selectedTop < childrenTop) { 3239 selectedTop = childrenTop + getVerticalFadingEdgeLength(); 3240 } else if (selectedBottom > childrenBottom) { 3241 selectedTop = childrenBottom - selected.getMeasuredHeight() 3242 - getVerticalFadingEdgeLength(); 3243 } 3244 } else { 3245 if (toPosition < firstPosition) { 3246 // Default to selecting whatever is first 3247 selectedPos = firstPosition; 3248 for (int i = 0; i < childCount; i++) { 3249 final View v = getChildAt(i); 3250 final int top = v.getTop(); 3251 3252 if (i == 0) { 3253 // Remember the position of the first item 3254 selectedTop = top; 3255 // See if we are scrolled at all 3256 if (firstPosition > 0 || top < childrenTop) { 3257 // If we are scrolled, don't select anything that is 3258 // in the fade region 3259 childrenTop += getVerticalFadingEdgeLength(); 3260 } 3261 } 3262 if (top >= childrenTop) { 3263 // Found a view whose top is fully visisble 3264 selectedPos = firstPosition + i; 3265 selectedTop = top; 3266 break; 3267 } 3268 } 3269 } else { 3270 final int itemCount = mItemCount; 3271 down = false; 3272 selectedPos = firstPosition + childCount - 1; 3273 3274 for (int i = childCount - 1; i >= 0; i--) { 3275 final View v = getChildAt(i); 3276 final int top = v.getTop(); 3277 final int bottom = v.getBottom(); 3278 3279 if (i == childCount - 1) { 3280 selectedTop = top; 3281 if (firstPosition + childCount < itemCount || bottom > childrenBottom) { 3282 childrenBottom -= getVerticalFadingEdgeLength(); 3283 } 3284 } 3285 3286 if (bottom <= childrenBottom) { 3287 selectedPos = firstPosition + i; 3288 selectedTop = top; 3289 break; 3290 } 3291 } 3292 } 3293 } 3294 3295 mResurrectToPosition = INVALID_POSITION; 3296 removeCallbacks(mFlingRunnable); 3297 removeCallbacks(mPositionScroller); 3298 mTouchMode = TOUCH_MODE_REST; 3299 clearScrollingCache(); 3300 mSpecificTop = selectedTop; 3301 selectedPos = lookForSelectablePosition(selectedPos, down); 3302 if (selectedPos >= firstPosition && selectedPos <= getLastVisiblePosition()) { 3303 mLayoutMode = LAYOUT_SPECIFIC; 3304 setSelectionInt(selectedPos); 3305 invokeOnItemScrollListener(); 3306 } else { 3307 selectedPos = INVALID_POSITION; 3308 } 3309 reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); 3310 3311 return selectedPos >= 0; 3312 } 3313 3314 @Override 3315 protected void handleDataChanged() { 3316 int count = mItemCount; 3317 if (count > 0) { 3318 3319 int newPos; 3320 3321 int selectablePos; 3322 3323 // Find the row we are supposed to sync to 3324 if (mNeedSync) { 3325 // Update this first, since setNextSelectedPositionInt inspects it 3326 mNeedSync = false; 3327 3328 if (mTranscriptMode == TRANSCRIPT_MODE_ALWAYS_SCROLL || 3329 (mTranscriptMode == TRANSCRIPT_MODE_NORMAL && 3330 mFirstPosition + getChildCount() >= mOldItemCount)) { 3331 mLayoutMode = LAYOUT_FORCE_BOTTOM; 3332 return; 3333 } 3334 3335 switch (mSyncMode) { 3336 case SYNC_SELECTED_POSITION: 3337 if (isInTouchMode()) { 3338 // We saved our state when not in touch mode. (We know this because 3339 // mSyncMode is SYNC_SELECTED_POSITION.) Now we are trying to 3340 // restore in touch mode. Just leave mSyncPosition as it is (possibly 3341 // adjusting if the available range changed) and return. 3342 mLayoutMode = LAYOUT_SYNC; 3343 mSyncPosition = Math.min(Math.max(0, mSyncPosition), count - 1); 3344 3345 return; 3346 } else { 3347 // See if we can find a position in the new data with the same 3348 // id as the old selection. This will change mSyncPosition. 3349 newPos = findSyncPosition(); 3350 if (newPos >= 0) { 3351 // Found it. Now verify that new selection is still selectable 3352 selectablePos = lookForSelectablePosition(newPos, true); 3353 if (selectablePos == newPos) { 3354 // Same row id is selected 3355 mSyncPosition = newPos; 3356 3357 if (mSyncHeight == getHeight()) { 3358 // If we are at the same height as when we saved state, try 3359 // to restore the scroll position too. 3360 mLayoutMode = LAYOUT_SYNC; 3361 } else { 3362 // We are not the same height as when the selection was saved, so 3363 // don't try to restore the exact position 3364 mLayoutMode = LAYOUT_SET_SELECTION; 3365 } 3366 3367 // Restore selection 3368 setNextSelectedPositionInt(newPos); 3369 return; 3370 } 3371 } 3372 } 3373 break; 3374 case SYNC_FIRST_POSITION: 3375 // Leave mSyncPosition as it is -- just pin to available range 3376 mLayoutMode = LAYOUT_SYNC; 3377 mSyncPosition = Math.min(Math.max(0, mSyncPosition), count - 1); 3378 3379 return; 3380 } 3381 } 3382 3383 if (!isInTouchMode()) { 3384 // We couldn't find matching data -- try to use the same position 3385 newPos = getSelectedItemPosition(); 3386 3387 // Pin position to the available range 3388 if (newPos >= count) { 3389 newPos = count - 1; 3390 } 3391 if (newPos < 0) { 3392 newPos = 0; 3393 } 3394 3395 // Make sure we select something selectable -- first look down 3396 selectablePos = lookForSelectablePosition(newPos, true); 3397 3398 if (selectablePos >= 0) { 3399 setNextSelectedPositionInt(selectablePos); 3400 return; 3401 } else { 3402 // Looking down didn't work -- try looking up 3403 selectablePos = lookForSelectablePosition(newPos, false); 3404 if (selectablePos >= 0) { 3405 setNextSelectedPositionInt(selectablePos); 3406 return; 3407 } 3408 } 3409 } else { 3410 3411 // We already know where we want to resurrect the selection 3412 if (mResurrectToPosition >= 0) { 3413 return; 3414 } 3415 } 3416 3417 } 3418 3419 // Nothing is selected. Give up and reset everything. 3420 mLayoutMode = mStackFromBottom ? LAYOUT_FORCE_BOTTOM : LAYOUT_FORCE_TOP; 3421 mSelectedPosition = INVALID_POSITION; 3422 mSelectedRowId = INVALID_ROW_ID; 3423 mNextSelectedPosition = INVALID_POSITION; 3424 mNextSelectedRowId = INVALID_ROW_ID; 3425 mNeedSync = false; 3426 checkSelectionChanged(); 3427 } 3428 3429 @Override 3430 protected void onDisplayHint(int hint) { 3431 super.onDisplayHint(hint); 3432 switch (hint) { 3433 case INVISIBLE: 3434 if (mPopup != null && mPopup.isShowing()) { 3435 dismissPopup(); 3436 } 3437 break; 3438 case VISIBLE: 3439 if (mFiltered && mPopup != null && !mPopup.isShowing()) { 3440 showPopup(); 3441 } 3442 break; 3443 } 3444 mPopupHidden = hint == INVISIBLE; 3445 } 3446 3447 /** 3448 * Removes the filter window 3449 */ 3450 private void dismissPopup() { 3451 if (mPopup != null) { 3452 mPopup.dismiss(); 3453 } 3454 } 3455 3456 /** 3457 * Shows the filter window 3458 */ 3459 private void showPopup() { 3460 // Make sure we have a window before showing the popup 3461 if (getWindowVisibility() == View.VISIBLE) { 3462 createTextFilter(true); 3463 positionPopup(); 3464 // Make sure we get focus if we are showing the popup 3465 checkFocus(); 3466 } 3467 } 3468 3469 private void positionPopup() { 3470 int screenHeight = getResources().getDisplayMetrics().heightPixels; 3471 final int[] xy = new int[2]; 3472 getLocationOnScreen(xy); 3473 // TODO: The 20 below should come from the theme 3474 // TODO: And the gravity should be defined in the theme as well 3475 final int bottomGap = screenHeight - xy[1] - getHeight() + (int) (mDensityScale * 20); 3476 if (!mPopup.isShowing()) { 3477 mPopup.showAtLocation(this, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 3478 xy[0], bottomGap); 3479 } else { 3480 mPopup.update(xy[0], bottomGap, -1, -1); 3481 } 3482 } 3483 3484 /** 3485 * What is the distance between the source and destination rectangles given the direction of 3486 * focus navigation between them? The direction basically helps figure out more quickly what is 3487 * self evident by the relationship between the rects... 3488 * 3489 * @param source the source rectangle 3490 * @param dest the destination rectangle 3491 * @param direction the direction 3492 * @return the distance between the rectangles 3493 */ 3494 static int getDistance(Rect source, Rect dest, int direction) { 3495 int sX, sY; // source x, y 3496 int dX, dY; // dest x, y 3497 switch (direction) { 3498 case View.FOCUS_RIGHT: 3499 sX = source.right; 3500 sY = source.top + source.height() / 2; 3501 dX = dest.left; 3502 dY = dest.top + dest.height() / 2; 3503 break; 3504 case View.FOCUS_DOWN: 3505 sX = source.left + source.width() / 2; 3506 sY = source.bottom; 3507 dX = dest.left + dest.width() / 2; 3508 dY = dest.top; 3509 break; 3510 case View.FOCUS_LEFT: 3511 sX = source.left; 3512 sY = source.top + source.height() / 2; 3513 dX = dest.right; 3514 dY = dest.top + dest.height() / 2; 3515 break; 3516 case View.FOCUS_UP: 3517 sX = source.left + source.width() / 2; 3518 sY = source.top; 3519 dX = dest.left + dest.width() / 2; 3520 dY = dest.bottom; 3521 break; 3522 default: 3523 throw new IllegalArgumentException("direction must be one of " 3524 + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}."); 3525 } 3526 int deltaX = dX - sX; 3527 int deltaY = dY - sY; 3528 return deltaY * deltaY + deltaX * deltaX; 3529 } 3530 3531 @Override 3532 protected boolean isInFilterMode() { 3533 return mFiltered; 3534 } 3535 3536 /** 3537 * Sends a key to the text filter window 3538 * 3539 * @param keyCode The keycode for the event 3540 * @param event The actual key event 3541 * 3542 * @return True if the text filter handled the event, false otherwise. 3543 */ 3544 boolean sendToTextFilter(int keyCode, int count, KeyEvent event) { 3545 if (!acceptFilter()) { 3546 return false; 3547 } 3548 3549 boolean handled = false; 3550 boolean okToSend = true; 3551 switch (keyCode) { 3552 case KeyEvent.KEYCODE_DPAD_UP: 3553 case KeyEvent.KEYCODE_DPAD_DOWN: 3554 case KeyEvent.KEYCODE_DPAD_LEFT: 3555 case KeyEvent.KEYCODE_DPAD_RIGHT: 3556 case KeyEvent.KEYCODE_DPAD_CENTER: 3557 case KeyEvent.KEYCODE_ENTER: 3558 okToSend = false; 3559 break; 3560 case KeyEvent.KEYCODE_BACK: 3561 if (mFiltered && mPopup != null && mPopup.isShowing()) { 3562 if (event.getAction() == KeyEvent.ACTION_DOWN 3563 && event.getRepeatCount() == 0) { 3564 getKeyDispatcherState().startTracking(event, this); 3565 handled = true; 3566 } else if (event.getAction() == KeyEvent.ACTION_UP 3567 && event.isTracking() && !event.isCanceled()) { 3568 handled = true; 3569 mTextFilter.setText(""); 3570 } 3571 } 3572 okToSend = false; 3573 break; 3574 case KeyEvent.KEYCODE_SPACE: 3575 // Only send spaces once we are filtered 3576 okToSend = mFiltered; 3577 break; 3578 } 3579 3580 if (okToSend) { 3581 createTextFilter(true); 3582 3583 KeyEvent forwardEvent = event; 3584 if (forwardEvent.getRepeatCount() > 0) { 3585 forwardEvent = KeyEvent.changeTimeRepeat(event, event.getEventTime(), 0); 3586 } 3587 3588 int action = event.getAction(); 3589 switch (action) { 3590 case KeyEvent.ACTION_DOWN: 3591 handled = mTextFilter.onKeyDown(keyCode, forwardEvent); 3592 break; 3593 3594 case KeyEvent.ACTION_UP: 3595 handled = mTextFilter.onKeyUp(keyCode, forwardEvent); 3596 break; 3597 3598 case KeyEvent.ACTION_MULTIPLE: 3599 handled = mTextFilter.onKeyMultiple(keyCode, count, event); 3600 break; 3601 } 3602 } 3603 return handled; 3604 } 3605 3606 /** 3607 * Return an InputConnection for editing of the filter text. 3608 */ 3609 @Override 3610 public InputConnection onCreateInputConnection(EditorInfo outAttrs) { 3611 if (isTextFilterEnabled()) { 3612 // XXX we need to have the text filter created, so we can get an 3613 // InputConnection to proxy to. Unfortunately this means we pretty 3614 // much need to make it as soon as a list view gets focus. 3615 createTextFilter(false); 3616 if (mPublicInputConnection == null) { 3617 mDefInputConnection = new BaseInputConnection(this, false); 3618 mPublicInputConnection = new InputConnectionWrapper( 3619 mTextFilter.onCreateInputConnection(outAttrs), true) { 3620 @Override 3621 public boolean reportFullscreenMode(boolean enabled) { 3622 // Use our own input connection, since it is 3623 // the "real" one the IME is talking with. 3624 return mDefInputConnection.reportFullscreenMode(enabled); 3625 } 3626 3627 @Override 3628 public boolean performEditorAction(int editorAction) { 3629 // The editor is off in its own window; we need to be 3630 // the one that does this. 3631 if (editorAction == EditorInfo.IME_ACTION_DONE) { 3632 InputMethodManager imm = (InputMethodManager) 3633 getContext().getSystemService( 3634 Context.INPUT_METHOD_SERVICE); 3635 if (imm != null) { 3636 imm.hideSoftInputFromWindow(getWindowToken(), 0); 3637 } 3638 return true; 3639 } 3640 return false; 3641 } 3642 3643 @Override 3644 public boolean sendKeyEvent(KeyEvent event) { 3645 // Use our own input connection, since the filter 3646 // text view may not be shown in a window so has 3647 // no ViewRoot to dispatch events with. 3648 return mDefInputConnection.sendKeyEvent(event); 3649 } 3650 }; 3651 } 3652 outAttrs.inputType = EditorInfo.TYPE_CLASS_TEXT 3653 | EditorInfo.TYPE_TEXT_VARIATION_FILTER; 3654 outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE; 3655 return mPublicInputConnection; 3656 } 3657 return null; 3658 } 3659 3660 /** 3661 * For filtering we proxy an input connection to an internal text editor, 3662 * and this allows the proxying to happen. 3663 */ 3664 @Override 3665 public boolean checkInputConnectionProxy(View view) { 3666 return view == mTextFilter; 3667 } 3668 3669 /** 3670 * Creates the window for the text filter and populates it with an EditText field; 3671 * 3672 * @param animateEntrance true if the window should appear with an animation 3673 */ 3674 private void createTextFilter(boolean animateEntrance) { 3675 if (mPopup == null) { 3676 Context c = getContext(); 3677 PopupWindow p = new PopupWindow(c); 3678 LayoutInflater layoutInflater = (LayoutInflater) 3679 c.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 3680 mTextFilter = (EditText) layoutInflater.inflate( 3681 com.android.internal.R.layout.typing_filter, null); 3682 // For some reason setting this as the "real" input type changes 3683 // the text view in some way that it doesn't work, and I don't 3684 // want to figure out why this is. 3685 mTextFilter.setRawInputType(EditorInfo.TYPE_CLASS_TEXT 3686 | EditorInfo.TYPE_TEXT_VARIATION_FILTER); 3687 mTextFilter.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI); 3688 mTextFilter.addTextChangedListener(this); 3689 p.setFocusable(false); 3690 p.setTouchable(false); 3691 p.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); 3692 p.setContentView(mTextFilter); 3693 p.setWidth(LayoutParams.WRAP_CONTENT); 3694 p.setHeight(LayoutParams.WRAP_CONTENT); 3695 p.setBackgroundDrawable(null); 3696 mPopup = p; 3697 getViewTreeObserver().addOnGlobalLayoutListener(this); 3698 mGlobalLayoutListenerAddedFilter = true; 3699 } 3700 if (animateEntrance) { 3701 mPopup.setAnimationStyle(com.android.internal.R.style.Animation_TypingFilter); 3702 } else { 3703 mPopup.setAnimationStyle(com.android.internal.R.style.Animation_TypingFilterRestore); 3704 } 3705 } 3706 3707 /** 3708 * Clear the text filter. 3709 */ 3710 public void clearTextFilter() { 3711 if (mFiltered) { 3712 mTextFilter.setText(""); 3713 mFiltered = false; 3714 if (mPopup != null && mPopup.isShowing()) { 3715 dismissPopup(); 3716 } 3717 } 3718 } 3719 3720 /** 3721 * Returns if the ListView currently has a text filter. 3722 */ 3723 public boolean hasTextFilter() { 3724 return mFiltered; 3725 } 3726 3727 public void onGlobalLayout() { 3728 if (isShown()) { 3729 // Show the popup if we are filtered 3730 if (mFiltered && mPopup != null && !mPopup.isShowing() && !mPopupHidden) { 3731 showPopup(); 3732 } 3733 } else { 3734 // Hide the popup when we are no longer visible 3735 if (mPopup != null && mPopup.isShowing()) { 3736 dismissPopup(); 3737 } 3738 } 3739 3740 } 3741 3742 /** 3743 * For our text watcher that is associated with the text filter. Does 3744 * nothing. 3745 */ 3746 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 3747 } 3748 3749 /** 3750 * For our text watcher that is associated with the text filter. Performs 3751 * the actual filtering as the text changes, and takes care of hiding and 3752 * showing the popup displaying the currently entered filter text. 3753 */ 3754 public void onTextChanged(CharSequence s, int start, int before, int count) { 3755 if (mPopup != null && isTextFilterEnabled()) { 3756 int length = s.length(); 3757 boolean showing = mPopup.isShowing(); 3758 if (!showing && length > 0) { 3759 // Show the filter popup if necessary 3760 showPopup(); 3761 mFiltered = true; 3762 } else if (showing && length == 0) { 3763 // Remove the filter popup if the user has cleared all text 3764 dismissPopup(); 3765 mFiltered = false; 3766 } 3767 if (mAdapter instanceof Filterable) { 3768 Filter f = ((Filterable) mAdapter).getFilter(); 3769 // Filter should not be null when we reach this part 3770 if (f != null) { 3771 f.filter(s, this); 3772 } else { 3773 throw new IllegalStateException("You cannot call onTextChanged with a non " 3774 + "filterable adapter"); 3775 } 3776 } 3777 } 3778 } 3779 3780 /** 3781 * For our text watcher that is associated with the text filter. Does 3782 * nothing. 3783 */ 3784 public void afterTextChanged(Editable s) { 3785 } 3786 3787 public void onFilterComplete(int count) { 3788 if (mSelectedPosition < 0 && count > 0) { 3789 mResurrectToPosition = INVALID_POSITION; 3790 resurrectSelection(); 3791 } 3792 } 3793 3794 @Override 3795 protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 3796 return new LayoutParams(p); 3797 } 3798 3799 @Override 3800 public LayoutParams generateLayoutParams(AttributeSet attrs) { 3801 return new AbsListView.LayoutParams(getContext(), attrs); 3802 } 3803 3804 @Override 3805 protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 3806 return p instanceof AbsListView.LayoutParams; 3807 } 3808 3809 /** 3810 * Puts the list or grid into transcript mode. In this mode the list or grid will always scroll 3811 * to the bottom to show new items. 3812 * 3813 * @param mode the transcript mode to set 3814 * 3815 * @see #TRANSCRIPT_MODE_DISABLED 3816 * @see #TRANSCRIPT_MODE_NORMAL 3817 * @see #TRANSCRIPT_MODE_ALWAYS_SCROLL 3818 */ 3819 public void setTranscriptMode(int mode) { 3820 mTranscriptMode = mode; 3821 } 3822 3823 /** 3824 * Returns the current transcript mode. 3825 * 3826 * @return {@link #TRANSCRIPT_MODE_DISABLED}, {@link #TRANSCRIPT_MODE_NORMAL} or 3827 * {@link #TRANSCRIPT_MODE_ALWAYS_SCROLL} 3828 */ 3829 public int getTranscriptMode() { 3830 return mTranscriptMode; 3831 } 3832 3833 @Override 3834 public int getSolidColor() { 3835 return mCacheColorHint; 3836 } 3837 3838 /** 3839 * When set to a non-zero value, the cache color hint indicates that this list is always drawn 3840 * on top of a solid, single-color, opaque background 3841 * 3842 * @param color The background color 3843 */ 3844 public void setCacheColorHint(int color) { 3845 if (color != mCacheColorHint) { 3846 mCacheColorHint = color; 3847 int count = getChildCount(); 3848 for (int i = 0; i < count; i++) { 3849 getChildAt(i).setDrawingCacheBackgroundColor(color); 3850 } 3851 mRecycler.setCacheColorHint(color); 3852 } 3853 } 3854 3855 /** 3856 * When set to a non-zero value, the cache color hint indicates that this list is always drawn 3857 * on top of a solid, single-color, opaque background 3858 * 3859 * @return The cache color hint 3860 */ 3861 public int getCacheColorHint() { 3862 return mCacheColorHint; 3863 } 3864 3865 /** 3866 * Move all views (excluding headers and footers) held by this AbsListView into the supplied 3867 * List. This includes views displayed on the screen as well as views stored in AbsListView's 3868 * internal view recycler. 3869 * 3870 * @param views A list into which to put the reclaimed views 3871 */ 3872 public void reclaimViews(List<View> views) { 3873 int childCount = getChildCount(); 3874 RecyclerListener listener = mRecycler.mRecyclerListener; 3875 3876 // Reclaim views on screen 3877 for (int i = 0; i < childCount; i++) { 3878 View child = getChildAt(i); 3879 AbsListView.LayoutParams lp = (AbsListView.LayoutParams) child.getLayoutParams(); 3880 // Don't reclaim header or footer views, or views that should be ignored 3881 if (lp != null && mRecycler.shouldRecycleViewType(lp.viewType)) { 3882 views.add(child); 3883 if (listener != null) { 3884 // Pretend they went through the scrap heap 3885 listener.onMovedToScrapHeap(child); 3886 } 3887 } 3888 } 3889 mRecycler.reclaimScrapViews(views); 3890 removeAllViewsInLayout(); 3891 } 3892 3893 /** 3894 * @hide 3895 */ 3896 @Override 3897 protected boolean onConsistencyCheck(int consistency) { 3898 boolean result = super.onConsistencyCheck(consistency); 3899 3900 final boolean checkLayout = (consistency & ViewDebug.CONSISTENCY_LAYOUT) != 0; 3901 3902 if (checkLayout) { 3903 // The active recycler must be empty 3904 final View[] activeViews = mRecycler.mActiveViews; 3905 int count = activeViews.length; 3906 for (int i = 0; i < count; i++) { 3907 if (activeViews[i] != null) { 3908 result = false; 3909 Log.d(ViewDebug.CONSISTENCY_LOG_TAG, 3910 "AbsListView " + this + " has a view in its active recycler: " + 3911 activeViews[i]); 3912 } 3913 } 3914 3915 // All views in the recycler must NOT be on screen and must NOT have a parent 3916 final ArrayList<View> scrap = mRecycler.mCurrentScrap; 3917 if (!checkScrap(scrap)) result = false; 3918 final ArrayList<View>[] scraps = mRecycler.mScrapViews; 3919 count = scraps.length; 3920 for (int i = 0; i < count; i++) { 3921 if (!checkScrap(scraps[i])) result = false; 3922 } 3923 } 3924 3925 return result; 3926 } 3927 3928 private boolean checkScrap(ArrayList<View> scrap) { 3929 if (scrap == null) return true; 3930 boolean result = true; 3931 3932 final int count = scrap.size(); 3933 for (int i = 0; i < count; i++) { 3934 final View view = scrap.get(i); 3935 if (view.getParent() != null) { 3936 result = false; 3937 Log.d(ViewDebug.CONSISTENCY_LOG_TAG, "AbsListView " + this + 3938 " has a view in its scrap heap still attached to a parent: " + view); 3939 } 3940 if (indexOfChild(view) >= 0) { 3941 result = false; 3942 Log.d(ViewDebug.CONSISTENCY_LOG_TAG, "AbsListView " + this + 3943 " has a view in its scrap heap that is also a direct child: " + view); 3944 } 3945 } 3946 3947 return result; 3948 } 3949 3950 /** 3951 * Sets up this AbsListView to use a remote views adapter which connects to a RemoteViewsService 3952 * through the specified intent. 3953 * @param intent the intent used to identify the RemoteViewsService for the adapter to connect to. 3954 */ 3955 public void setRemoteViewsAdapter(Intent intent) { 3956 mRemoteAdapter = new RemoteViewsAdapter(getContext(), intent, this); 3957 } 3958 3959 /** 3960 * Called back when the adapter connects to the RemoteViewsService. 3961 */ 3962 public void onRemoteAdapterConnected() { 3963 if (mRemoteAdapter != mAdapter) { 3964 setAdapter(mRemoteAdapter); 3965 } 3966 } 3967 3968 /** 3969 * Called back when the adapter disconnects from the RemoteViewsService. 3970 */ 3971 public void onRemoteAdapterDisconnected() { 3972 if (mRemoteAdapter == mAdapter) { 3973 mRemoteAdapter = null; 3974 setAdapter(null); 3975 } 3976 } 3977 3978 /** 3979 * Sets the recycler listener to be notified whenever a View is set aside in 3980 * the recycler for later reuse. This listener can be used to free resources 3981 * associated to the View. 3982 * 3983 * @param listener The recycler listener to be notified of views set aside 3984 * in the recycler. 3985 * 3986 * @see android.widget.AbsListView.RecycleBin 3987 * @see android.widget.AbsListView.RecyclerListener 3988 */ 3989 public void setRecyclerListener(RecyclerListener listener) { 3990 mRecycler.mRecyclerListener = listener; 3991 } 3992 3993 /** 3994 * AbsListView extends LayoutParams to provide a place to hold the view type. 3995 */ 3996 public static class LayoutParams extends ViewGroup.LayoutParams { 3997 /** 3998 * View type for this view, as returned by 3999 * {@link android.widget.Adapter#getItemViewType(int) } 4000 */ 4001 @ViewDebug.ExportedProperty(mapping = { 4002 @ViewDebug.IntToString(from = ITEM_VIEW_TYPE_IGNORE, to = "ITEM_VIEW_TYPE_IGNORE"), 4003 @ViewDebug.IntToString(from = ITEM_VIEW_TYPE_HEADER_OR_FOOTER, to = "ITEM_VIEW_TYPE_HEADER_OR_FOOTER") 4004 }) 4005 int viewType; 4006 4007 /** 4008 * When this boolean is set, the view has been added to the AbsListView 4009 * at least once. It is used to know whether headers/footers have already 4010 * been added to the list view and whether they should be treated as 4011 * recycled views or not. 4012 */ 4013 @ViewDebug.ExportedProperty 4014 boolean recycledHeaderFooter; 4015 4016 /** 4017 * When an AbsListView is measured with an AT_MOST measure spec, it needs 4018 * to obtain children views to measure itself. When doing so, the children 4019 * are not attached to the window, but put in the recycler which assumes 4020 * they've been attached before. Setting this flag will force the reused 4021 * view to be attached to the window rather than just attached to the 4022 * parent. 4023 */ 4024 @ViewDebug.ExportedProperty 4025 boolean forceAdd; 4026 4027 public LayoutParams(Context c, AttributeSet attrs) { 4028 super(c, attrs); 4029 } 4030 4031 public LayoutParams(int w, int h) { 4032 super(w, h); 4033 } 4034 4035 public LayoutParams(int w, int h, int viewType) { 4036 super(w, h); 4037 this.viewType = viewType; 4038 } 4039 4040 public LayoutParams(ViewGroup.LayoutParams source) { 4041 super(source); 4042 } 4043 } 4044 4045 /** 4046 * A RecyclerListener is used to receive a notification whenever a View is placed 4047 * inside the RecycleBin's scrap heap. This listener is used to free resources 4048 * associated to Views placed in the RecycleBin. 4049 * 4050 * @see android.widget.AbsListView.RecycleBin 4051 * @see android.widget.AbsListView#setRecyclerListener(android.widget.AbsListView.RecyclerListener) 4052 */ 4053 public static interface RecyclerListener { 4054 /** 4055 * Indicates that the specified View was moved into the recycler's scrap heap. 4056 * The view is not displayed on screen any more and any expensive resource 4057 * associated with the view should be discarded. 4058 * 4059 * @param view 4060 */ 4061 void onMovedToScrapHeap(View view); 4062 } 4063 4064 /** 4065 * The RecycleBin facilitates reuse of views across layouts. The RecycleBin has two levels of 4066 * storage: ActiveViews and ScrapViews. ActiveViews are those views which were onscreen at the 4067 * start of a layout. By construction, they are displaying current information. At the end of 4068 * layout, all views in ActiveViews are demoted to ScrapViews. ScrapViews are old views that 4069 * could potentially be used by the adapter to avoid allocating views unnecessarily. 4070 * 4071 * @see android.widget.AbsListView#setRecyclerListener(android.widget.AbsListView.RecyclerListener) 4072 * @see android.widget.AbsListView.RecyclerListener 4073 */ 4074 class RecycleBin { 4075 private RecyclerListener mRecyclerListener; 4076 4077 /** 4078 * The position of the first view stored in mActiveViews. 4079 */ 4080 private int mFirstActivePosition; 4081 4082 /** 4083 * Views that were on screen at the start of layout. This array is populated at the start of 4084 * layout, and at the end of layout all view in mActiveViews are moved to mScrapViews. 4085 * Views in mActiveViews represent a contiguous range of Views, with position of the first 4086 * view store in mFirstActivePosition. 4087 */ 4088 private View[] mActiveViews = new View[0]; 4089 4090 /** 4091 * Unsorted views that can be used by the adapter as a convert view. 4092 */ 4093 private ArrayList<View>[] mScrapViews; 4094 4095 private int mViewTypeCount; 4096 4097 private ArrayList<View> mCurrentScrap; 4098 4099 public void setViewTypeCount(int viewTypeCount) { 4100 if (viewTypeCount < 1) { 4101 throw new IllegalArgumentException("Can't have a viewTypeCount < 1"); 4102 } 4103 //noinspection unchecked 4104 ArrayList<View>[] scrapViews = new ArrayList[viewTypeCount]; 4105 for (int i = 0; i < viewTypeCount; i++) { 4106 scrapViews[i] = new ArrayList<View>(); 4107 } 4108 mViewTypeCount = viewTypeCount; 4109 mCurrentScrap = scrapViews[0]; 4110 mScrapViews = scrapViews; 4111 } 4112 4113 public void markChildrenDirty() { 4114 if (mViewTypeCount == 1) { 4115 final ArrayList<View> scrap = mCurrentScrap; 4116 final int scrapCount = scrap.size(); 4117 for (int i = 0; i < scrapCount; i++) { 4118 scrap.get(i).forceLayout(); 4119 } 4120 } else { 4121 final int typeCount = mViewTypeCount; 4122 for (int i = 0; i < typeCount; i++) { 4123 final ArrayList<View> scrap = mScrapViews[i]; 4124 final int scrapCount = scrap.size(); 4125 for (int j = 0; j < scrapCount; j++) { 4126 scrap.get(j).forceLayout(); 4127 } 4128 } 4129 } 4130 } 4131 4132 public boolean shouldRecycleViewType(int viewType) { 4133 return viewType >= 0; 4134 } 4135 4136 /** 4137 * Clears the scrap heap. 4138 */ 4139 void clear() { 4140 if (mViewTypeCount == 1) { 4141 final ArrayList<View> scrap = mCurrentScrap; 4142 final int scrapCount = scrap.size(); 4143 for (int i = 0; i < scrapCount; i++) { 4144 removeDetachedView(scrap.remove(scrapCount - 1 - i), false); 4145 } 4146 } else { 4147 final int typeCount = mViewTypeCount; 4148 for (int i = 0; i < typeCount; i++) { 4149 final ArrayList<View> scrap = mScrapViews[i]; 4150 final int scrapCount = scrap.size(); 4151 for (int j = 0; j < scrapCount; j++) { 4152 removeDetachedView(scrap.remove(scrapCount - 1 - j), false); 4153 } 4154 } 4155 } 4156 } 4157 4158 /** 4159 * Fill ActiveViews with all of the children of the AbsListView. 4160 * 4161 * @param childCount The minimum number of views mActiveViews should hold 4162 * @param firstActivePosition The position of the first view that will be stored in 4163 * mActiveViews 4164 */ 4165 void fillActiveViews(int childCount, int firstActivePosition) { 4166 if (mActiveViews.length < childCount) { 4167 mActiveViews = new View[childCount]; 4168 } 4169 mFirstActivePosition = firstActivePosition; 4170 4171 final View[] activeViews = mActiveViews; 4172 for (int i = 0; i < childCount; i++) { 4173 View child = getChildAt(i); 4174 AbsListView.LayoutParams lp = (AbsListView.LayoutParams) child.getLayoutParams(); 4175 // Don't put header or footer views into the scrap heap 4176 if (lp != null && lp.viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) { 4177 // Note: We do place AdapterView.ITEM_VIEW_TYPE_IGNORE in active views. 4178 // However, we will NOT place them into scrap views. 4179 activeViews[i] = child; 4180 } 4181 } 4182 } 4183 4184 /** 4185 * Get the view corresponding to the specified position. The view will be removed from 4186 * mActiveViews if it is found. 4187 * 4188 * @param position The position to look up in mActiveViews 4189 * @return The view if it is found, null otherwise 4190 */ 4191 View getActiveView(int position) { 4192 int index = position - mFirstActivePosition; 4193 final View[] activeViews = mActiveViews; 4194 if (index >=0 && index < activeViews.length) { 4195 final View match = activeViews[index]; 4196 activeViews[index] = null; 4197 return match; 4198 } 4199 return null; 4200 } 4201 4202 /** 4203 * @return A view from the ScrapViews collection. These are unordered. 4204 */ 4205 View getScrapView(int position) { 4206 ArrayList<View> scrapViews; 4207 if (mViewTypeCount == 1) { 4208 scrapViews = mCurrentScrap; 4209 int size = scrapViews.size(); 4210 if (size > 0) { 4211 return scrapViews.remove(size - 1); 4212 } else { 4213 return null; 4214 } 4215 } else { 4216 int whichScrap = mAdapter.getItemViewType(position); 4217 if (whichScrap >= 0 && whichScrap < mScrapViews.length) { 4218 scrapViews = mScrapViews[whichScrap]; 4219 int size = scrapViews.size(); 4220 if (size > 0) { 4221 return scrapViews.remove(size - 1); 4222 } 4223 } 4224 } 4225 return null; 4226 } 4227 4228 /** 4229 * Put a view into the ScapViews list. These views are unordered. 4230 * 4231 * @param scrap The view to add 4232 */ 4233 void addScrapView(View scrap) { 4234 AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams(); 4235 if (lp == null) { 4236 return; 4237 } 4238 4239 // Don't put header or footer views or views that should be ignored 4240 // into the scrap heap 4241 int viewType = lp.viewType; 4242 if (!shouldRecycleViewType(viewType)) { 4243 if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) { 4244 removeDetachedView(scrap, false); 4245 } 4246 return; 4247 } 4248 4249 if (mViewTypeCount == 1) { 4250 scrap.dispatchStartTemporaryDetach(); 4251 mCurrentScrap.add(scrap); 4252 } else { 4253 scrap.dispatchStartTemporaryDetach(); 4254 mScrapViews[viewType].add(scrap); 4255 } 4256 4257 if (mRecyclerListener != null) { 4258 mRecyclerListener.onMovedToScrapHeap(scrap); 4259 } 4260 } 4261 4262 /** 4263 * Move all views remaining in mActiveViews to mScrapViews. 4264 */ 4265 void scrapActiveViews() { 4266 final View[] activeViews = mActiveViews; 4267 final boolean hasListener = mRecyclerListener != null; 4268 final boolean multipleScraps = mViewTypeCount > 1; 4269 4270 ArrayList<View> scrapViews = mCurrentScrap; 4271 final int count = activeViews.length; 4272 for (int i = count - 1; i >= 0; i--) { 4273 final View victim = activeViews[i]; 4274 if (victim != null) { 4275 int whichScrap = ((AbsListView.LayoutParams) victim.getLayoutParams()).viewType; 4276 4277 activeViews[i] = null; 4278 4279 if (!shouldRecycleViewType(whichScrap)) { 4280 // Do not move views that should be ignored 4281 if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) { 4282 removeDetachedView(victim, false); 4283 } 4284 continue; 4285 } 4286 4287 if (multipleScraps) { 4288 scrapViews = mScrapViews[whichScrap]; 4289 } 4290 victim.dispatchStartTemporaryDetach(); 4291 scrapViews.add(victim); 4292 4293 if (hasListener) { 4294 mRecyclerListener.onMovedToScrapHeap(victim); 4295 } 4296 4297 if (ViewDebug.TRACE_RECYCLER) { 4298 ViewDebug.trace(victim, 4299 ViewDebug.RecyclerTraceType.MOVE_FROM_ACTIVE_TO_SCRAP_HEAP, 4300 mFirstActivePosition + i, -1); 4301 } 4302 } 4303 } 4304 4305 pruneScrapViews(); 4306 } 4307 4308 /** 4309 * Makes sure that the size of mScrapViews does not exceed the size of mActiveViews. 4310 * (This can happen if an adapter does not recycle its views). 4311 */ 4312 private void pruneScrapViews() { 4313 final int maxViews = mActiveViews.length; 4314 final int viewTypeCount = mViewTypeCount; 4315 final ArrayList<View>[] scrapViews = mScrapViews; 4316 for (int i = 0; i < viewTypeCount; ++i) { 4317 final ArrayList<View> scrapPile = scrapViews[i]; 4318 int size = scrapPile.size(); 4319 final int extras = size - maxViews; 4320 size--; 4321 for (int j = 0; j < extras; j++) { 4322 removeDetachedView(scrapPile.remove(size--), false); 4323 } 4324 } 4325 } 4326 4327 /** 4328 * Puts all views in the scrap heap into the supplied list. 4329 */ 4330 void reclaimScrapViews(List<View> views) { 4331 if (mViewTypeCount == 1) { 4332 views.addAll(mCurrentScrap); 4333 } else { 4334 final int viewTypeCount = mViewTypeCount; 4335 final ArrayList<View>[] scrapViews = mScrapViews; 4336 for (int i = 0; i < viewTypeCount; ++i) { 4337 final ArrayList<View> scrapPile = scrapViews[i]; 4338 views.addAll(scrapPile); 4339 } 4340 } 4341 } 4342 4343 /** 4344 * Updates the cache color hint of all known views. 4345 * 4346 * @param color The new cache color hint. 4347 */ 4348 void setCacheColorHint(int color) { 4349 if (mViewTypeCount == 1) { 4350 final ArrayList<View> scrap = mCurrentScrap; 4351 final int scrapCount = scrap.size(); 4352 for (int i = 0; i < scrapCount; i++) { 4353 scrap.get(i).setDrawingCacheBackgroundColor(color); 4354 } 4355 } else { 4356 final int typeCount = mViewTypeCount; 4357 for (int i = 0; i < typeCount; i++) { 4358 final ArrayList<View> scrap = mScrapViews[i]; 4359 final int scrapCount = scrap.size(); 4360 for (int j = 0; j < scrapCount; j++) { 4361 scrap.get(j).setDrawingCacheBackgroundColor(color); 4362 } 4363 } 4364 } 4365 // Just in case this is called during a layout pass 4366 final View[] activeViews = mActiveViews; 4367 final int count = activeViews.length; 4368 for (int i = 0; i < count; ++i) { 4369 final View victim = activeViews[i]; 4370 if (victim != null) { 4371 victim.setDrawingCacheBackgroundColor(color); 4372 } 4373 } 4374 } 4375 } 4376} 4377