FastScroller.java revision b2e551711aa93a3d296ced6368e895743d43da80
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/* 29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project 39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License. 69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at 79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * 109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and 149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License. 159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.widget; 189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context; 209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.ColorStateList; 219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.TypedArray; 229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Canvas; 239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Paint; 2420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powellimport android.graphics.Rect; 259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.RectF; 269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.drawable.Drawable; 2720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powellimport android.graphics.drawable.NinePatchDrawable; 289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Handler; 299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.SystemClock; 309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.view.MotionEvent; 3120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powellimport android.view.View; 32d43bd48fe311fa6320e46ca9b9ce25241c476ac6Adam Powellimport android.widget.AbsListView.OnScrollListener; 339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/** 359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Helper class for AbsListView to draw and control the Fast Scroll thumb 369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */ 379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectclass FastScroller { 389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Minimum number of pages to justify showing a fast scroll thumb 409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static int MIN_PAGES = 4; 419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Scroll thumb not showing 429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final int STATE_NONE = 0; 439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Not implemented yet - fade-in transition 449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final int STATE_ENTER = 1; 459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Scroll thumb visible and moving along with the scrollbar 469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final int STATE_VISIBLE = 2; 479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Scroll thumb being dragged by user 489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final int STATE_DRAGGING = 3; 499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Scroll thumb fading out due to inactivity timeout 509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private static final int STATE_EXIT = 4; 5120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 5220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell private static final int[] PRESSED_STATES = new int[] { 5320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell android.R.attr.state_pressed 5420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell }; 5520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 5620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell private static final int[] DEFAULT_STATES = new int[0]; 5720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 5820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell private static final int[] ATTRS = new int[] { 59b2e551711aa93a3d296ced6368e895743d43da80Adam Powell android.R.attr.fastScrollTextColor, 60128b6ba93d0549fd2beff4482678e1229dc1cf3dAdam Powell android.R.attr.fastScrollThumbDrawable, 61128b6ba93d0549fd2beff4482678e1229dc1cf3dAdam Powell android.R.attr.fastScrollTrackDrawable, 62128b6ba93d0549fd2beff4482678e1229dc1cf3dAdam Powell android.R.attr.fastScrollPreviewBackgroundLeft, 63128b6ba93d0549fd2beff4482678e1229dc1cf3dAdam Powell android.R.attr.fastScrollPreviewBackgroundRight, 64128b6ba93d0549fd2beff4482678e1229dc1cf3dAdam Powell android.R.attr.fastScrollOverlayPosition 6520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell }; 6620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 67b2e551711aa93a3d296ced6368e895743d43da80Adam Powell private static final int TEXT_COLOR = 0; 6820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell private static final int THUMB_DRAWABLE = 1; 6920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell private static final int TRACK_DRAWABLE = 2; 7020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell private static final int PREVIEW_BACKGROUND_LEFT = 3; 7120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell private static final int PREVIEW_BACKGROUND_RIGHT = 4; 7220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell private static final int OVERLAY_POSITION = 5; 7320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 7420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell private static final int OVERLAY_FLOATING = 0; 7520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell private static final int OVERLAY_AT_THUMB = 1; 769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Drawable mThumbDrawable; 789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Drawable mOverlayDrawable; 7920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell private Drawable mTrackDrawable; 8020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 8120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell private Drawable mOverlayDrawableLeft; 8220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell private Drawable mOverlayDrawableRight; 839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int mThumbH; 859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int mThumbW; 869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int mThumbY; 879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private RectF mOverlayPos; 891ffaebc41bfd47472543d1ff86c8412c868fc4c9Amith Yamasani private int mOverlaySize; 909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private AbsListView mList; 929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private boolean mScrollCompleted; 939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int mVisibleItem; 949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Paint mPaint; 959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int mListOffset; 964df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project private int mItemCount = -1; 974df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project private boolean mLongList; 989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Object [] mSections; 1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private String mSectionText; 1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private boolean mDrawOverlay; 1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private ScrollFade mScrollFade; 1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private int mState; 1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private Handler mHandler = new Handler(); 1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private BaseAdapter mListAdapter; 1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private SectionIndexer mSectionIndexer; 1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private boolean mChangedBounds; 1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 11320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell private int mPosition; 11420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 11520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell private boolean mAlwaysShow; 11620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 11720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell private int mOverlayPosition; 11820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 11920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell private static final int FADE_TIMEOUT = 1500; 12020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 12120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell private final Rect mTmpRect = new Rect(); 12220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public FastScroller(Context context, AbsListView listView) { 1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mList = listView; 1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project init(context); 1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 12820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell public void setAlwaysShow(boolean alwaysShow) { 12920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mAlwaysShow = alwaysShow; 13020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell if (alwaysShow) { 13120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mHandler.removeCallbacks(mScrollFade); 13220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell setState(STATE_VISIBLE); 13320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell } else if (mState == STATE_VISIBLE) { 13420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mHandler.postDelayed(mScrollFade, FADE_TIMEOUT); 13520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell } 13620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell } 13720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 13820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell public boolean isAlwaysShowEnabled() { 13920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell return mAlwaysShow; 14020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell } 14120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 14220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell private void refreshDrawableState() { 14320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell int[] state = mState == STATE_DRAGGING ? PRESSED_STATES : DEFAULT_STATES; 14420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 14520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell if (mThumbDrawable != null && mThumbDrawable.isStateful()) { 14620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mThumbDrawable.setState(state); 14720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell } 14820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell if (mTrackDrawable != null && mTrackDrawable.isStateful()) { 14920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mTrackDrawable.setState(state); 15020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell } 15120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell } 15220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 15320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell public void setScrollbarPosition(int position) { 15420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mPosition = position; 15520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell switch (position) { 15620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell default: 15720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell case View.SCROLLBAR_POSITION_DEFAULT: 15820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell case View.SCROLLBAR_POSITION_RIGHT: 15920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mOverlayDrawable = mOverlayDrawableRight; 16020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell break; 16120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell case View.SCROLLBAR_POSITION_LEFT: 16220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mOverlayDrawable = mOverlayDrawableLeft; 16320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell break; 16420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell } 16520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell } 16620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 16720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell public int getWidth() { 16820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell return mThumbW; 16920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell } 17020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void setState(int state) { 1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project switch (state) { 1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case STATE_NONE: 1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mHandler.removeCallbacks(mScrollFade); 1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mList.invalidate(); 1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case STATE_VISIBLE: 1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mState != STATE_VISIBLE) { // Optimization 1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project resetThumbPos(); 1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Fall through 1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case STATE_DRAGGING: 1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mHandler.removeCallbacks(mScrollFade); 1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project case STATE_EXIT: 1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int viewWidth = mList.getWidth(); 1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mList.invalidate(viewWidth - mThumbW, mThumbY, viewWidth, mThumbY + mThumbH); 1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project break; 1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mState = state; 19120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell refreshDrawableState(); 1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public int getState() { 1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return mState; 1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void resetThumbPos() { 1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final int viewWidth = mList.getWidth(); 2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Bounds are always top right. Y coordinate get's translated during draw 20120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell switch (mPosition) { 20220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell case View.SCROLLBAR_POSITION_DEFAULT: 20320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell case View.SCROLLBAR_POSITION_RIGHT: 20420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mThumbDrawable.setBounds(viewWidth - mThumbW, 0, viewWidth, mThumbH); 20520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell break; 20620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell case View.SCROLLBAR_POSITION_LEFT: 20720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mThumbDrawable.setBounds(0, 0, mThumbW, mThumbH); 20820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell break; 20920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell } 2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mThumbDrawable.setAlpha(ScrollFade.ALPHA_MAX); 2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 213a5e441525660765e0913140daff4d12a0d94e88cMitsuru Oshima private void useThumbDrawable(Context context, Drawable drawable) { 2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mThumbDrawable = drawable; 21520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell if (drawable instanceof NinePatchDrawable) { 21620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mThumbW = context.getResources().getDimensionPixelSize( 21720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell com.android.internal.R.dimen.fastscroll_thumb_width); 21820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mThumbH = context.getResources().getDimensionPixelSize( 21920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell com.android.internal.R.dimen.fastscroll_thumb_height); 22020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell } else { 22120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mThumbW = drawable.getIntrinsicWidth(); 22220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mThumbH = drawable.getIntrinsicHeight(); 22320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell } 2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mChangedBounds = true; 2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void init(Context context) { 2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Get both the scrollbar states drawables 22920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell TypedArray ta = context.getTheme().obtainStyledAttributes(ATTRS); 230128b6ba93d0549fd2beff4482678e1229dc1cf3dAdam Powell useThumbDrawable(context, ta.getDrawable(THUMB_DRAWABLE)); 231128b6ba93d0549fd2beff4482678e1229dc1cf3dAdam Powell mTrackDrawable = ta.getDrawable(TRACK_DRAWABLE); 2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 233128b6ba93d0549fd2beff4482678e1229dc1cf3dAdam Powell mOverlayDrawableLeft = ta.getDrawable(PREVIEW_BACKGROUND_LEFT); 234128b6ba93d0549fd2beff4482678e1229dc1cf3dAdam Powell mOverlayDrawableRight = ta.getDrawable(PREVIEW_BACKGROUND_RIGHT); 235128b6ba93d0549fd2beff4482678e1229dc1cf3dAdam Powell mOverlayPosition = ta.getInt(OVERLAY_POSITION, OVERLAY_FLOATING); 2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mScrollCompleted = true; 2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 239d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy getSectionsFromIndexer(); 2401ffaebc41bfd47472543d1ff86c8412c868fc4c9Amith Yamasani 2411ffaebc41bfd47472543d1ff86c8412c868fc4c9Amith Yamasani mOverlaySize = context.getResources().getDimensionPixelSize( 2421ffaebc41bfd47472543d1ff86c8412c868fc4c9Amith Yamasani com.android.internal.R.dimen.fastscroll_overlay_size); 2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mOverlayPos = new RectF(); 2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mScrollFade = new ScrollFade(); 2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mPaint = new Paint(); 2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mPaint.setAntiAlias(true); 2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mPaint.setTextAlign(Paint.Align.CENTER); 2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mPaint.setTextSize(mOverlaySize / 2); 24920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 250b2e551711aa93a3d296ced6368e895743d43da80Adam Powell ColorStateList textColor = ta.getColorStateList(TEXT_COLOR); 2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int textColorNormal = textColor.getDefaultColor(); 2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mPaint.setColor(textColorNormal); 2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mPaint.setStyle(Paint.Style.FILL_AND_STROKE); 2549b38c6097e8d866fad1a12c1aba1a9c36e1725d7NoraBora 2559b38c6097e8d866fad1a12c1aba1a9c36e1725d7NoraBora // to show mOverlayDrawable properly 2569b38c6097e8d866fad1a12c1aba1a9c36e1725d7NoraBora if (mList.getWidth() > 0 && mList.getHeight() > 0) { 2579b38c6097e8d866fad1a12c1aba1a9c36e1725d7NoraBora onSizeChanged(mList.getWidth(), mList.getHeight(), 0, 0); 2589b38c6097e8d866fad1a12c1aba1a9c36e1725d7NoraBora } 2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mState = STATE_NONE; 26120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell refreshDrawableState(); 26220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 26320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell ta.recycle(); 26420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 26520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell setScrollbarPosition(mList.getVerticalScrollbarPosition()); 2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project void stop() { 2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project setState(STATE_NONE); 2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean isVisible() { 2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return !(mState == STATE_NONE); 2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void draw(Canvas canvas) { 2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mState == STATE_NONE) { 2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // No need to draw anything 2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final int y = mThumbY; 2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final int viewWidth = mList.getWidth(); 2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final FastScroller.ScrollFade scrollFade = mScrollFade; 2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int alpha = -1; 2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mState == STATE_EXIT) { 2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project alpha = scrollFade.getAlpha(); 2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (alpha < ScrollFade.ALPHA_MAX / 2) { 2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mThumbDrawable.setAlpha(alpha * 2); 2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 29320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell int left = 0; 29420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell switch (mPosition) { 29520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell case View.SCROLLBAR_POSITION_DEFAULT: 29620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell case View.SCROLLBAR_POSITION_RIGHT: 29720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell left = viewWidth - (mThumbW * alpha) / ScrollFade.ALPHA_MAX; 29820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell break; 29920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell case View.SCROLLBAR_POSITION_LEFT: 30020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell left = -mThumbW + (mThumbW * alpha) / ScrollFade.ALPHA_MAX; 30120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell break; 30220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell } 30320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mThumbDrawable.setBounds(left, 0, left + mThumbW, mThumbH); 3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mChangedBounds = true; 3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 30720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell if (mTrackDrawable != null) { 3082c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell final Rect thumbBounds = mThumbDrawable.getBounds(); 3092c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell final int left = thumbBounds.left; 3102c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell final int halfThumbHeight = (thumbBounds.bottom - thumbBounds.top) / 2; 31120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell final int trackWidth = mTrackDrawable.getIntrinsicWidth(); 3122c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell final int trackLeft = (left + mThumbW / 2) - trackWidth / 2; 3132c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell mTrackDrawable.setBounds(trackLeft, halfThumbHeight, 3142c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell trackLeft + trackWidth, mList.getHeight() - halfThumbHeight); 31520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mTrackDrawable.draw(canvas); 31620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell } 31720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project canvas.translate(0, y); 3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mThumbDrawable.draw(canvas); 3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project canvas.translate(0, -y); 3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // If user is dragging the scroll bar, draw the alphabet overlay 3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mState == STATE_DRAGGING && mDrawOverlay) { 32420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell if (mOverlayPosition == OVERLAY_AT_THUMB) { 32520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell int left = 0; 32620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell switch (mPosition) { 32720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell default: 32820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell case View.SCROLLBAR_POSITION_DEFAULT: 32920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell case View.SCROLLBAR_POSITION_RIGHT: 33020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell left = Math.max(0, 33120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mThumbDrawable.getBounds().left - mThumbW - mOverlaySize); 33220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell break; 33320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell case View.SCROLLBAR_POSITION_LEFT: 33420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell left = Math.min(mThumbDrawable.getBounds().right + mThumbW, 33520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mList.getWidth() - mOverlaySize); 33620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell break; 33720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell } 33820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 33920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell int top = Math.max(0, 34020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell Math.min(y + (mThumbH - mOverlaySize) / 2, mList.getHeight() - mOverlaySize)); 34120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 34220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell final RectF pos = mOverlayPos; 34320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell pos.left = left; 34420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell pos.right = pos.left + mOverlaySize; 34520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell pos.top = top; 34620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell pos.bottom = pos.top + mOverlaySize; 34720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell if (mOverlayDrawable != null) { 34820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mOverlayDrawable.setBounds((int) pos.left, (int) pos.top, 34920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell (int) pos.right, (int) pos.bottom); 35020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell } 35120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell } 3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mOverlayDrawable.draw(canvas); 3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final Paint paint = mPaint; 3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float descent = paint.descent(); 3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final RectF rectF = mOverlayPos; 35620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell final Rect tmpRect = mTmpRect; 35720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mOverlayDrawable.getPadding(tmpRect); 35820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell final int hOff = (tmpRect.right - tmpRect.left) / 2; 35920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell final int vOff = (tmpRect.bottom - tmpRect.top) / 2; 36020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell canvas.drawText(mSectionText, (int) (rectF.left + rectF.right) / 2 - hOff, 36120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell (int) (rectF.bottom + rectF.top) / 2 + mOverlaySize / 4 - descent - vOff, 36220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell paint); 3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (mState == STATE_EXIT) { 3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (alpha == 0) { // Done with exit 3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project setState(STATE_NONE); 3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mList.invalidate(viewWidth - mThumbW, y, viewWidth, y + mThumbH); 3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project void onSizeChanged(int w, int h, int oldw, int oldh) { 3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mThumbDrawable != null) { 37420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell switch (mPosition) { 37520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell default: 37620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell case View.SCROLLBAR_POSITION_DEFAULT: 37720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell case View.SCROLLBAR_POSITION_RIGHT: 37820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mThumbDrawable.setBounds(w - mThumbW, 0, w, mThumbH); 37920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell break; 38020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell case View.SCROLLBAR_POSITION_LEFT: 38120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mThumbDrawable.setBounds(0, 0, mThumbW, mThumbH); 38220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell break; 38320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell } 3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 38520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell if (mOverlayPosition == OVERLAY_FLOATING) { 38620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell final RectF pos = mOverlayPos; 38720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell pos.left = (w - mOverlaySize) / 2; 38820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell pos.right = pos.left + mOverlaySize; 38920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell pos.top = h / 10; // 10% from top 39020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell pos.bottom = pos.top + mOverlaySize; 39120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell if (mOverlayDrawable != null) { 39220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mOverlayDrawable.setBounds((int) pos.left, (int) pos.top, 39320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell (int) pos.right, (int) pos.bottom); 39420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell } 3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 3972c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell 3982c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell void onItemCountChanged(int oldCount, int newCount) { 3992c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell if (mAlwaysShow) { 4002c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell mLongList = true; 4012c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell } 4022c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell } 4032c6196aa557e4eaf09d1ec3ad1347742f7f9f7b5Adam Powell 4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, 4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int totalItemCount) { 4064df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project // Are there enough pages to require fast scroll? Recompute only if total count changes 4074df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project if (mItemCount != totalItemCount && visibleItemCount > 0) { 4084df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project mItemCount = totalItemCount; 40932c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell mLongList = mItemCount / visibleItemCount >= MIN_PAGES; 41032c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell } 41132c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell if (mAlwaysShow) { 41232c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell mLongList = true; 4134df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project } 4144df2423a947bcd3f024cc3d3a1a315a8dc428598The Android Open Source Project if (!mLongList) { 4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mState != STATE_NONE) { 4169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project setState(STATE_NONE); 4179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (totalItemCount - visibleItemCount > 0 && mState != STATE_DRAGGING ) { 42132c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell mThumbY = getThumbPositionForListPosition(firstVisibleItem, visibleItemCount, 42232c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell totalItemCount); 4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mChangedBounds) { 4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project resetThumbPos(); 4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mChangedBounds = false; 4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mScrollCompleted = true; 4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (firstVisibleItem == mVisibleItem) { 4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mVisibleItem = firstVisibleItem; 4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mState != STATE_DRAGGING) { 4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project setState(STATE_VISIBLE); 43520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell if (!mAlwaysShow) { 43620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mHandler.postDelayed(mScrollFade, FADE_TIMEOUT); 43720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell } 4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 441d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy SectionIndexer getSectionIndexer() { 442d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy return mSectionIndexer; 443d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy } 444d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy 445d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy Object[] getSections() { 446d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy if (mListAdapter == null && mList != null) { 447d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy getSectionsFromIndexer(); 448d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy } 449d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy return mSections; 450d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy } 451d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy 452d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy private void getSectionsFromIndexer() { 4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project Adapter adapter = mList.getAdapter(); 4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSectionIndexer = null; 4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (adapter instanceof HeaderViewListAdapter) { 4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mListOffset = ((HeaderViewListAdapter)adapter).getHeadersCount(); 4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project adapter = ((HeaderViewListAdapter)adapter).getWrappedAdapter(); 4589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (adapter instanceof ExpandableListConnector) { 4609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ExpandableListAdapter expAdapter = ((ExpandableListConnector)adapter).getAdapter(); 4619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (expAdapter instanceof SectionIndexer) { 4629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSectionIndexer = (SectionIndexer) expAdapter; 4639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mListAdapter = (BaseAdapter) adapter; 4649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSections = mSectionIndexer.getSections(); 4659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 4679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (adapter instanceof SectionIndexer) { 4689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mListAdapter = (BaseAdapter) adapter; 4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSectionIndexer = (SectionIndexer) adapter; 4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSections = mSectionIndexer.getSections(); 4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mListAdapter = (BaseAdapter) adapter; 4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSections = new String[] { " " }; 4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 4799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void scrollTo(float position) { 4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int count = mList.getCount(); 4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mScrollCompleted = false; 4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float fThreshold = (1.0f / count) / 8; 4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final Object[] sections = mSections; 4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int sectionIndex; 4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (sections != null && sections.length > 1) { 4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final int nSections = sections.length; 4879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int section = (int) (position * nSections); 4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (section >= nSections) { 4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project section = nSections - 1; 4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int exactSection = section; 4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project sectionIndex = section; 4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int index = mSectionIndexer.getPositionForSection(section); 4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Given the expected section and index, the following code will 4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // try to account for missing sections (no names starting with..) 4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // It will compute the scroll space of surrounding empty sections 4979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // and interpolate the currently visible letter's range across the 4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // available space, so that there is always some list movement while 4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // the user moves the thumb. 5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int nextIndex = count; 5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int prevIndex = index; 5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int prevSection = section; 5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int nextSection = section + 1; 5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Assume the next section is unique 5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (section < nSections - 1) { 5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project nextIndex = mSectionIndexer.getPositionForSection(section + 1); 5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Find the previous index if we're slicing the previous section 5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (nextIndex == index) { 5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Non-existent letter 5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (section > 0) { 5139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project section--; 514b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project prevIndex = mSectionIndexer.getPositionForSection(section); 515b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project if (prevIndex != index) { 516b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project prevSection = section; 517b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project sectionIndex = section; 518b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project break; 519b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project } else if (section == 0) { 520b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project // When section reaches 0 here, sectionIndex must follow it. 521b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project // Assuming mSectionIndexer.getPositionForSection(0) == 0. 522b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project sectionIndex = 0; 523b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project break; 524b2a3dd88a53cc8c6d19f6dc8ec4f3d6c4abd9b54The Android Open Source Project } 5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Find the next index, in case the assumed next index is not 5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // unique. For instance, if there is no P, then request for P's 5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // position actually returns Q's. So we need to look ahead to make 5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // sure that there is really a Q at Q's position. If not, move 5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // further down... 5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int nextNextSection = nextSection + 1; 5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project while (nextNextSection < nSections && 5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mSectionIndexer.getPositionForSection(nextNextSection) == nextIndex) { 5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project nextNextSection++; 5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project nextSection++; 5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Compute the beginning and ending scroll range percentage of the 5399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // currently visible letter. This could be equal to or greater than 5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // (1 / nSections). 5419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float fPrev = (float) prevSection / nSections; 5429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project float fNext = (float) nextSection / nSections; 5439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (prevSection == exactSection && position - fPrev < fThreshold) { 5449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project index = prevIndex; 5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project index = prevIndex + (int) ((nextIndex - prevIndex) * (position - fPrev) 5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project / (fNext - fPrev)); 5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Don't overflow 5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (index > count - 1) index = count - 1; 5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mList instanceof ExpandableListView) { 5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ExpandableListView expList = (ExpandableListView) mList; 5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project expList.setSelectionFromTop(expList.getFlatListPosition( 5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ExpandableListView.getPackedPositionForGroup(index + mListOffset)), 0); 5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (mList instanceof ListView) { 5579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ((ListView)mList).setSelectionFromTop(index + mListOffset, 0); 5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mList.setSelection(index + mListOffset); 5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int index = (int) (position * count); 5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mList instanceof ExpandableListView) { 5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ExpandableListView expList = (ExpandableListView) mList; 5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project expList.setSelectionFromTop(expList.getFlatListPosition( 5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ExpandableListView.getPackedPositionForGroup(index + mListOffset)), 0); 5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (mList instanceof ListView) { 5689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project ((ListView)mList).setSelectionFromTop(index + mListOffset, 0); 5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mList.setSelection(index + mListOffset); 5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project sectionIndex = -1; 5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (sectionIndex >= 0) { 5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project String text = mSectionText = sections[sectionIndex].toString(); 5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mDrawOverlay = (text.length() != 1 || text.charAt(0) != ' ') && 5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project sectionIndex < sections.length; 5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mDrawOverlay = false; 5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 58432c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell private int getThumbPositionForListPosition(int firstVisibleItem, int visibleItemCount, 58532c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell int totalItemCount) { 58632c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell if (mSectionIndexer == null) { 58732c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell getSectionsFromIndexer(); 58832c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell } 58932c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell if (mSectionIndexer == null) { 59032c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell return ((mList.getHeight() - mThumbH) * firstVisibleItem) 59132c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell / (totalItemCount - visibleItemCount); 59232c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell } 59332c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell 59432c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell firstVisibleItem -= mListOffset; 59532c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell if (firstVisibleItem < 0) { 59632c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell return 0; 59732c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell } 59832c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell totalItemCount -= mListOffset; 59932c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell 60032c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell final int trackHeight = mList.getHeight() - mThumbH; 60132c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell 60232c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell final int section = mSectionIndexer.getSectionForPosition(firstVisibleItem); 60332c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell final int sectionPos = mSectionIndexer.getPositionForSection(section); 60432c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell final int nextSectionPos = mSectionIndexer.getPositionForSection(section + 1); 60532c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell final int sectionCount = mSectionIndexer.getSections().length; 60632c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell final int positionsInSection = nextSectionPos - sectionPos; 60732c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell 60832c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell final View child = mList.getChildAt(0); 60932aa2c90ee6e12f6c53c7d572d5c02f1d795b8f7Adam Powell final float incrementalPos = child == null ? 0 : firstVisibleItem + 61032c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell (float) (mList.getPaddingTop() - child.getTop()) / child.getHeight(); 61132c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell final float posWithinSection = (incrementalPos - sectionPos) / positionsInSection; 61232c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell int result = (int) ((section + posWithinSection) / sectionCount * trackHeight); 61332c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell 61432c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell // Fake out the scrollbar for the last item. Since the section indexer won't 61532c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell // ever actually move the list in this end space, make scrolling across the last item 61632c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell // account for whatever space is remaining. 61732c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell if (firstVisibleItem > 0 && firstVisibleItem + visibleItemCount == totalItemCount) { 61832c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell final View lastChild = mList.getChildAt(visibleItemCount - 1); 61932c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell final float lastItemVisible = (float) (mList.getHeight() - mList.getPaddingBottom() 62032c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell - lastChild.getTop()) / lastChild.getHeight(); 62132c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell result += (trackHeight - result) * lastItemVisible; 62232c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell } 62332c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell 62432c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell return result; 62532c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell } 62632c3a6929af9d63de3bf45a61be6e1a4bde136d3Adam Powell 6279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project private void cancelFling() { 6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Cancel the list fling 6299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project MotionEvent cancelFling = MotionEvent.obtain(0, 0, MotionEvent.ACTION_CANCEL, 0, 0, 0); 6309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mList.onTouchEvent(cancelFling); 6319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project cancelFling.recycle(); 6329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean onInterceptTouchEvent(MotionEvent ev) { 6359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mState > STATE_NONE && ev.getAction() == MotionEvent.ACTION_DOWN) { 63682f3495b146b267f3786997752cef25310176349Romain Guy if (isPointInside(ev.getX(), ev.getY())) { 6379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project setState(STATE_DRAGGING); 6389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 6399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 6429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project boolean onTouchEvent(MotionEvent me) { 6459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mState == STATE_NONE) { 6469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 6479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 64882f3495b146b267f3786997752cef25310176349Romain Guy 64982f3495b146b267f3786997752cef25310176349Romain Guy final int action = me.getAction(); 65082f3495b146b267f3786997752cef25310176349Romain Guy 65182f3495b146b267f3786997752cef25310176349Romain Guy if (action == MotionEvent.ACTION_DOWN) { 65282f3495b146b267f3786997752cef25310176349Romain Guy if (isPointInside(me.getX(), me.getY())) { 6539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project setState(STATE_DRAGGING); 6549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mListAdapter == null && mList != null) { 655d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy getSectionsFromIndexer(); 6569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 657d43fe07852a23c78da3b51b45469687c20be919aMarco Nelissen if (mList != null) { 658d43fe07852a23c78da3b51b45469687c20be919aMarco Nelissen mList.requestDisallowInterceptTouchEvent(true); 659d43bd48fe311fa6320e46ca9b9ce25241c476ac6Adam Powell mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL); 660d43fe07852a23c78da3b51b45469687c20be919aMarco Nelissen } 6619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 6629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project cancelFling(); 6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 665cd3e4ad4fb526c5f36f64f1d1a39a0b3c5e4c462Marco Nelissen } else if (action == MotionEvent.ACTION_UP) { // don't add ACTION_CANCEL here 6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mState == STATE_DRAGGING) { 667d43fe07852a23c78da3b51b45469687c20be919aMarco Nelissen if (mList != null) { 668d43fe07852a23c78da3b51b45469687c20be919aMarco Nelissen // ViewGroup does the right thing already, but there might 669d43fe07852a23c78da3b51b45469687c20be919aMarco Nelissen // be other classes that don't properly reset on touch-up, 670d43fe07852a23c78da3b51b45469687c20be919aMarco Nelissen // so do this explicitly just in case. 671d43fe07852a23c78da3b51b45469687c20be919aMarco Nelissen mList.requestDisallowInterceptTouchEvent(false); 672d43bd48fe311fa6320e46ca9b9ce25241c476ac6Adam Powell mList.reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); 673d43fe07852a23c78da3b51b45469687c20be919aMarco Nelissen } 6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project setState(STATE_VISIBLE); 6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final Handler handler = mHandler; 6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project handler.removeCallbacks(mScrollFade); 67720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell if (!mAlwaysShow) { 67820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell handler.postDelayed(mScrollFade, 1000); 67920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell } 68020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 68120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell mList.invalidate(); 6829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 6839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 68482f3495b146b267f3786997752cef25310176349Romain Guy } else if (action == MotionEvent.ACTION_MOVE) { 6859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mState == STATE_DRAGGING) { 6869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project final int viewHeight = mList.getHeight(); 6879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // Jitter 6889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int newThumbY = (int) me.getY() - mThumbH + 10; 6899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (newThumbY < 0) { 6909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project newThumbY = 0; 6919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else if (newThumbY + mThumbH > viewHeight) { 6929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project newThumbY = viewHeight - mThumbH; 6939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (Math.abs(mThumbY - newThumbY) < 2) { 6959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 6969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 6979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mThumbY = newThumbY; 6989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project // If the previous scrollTo is still pending 6999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (mScrollCompleted) { 7009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project scrollTo((float) mThumbY / (viewHeight - mThumbH)); 7019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return true; 7039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return false; 7069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 707d6a463a9f23b3901bf729f2f27a6bb8f78b95248Romain Guy 70882f3495b146b267f3786997752cef25310176349Romain Guy boolean isPointInside(float x, float y) { 70920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell boolean inTrack = false; 71020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell switch (mPosition) { 71120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell default: 71220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell case View.SCROLLBAR_POSITION_DEFAULT: 71320232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell case View.SCROLLBAR_POSITION_RIGHT: 71420232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell inTrack = x > mList.getWidth() - mThumbW; 71520232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell break; 71620232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell case View.SCROLLBAR_POSITION_LEFT: 71720232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell inTrack = x < mThumbW; 71820232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell break; 71920232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell } 72020232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell 72120232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell // Allow taps in the track to start moving. 72220232d0f7ce2220df72dd78bed052f6b4a643f10Adam Powell return inTrack && (mTrackDrawable != null || y >= mThumbY && y <= mThumbY + mThumbH); 72382f3495b146b267f3786997752cef25310176349Romain Guy } 72482f3495b146b267f3786997752cef25310176349Romain Guy 7259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public class ScrollFade implements Runnable { 7269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project long mStartTime; 7289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project long mFadeDuration; 7299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project static final int ALPHA_MAX = 208; 7309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project static final long FADE_DURATION = 200; 7319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project void startFade() { 7339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mFadeDuration = FADE_DURATION; 7349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mStartTime = SystemClock.uptimeMillis(); 7359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project setState(STATE_EXIT); 7369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int getAlpha() { 7399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (getState() != STATE_EXIT) { 7409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return ALPHA_MAX; 7419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project int alpha; 7439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project long now = SystemClock.uptimeMillis(); 7449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (now > mStartTime + mFadeDuration) { 7459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project alpha = 0; 7469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 7479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project alpha = (int) (ALPHA_MAX - ((now - mStartTime) * ALPHA_MAX) / mFadeDuration); 7489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return alpha; 7509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project public void run() { 7539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (getState() != STATE_EXIT) { 7549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project startFade(); 7559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project return; 7569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project 7589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project if (getAlpha() > 0) { 7599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project mList.invalidate(); 7609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } else { 7619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project setState(STATE_NONE); 7629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project } 7659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project} 766