爲Android HorizontalListView添加Scrollbar

最近由於項目需要,要實現一個橫向滾動的列表,打算直接用HorizontalScrollView,但是因爲沒有視圖複用,列表項多了之後會佔用較多內存,排除;再想到用Gallery,但Gallery有一個自動定位到中央的動畫效果,要去除這個效果的工作比較複雜。最後搜了一下找到了這個Android-HorizontalListView,實現了ListView的大部分功能,只是不支持Header、Footer和ScrollBar,項目要求提供Scroolbar的顯示,所以就在上面動手吧,添加橫向的Scrollbar。

ScrollBar作爲一個基礎功能被實現在View中,子類需要實現computeXXXScrollRange、computeXXXScrollExtent和computeXXXScrollOffset這三個方法,其中:

1、computeXXXScrollRange,計算滾動條的滾動範圍

2、computeXXXScrollExtent,計算滑塊顯示的大小

3、computeXXXScrollOffset,計算滾動的偏移量,也就是當前滾動的位置

上面這三個方法返回的值都是相對值,也就是計算的時候採用一個統一的度量,具體視圖上的顯示在View的onDrawScrollbar()方法中已經實現。

有了這三個方法之後,還需要調用在滾動的過程中調用awakenScrollbars(),因爲Scrollbar在停止滾動後會自動隱藏,所以就需要在滾動的時候不斷喚起scrollbar讓它顯示出來,也就是這個方法。

HorizontalListView通過監聽滾動手勢來不斷requestLayout,重新佈局列表的顯示來達到滾動的效果,所以awakenScrollbars這個方法就添加在onLayout中。

protected void onLayout(boolean changed, int left, int top, int right,
			int bottom) {
		super.onLayout(changed, left, top, right, bottom);

		if (mAdapter == null) {
			return;
		}

		// Force the OS to redraw this view
		if (!awakenScrollBars()) {
			postInvalidate();
		}

		...
}

下面添加scrollbar相關的計算

@Override
	protected int computeHorizontalScrollRange() {
		int count = mAdapter.getCount();
		return Math.max(count * 100, 0);
	}

	@Override
	protected int computeHorizontalScrollExtent() {
		int count = getChildCount();
		if (count > 0) {
			int extent = count * 100;

			View view = getChildAt(0);
			final int left = view.getLeft();
			int width = view.getWidth();
			if (width > 0) {
				extent += (left * 100) / width;
			}

			view = getChildAt(count - 1);
			final int right = view.getRight();
			width = view.getWidth();
			if (width > 0) {
				extent -= ((right - getWidth()) * 100) / width;
			}

			return extent;

		}
		return 0;
	}

	@Override
	protected int computeHorizontalScrollOffset() {
		final int firstPosition = mLeftViewAdapterIndex;
		final int childCount = getChildCount();
		if (firstPosition >= 0 && childCount > 0) {
			final View view = getChildAt(0);
            final int left = view.getLeft();
            int width = view.getWidth();
            if (width > 0) {
            	int result = Math.max(firstPosition * 100 - (left * 100) / width, 0);
                return result;
            }
		}
		return 0;
	}

這裏參考了AbsListView中的實現,上面的計算過程比較容易理解,×100是爲了保持兩位小數的精度。range的值就是總的列表項個數(mAdapter.getCount());滑塊的大小extent是當前顯示的個數,然後去除左右邊界超出的部分來提高精度;offset是顯示的第一個列表的索引值。也就是說上面採用了“個數”爲度量單位進行計算。


發佈了54 篇原創文章 · 獲贊 12 · 訪問量 72萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章