Code example for AbsListView

Methods: getChildAt, getHeight, getPaddingBottom, getPaddingTop, getWidth

0
			// Fall through 
		case STATE_DRAGGING:
			mHandler.removeCallbacks(mScrollFade);
			break; 
		case STATE_EXIT:
			int viewWidth = mList.getWidth();
			doInvalidate(viewWidth - mThumbW, mThumbY, viewWidth, mThumbY + mThumbH);
			break; 
		} 
		mState = state;
		refreshDrawableState(); 
	} 
 
	public int getState() { 
		return mState;
	} 
 
	/** 
	 * Updates the section list. Should be called from the adapter when the data is invalidated or 
	 * changed. 
	 *  
	 * @param resetDragging 
	 *            True to reset current dragging state, false to keep dragging. Depending on how 
	 *            much the underlying data changed, preserving the drag state may or may not be 
	 *            confusing to the user. It's up to the application to make that decision and call 
	 *            with the correct resetDraggging value. 
	 */ 
	public void updateSections(boolean resetDragging) {
		mListAdapter = null;
		if (mFastScrollEnabled) {
			if (mList != null) {
				getSectionsFromIndexer(); 
 
				if (resetDragging) {
					if (mState == STATE_DRAGGING) {
						mList.requestDisallowInterceptTouchEvent(false);
						setState(STATE_VISIBLE);
						final Handler handler = mHandler;
						handler.removeCallbacks(mScrollFade);
						if (!mAlwaysShow) {
							handler.postDelayed(mScrollFade, FADE_TIMEOUT);
						} 
						doInvalidate(); 
					} 
				} 
			} 
		} 
	} 
 
	@Override 
	public void refreshDrawableState() { 
		int[] state = mState == STATE_DRAGGING ? PRESSED_STATES : DEFAULT_STATES;
 
		if (mThumbDrawable != null && mThumbDrawable.isStateful()) {
			mThumbDrawable.setState(state);
		} 
	} 
 
	@Override 
	public void onChildViewAdded(View parent, View child) {
		if (child instanceof AbsListView) {
			initListView((AbsListView) child);
		} 
	} 
 
	@Override 
	public void onChildViewRemoved(View parent, View child) {
		if (child == mList) {
			mList = null;
			mListAdapter = null;
			mSections = null;
		} 
	} 
 
	@Override 
	public void draw(Canvas canvas) {
		super.draw(canvas);
 
		if (mState == STATE_NONE || !mFastScrollEnabled) {
			// No need to draw anything 
			return; 
		} 
 
		final int y = mThumbY;
		final int viewWidth = mList.getWidth();
		final int viewHeight = mList.getHeight();
		final ScrollFade scrollFade = mScrollFade;
 
		int alpha = -1;
		if (mState == STATE_EXIT) {
			alpha = scrollFade.getAlpha();
			if (alpha < ScrollFade.ALPHA_MAX / 2) {
				mThumbDrawable.setAlpha(alpha * 2);
			} 
			int left = viewWidth - (mThumbW * alpha) / ScrollFade.ALPHA_MAX;
			mThumbDrawable.setBounds(left, 0, left + mThumbW, mThumbH);
			mChangedBounds = true;
		} 
 
		canvas.translate(0, y);
		mThumbDrawable.draw(canvas);
		canvas.translate(0, -y);
 
		// If user is dragging the scroll bar, draw the alphabet overlay 
		if (mState == STATE_DRAGGING && mDrawOverlay) {
			if (mOverlayPosition == OVERLAY_AT_THUMB) {
				int left = Math.max(0, viewWidth - mThumbW - mOverlayW);
				int top = Math.max(0, Math.min(y + (mThumbH - mOverlayH) / 2, viewHeight - mOverlayH));
 
				final RectF pos = mOverlayPos;
				pos.left = left;
				pos.right = pos.left + mOverlayW;
				pos.top = top;
				pos.bottom = pos.top + mOverlayH;
				if (mOverlayDrawable != null) {
					mOverlayDrawable.setBounds((int) pos.left, (int) pos.top, (int) pos.right, (int) pos.bottom);
				} 
			} 
			mOverlayDrawable.draw(canvas);
			final Paint paint = mPaint;
			float ascent = paint.ascent();
			float descent = paint.descent();
			final RectF rectF = mOverlayPos;
			final Rect tmpRect = mTmpRect;
			mOverlayDrawable.getPadding(tmpRect);
			final int hOff = (tmpRect.right - tmpRect.left) / 2;
			final int vOff = (tmpRect.bottom - tmpRect.top) / 2;
			canvas.drawText(mSectionText, (int) (rectF.left + rectF.right) / 2 - hOff, (int) (rectF.bottom + rectF.top)
					/ 2 - (ascent + descent) / 2 - vOff, paint);
		} else if (mState == STATE_EXIT) {
			if (alpha == 0) { // Done with exit
				setState(STATE_NONE);
			} else { 
				doInvalidate(viewWidth - mThumbW, y, viewWidth, y + mThumbH);
			} 
		} 
	} 
 
	@Override 
	public void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
 
		if (mThumbDrawable != null) {
			mThumbDrawable.setBounds(w - mThumbW, 0, w, mThumbH);
		} 
		if (mOverlayPosition == OVERLAY_FLOATING) {
			final RectF pos = mOverlayPos;
			pos.left = (w - mOverlayW) / 2;
			pos.right = pos.left + mOverlayW;
			pos.top = h / 10; // 10% from top
			pos.bottom = pos.top + mOverlayH;
			if (mOverlayDrawable != null) {
				mOverlayDrawable.setBounds((int) pos.left, (int) pos.top, (int) pos.right, (int) pos.bottom);
			} 
		} 
	} 
 
	@Override 
	public void onScrollStateChanged(AbsListView view, int scrollState) {
	} 
 
	@Override 
	public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
		updateScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
	} 
 
	private void updateScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
		// Are there enough pages to require fast scroll? Recompute only if 
		// total count changes 
		if (mItemCount != totalItemCount && visibleItemCount > 0) {
			mItemCount = totalItemCount;
			mLongList = mItemCount / visibleItemCount >= MIN_PAGES;
		} 
		if (mAlwaysShow) {
			mLongList = true;
		} 
		if (!mLongList) {
			if (mState != STATE_NONE) {
				setState(STATE_NONE);
			} 
			return; 
		} 
 
		if (totalItemCount - visibleItemCount > 0 && mState != STATE_DRAGGING) {
			final int viewWidth = mList.getWidth();
			doInvalidate(viewWidth - mThumbW, mThumbY, viewWidth, mThumbY + mThumbH);
 
			mThumbY = getThumbPositionForListPosition(firstVisibleItem, visibleItemCount, totalItemCount);
 
			doInvalidate(viewWidth - mThumbW, mThumbY, viewWidth, mThumbY + mThumbH);
 
			if (mChangedBounds) {
				resetThumbPos(); 
				mChangedBounds = false;
			} 
		} 
		mScrollCompleted = true;
		if (firstVisibleItem == mVisibleItem) {
			return; 
		} 
		mVisibleItem = firstVisibleItem;
		if (mState != STATE_DRAGGING) {
			setState(STATE_VISIBLE);
			final Handler handler = mHandler;
			handler.removeCallbacks(mScrollFade);
			if (!mAlwaysShow) {
				handler.postDelayed(mScrollFade, FADE_TIMEOUT);
			} 
		} 
	} 
 
	private void setThumbDrawable(Drawable drawable) {
		mThumbDrawable = drawable;
		if (drawable instanceof NinePatchDrawable) {
			Resources res = getResources();
			mThumbW = res.getDimensionPixelSize(R.dimen.fastscroll_thumb_width);
			mThumbH = res.getDimensionPixelSize(R.dimen.fastscroll_thumb_height);
		} else { 
			mThumbW = drawable.getIntrinsicWidth();
			mThumbH = drawable.getIntrinsicHeight();
		} 
		mChangedBounds = true;
	} 
 
	private void resetThumbPos() { 
		final int viewWidth = mList.getWidth();
		mThumbDrawable.setBounds(viewWidth - mThumbW, 0, viewWidth, mThumbH);
		mThumbDrawable.setAlpha(ScrollFade.ALPHA_MAX);
	} 
 
	private void getSectionsFromIndexer() { 
		Adapter adapter = mList.getAdapter();
		mSectionIndexer = null;
 
		if (!mFastScrollEnabled) {
			mListAdapter = (BaseAdapter) adapter;
			mSections = new String[] { " " };
			return; 
		} 
 
		if (adapter instanceof HeaderViewListAdapter) {
			mListOffset = ((HeaderViewListAdapter) adapter).getHeadersCount();
			adapter = ((HeaderViewListAdapter) adapter).getWrappedAdapter();
		} 
 
		if (adapter instanceof SectionIndexer) {
			mListAdapter = (BaseAdapter) adapter;
			mSectionIndexer = (SectionIndexer) adapter;
			mSections = mSectionIndexer.getSections();
			if (mSections == null) {
				mSections = new String[] { " " };
			} 
		} else { 
			mListAdapter = (BaseAdapter) adapter;
			mSections = new String[] { " " };
		} 
	} 
 
	private void scrollTo(float position) {
		int count = mList.getCount();
		mScrollCompleted = false;
		float fThreshold = (1.0f / count) / 8;
		final Object[] sections = mSections;
		int sectionIndex;
		if (sections != null && sections.length > 1) {
			final int nSections = sections.length;
			int section = (int) (position * nSections);
			if (section >= nSections) {
				section = nSections - 1;
			} 
			int exactSection = section;
			sectionIndex = section;
			int index = mSectionIndexer.getPositionForSection(section);
			// Given the expected section and index, the following code will 
			// try to account for missing sections (no names starting with..) 
			// It will compute the scroll space of surrounding empty sections 
			// and interpolate the currently visible letter's range across the 
			// available space, so that there is always some list movement while 
			// the user moves the thumb. 
			int nextIndex = count;
			int prevIndex = index;
			int prevSection = section;
			int nextSection = section + 1;
			// Assume the next section is unique 
			if (section < nSections - 1) {
				nextIndex = mSectionIndexer.getPositionForSection(section + 1);
			} 
 
			// Find the previous index if we're slicing the previous section 
			if (nextIndex == index) {
				// Non-existent letter 
				while (section > 0) {
					section--;
					prevIndex = mSectionIndexer.getPositionForSection(section);
					if (prevIndex != index) {
						prevSection = section;
						sectionIndex = section;
						break; 
					} else if (section == 0) {
						// When section reaches 0 here, sectionIndex must follow 
						// it. 
						// Assuming mSectionIndexer.getPositionForSection(0) == 
						// 0. 
						sectionIndex = 0;
						break; 
					} 
				} 
			} 
			// Find the next index, in case the assumed next index is not 
			// unique. For instance, if there is no P, then request for P's 
			// position actually returns Q's. So we need to look ahead to make 
			// sure that there is really a Q at Q's position. If not, move 
			// further down... 
			int nextNextSection = nextSection + 1;
			while (nextNextSection < nSections && mSectionIndexer.getPositionForSection(nextNextSection) == nextIndex) {
				nextNextSection++;
				nextSection++;
			} 
			// Compute the beginning and ending scroll range percentage of the 
			// currently visible letter. This could be equal to or greater than 
			// (1 / nSections). 
			float fPrev = (float) prevSection / nSections;
			float fNext = (float) nextSection / nSections;
			if (prevSection == exactSection && position - fPrev < fThreshold) {
				index = prevIndex;
			} else { 
				index = prevIndex + (int) ((nextIndex - prevIndex) * (position - fPrev) / (fNext - fPrev));
			} 
			// Don't overflow 
			if (index > count - 1) index = count - 1;
 
			if (mList instanceof ListView) {
				((ListView) mList).setSelectionFromTop(index + mListOffset, 0);
			} else { 
				mList.setSelection(index + mListOffset);
			} 
		} else { 
			int index = (int) (position * count);
			// Don't overflow 
			if (index > count - 1) index = count - 1;
 
			if (mList instanceof ExpandableListView) {
				ExpandableListView expList = (ExpandableListView) mList;
				expList.setSelectionFromTop(
						expList.getFlatListPosition(ExpandableListView.getPackedPositionForGroup(index + mListOffset)),
						0); 
			} else if (mList instanceof ListView) {
				((ListView) mList).setSelectionFromTop(index + mListOffset, 0);
			} else { 
				mList.setSelection(index + mListOffset);
			} 
			sectionIndex = -1;
		} 
 
		if (sectionIndex >= 0) {
			String text = mSectionText = sections[sectionIndex].toString();
			mDrawOverlay = (text.length() != 1 || text.charAt(0) != ' ') && sectionIndex < sections.length;
		} else { 
			mDrawOverlay = false;
		} 
	} 
 
	private int getThumbPositionForListPosition(int firstVisibleItem, int visibleItemCount, int totalItemCount) {
		if (mSectionIndexer == null || mListAdapter == null) {
			getSectionsFromIndexer(); 
		} 
		if (mSectionIndexer == null || !mMatchDragPosition) {
			int position = ((mList.getHeight() - mThumbH) * firstVisibleItem) / (totalItemCount - visibleItemCount);
			return position;
		} 
 
		firstVisibleItem -= mListOffset;
		if (firstVisibleItem < 0) {
			return 0; 
		} 
		totalItemCount -= mListOffset;
 
		final int trackHeight = mList.getHeight() - mThumbH;
 
		final int section = mSectionIndexer.getSectionForPosition(firstVisibleItem);
		final int sectionPos = mSectionIndexer.getPositionForSection(section);
		final int nextSectionPos = mSectionIndexer.getPositionForSection(section + 1);
		final int sectionCount = mSections.length;
		final int positionsInSection = nextSectionPos - sectionPos;
 
		final View child = mList.getChildAt(0);
		final float incrementalPos = child == null ? 0 : firstVisibleItem
				+ (float) (mList.getPaddingTop() - child.getTop()) / child.getHeight();
		final float posWithinSection = (incrementalPos - sectionPos) / positionsInSection;
		int result = (int) ((section + posWithinSection) / sectionCount * trackHeight);
 
		// Fake out the scrollbar for the last item. Since the section indexer 
		// won't 
		// ever actually move the list in this end space, make scrolling across 
		// the last item 
		// account for whatever space is remaining. 
		if (firstVisibleItem > 0 && firstVisibleItem + visibleItemCount == totalItemCount) {
			final View lastChild = mList.getChildAt(visibleItemCount - 1);
			final float lastItemVisible = (float) (mList.getHeight() - mList.getPaddingBottom() - lastChild.getTop())
					/ lastChild.getHeight();
			result += (trackHeight - result) * lastItemVisible;
		} 
 
		return result;