判斷listview的滾動狀態及在滾動中item顯示位置的方法


在寫listview的下拉刷新和上拉加載時,會遇到如下問題:
怎樣判斷listview滑動到了頂部或是底部?
下拉刷新時,一般只有當第一個item完全可見時,才處理下拉的邏輯,而上拉加載則處理方式比較多,可以在手指已經離開屏幕但listview由於慣性作用滑動到底部時自動加載,也可以在最底部顯示一個按鈕,點擊加載,加載完成后,又將按鈕置於新加載內容的最底部,還可以在最后一個item完全可見時處理手指滑動上拉的邏輯來進行加載,等等。
那么,再細化一點,第一個item完全可見和能見到一部分、最后一個item完全可見和僅僅能見到一部分分別需要怎么判斷?如果一個listview的item數目很少,都不足以充滿整個屏幕,顯然第一個和最后一個item都是完全可見的,這時候,還需要處理下拉和上拉的邏輯?如果(不)需要,又該如何判斷?本文將對listview的item顯示狀態做個簡單總結,相信除了應用於下拉刷新和上拉加載之外,還會有其他價值。
demo非常簡單,就是一個listview界面,主要來看自定義的HwgtListView這個類:
HwgtListView.java:
public class HwgtListView extends ListView implements OnScrollListener {
	public HwgtListView(Context context, AttributeSet attrs) {  
		super(context, attrs); 
		setOnScrollListener(this); 
	 }
	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {
		switch (scrollState) {
			case OnScrollListener.SCROLL_STATE_IDLE:
				Log.d("HWGT", "SCROLL_STATE_IDLE........");
			  break;
			case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
				Log.d("HWGT", "SCROLL_STATE_TOUCH_SCROLL........");
			break;
			case OnScrollListener.SCROLL_STATE_FLING:
				Log.d("HWGT", "SCROLL_STATE_FLING........");
			break;
		}
	}
	@Override
	public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
		Log.d("HWGT", "firstVisibleItem..=.."+firstVisibleItem +
				      "  visibleItemCount..=.."+visibleItemCount +
				        "  totalItemCount..=.."+totalItemCount);
		
	}
}

由於我們主要是分析listview的滾動狀態,所以只需要實現OnScrollListener接口,復寫onScroll 和 onScrollStateChanged 兩個方法。

首先看onScrollStateChanged方法,它有一個scrollState參數,表示listview的三種滾動狀態:
1、手指接觸屏幕並開始滑動listview ,這個時候會打印 log:SCROLL_STATE_TOUCH_SCROLL。   
2、手指離開屏幕,listview由於慣性還在滾動中,這個時候打印 log:SCROLL_STATE_FLING。 
3、listview 停止滾動,這個時候打印 log:SCROLL_STATE_IDLE。
由於是滾動狀態的變化,StateChanged,所以,只是在三種狀態切換時打印一次log,手指剛接觸屏幕,還沒有滑動時,在onScrollStateChanged 方法中是不會打印log的。
以上是listview的item比較多,在屏幕上顯示不完的情況,如果item非常少,比如只有3個,連一屏幕都占不滿,則上述log的打印只會出現第一和第三種情況,不會出現SCROLL_STATE_FLING 的 log。在實際需求中,item比較少,連一屏幕都占不滿時,一般就不支持上拉加載了,因為沒意義,所以,這種情況只需要在case OnScrollListener.SCROLL_STATE_FLING:中做一個標識,用來判斷要不要響應手指上滑的邏輯。

再來看onScroll方法,該方法是listview正在滾動時觸發,不管手指是否在屏幕上。
onScroll 和 onScrollStateChanged 這兩個方法中相關的log打印順序是這樣的
SCROLL_STATE_TOUCH_SCROLL    // 首先是手指滑動屏幕引起onScrollStateChanged (從靜止到滾動的改變)
onScroll 
onScroll 
... ...          // 然后,一堆onScroll ,說明正在滾動,到底一秒打印幾次,不是那么重要
SCROLL_STATE_TOUCH_SCROLL    // 表示手指離開屏幕
onScroll 
onScroll 
... ...          // 然后,又是一堆onScroll ,這次是手指已離開屏幕,只不過listview由於慣性作用而處於滾動狀態
SCROLL_STATE_IDLE    // listview停止滾動了
有一種比較特殊的情況,手指輕點屏幕,並未觸發滾動時,會在打印 SCROLL_STATE_TOUCH_SCROLL log之前打印少量的 onScroll  log信息,具體原因不太清楚,但是貌似不太影響平時的使用,有空再研究。

至此,onScroll 和 onScrollStateChanged 這兩個方法的作用和 listview 相關狀態的監聽響應順序已經清楚了,再來看onScroll 方法的參數,主要是3個int類型的參數,int firstVisibleItem、int visibleItemCount 和 int totalItemCount,顧名思義,
totalItemCount 指的是所有item的數量,
firstVisibleItem 代表的是屏幕上第一個可見的item的索引,不管它是完全可見還是只有很小的一部分可見。
visibleItemCount 則稍微有點特殊,它指的是屏幕上可見的item的數量,在屏幕被充滿時,這個值有時候會有差別,比如屏幕上最上邊和最下邊的item都只有非常小一部分可見時,稍微滑動listview讓屏幕上最上邊或是最下邊的item不可見,可能會導致 visibleItemCount 的數量減少1.所以,在使用這個參數時,需要留意這種特殊情況可能帶來的隱患。

接下來,結合上文總結的東西,看一下實際的應用:
1、判斷listview是否滑動到頂部
//在onScroll方法中判斷:
if (firstVisibleItem == 0) { // 只要第一個item有一小部分可見都會滿足條件
	View firstView = view.getChildAt(firstVisibleItem);
	if (firstView != null) {
		if(firstView.getTop()==0){ // 判斷第一個item到頂部的距離
			Log.d("HWGT", "滑動到頂部了,卧槽!");
		}
	}
}

上文已經分析過,僅僅只是判斷if (firstVisibleItem == 0)是不太准確的,因為只要是第一個item只有很小的一部分可見,都會導致這個條件成立,所以還需要判斷第一個item到屏幕頂部的距離,即(firstView.getTop()==0)。

2、判斷listview是由於慣性作用而到達底部的
//首先在onScroll方法中判斷listview到達底部:
if ((firstVisibleItem + visibleItemCount) == totalItemCount) {
    View lastVisibleItemView = view.getChildAt(view.getChildCount() - 1);
    if (lastVisibleItemView != null) {
    	if(getHeight() == lastVisibleItemView.getBottom()){
    		Log.d("HWGT", getHeight()+"...滾到底部了...=.."+lastVisibleItemView.getBottom());
    	}
    }
}

同理,僅僅只是判斷 ((firstVisibleItem + visibleItemCount) == totalItemCount) 也是不准確的,

還需要判斷 (getHeight() == lastVisibleItemView.getBottom())。
那么如何判斷是由於慣性作用而不是手指拖動listview到達底部的呢?
上文已經分析過onScroll 和 onScrollStateChanged 這兩個方法的作用和 listview 相關狀態的監聽響應順序了,
在listview由於慣性作用滑動到底部的情況下,log的打印順序是這樣的:

SCROLL_STATE_TOUCH_SCROLL........

SCROLL_STATE_FLING........

1845...滾到底部了...=..1845

SCROLL_STATE_IDLE........

而listview在手指拖動下到達底部的情況下,log的打印順序是這樣的:

SCROLL_STATE_TOUCH_SCROLL........

1845...滾到底部了...=..1845

SCROLL_STATE_FLING........

SCROLL_STATE_IDLE........

所以在onScrollStateChanged 方法中增加相應的標識再結合onScroll方法中的判斷,就可以准確判斷listview是由於慣性作用而到達底部的了。

3判斷listview是手指滑動過程中到達底部的
... ...  //上文已經分析過了,參考 2、判斷listview是由於慣性作用而到達底部的
4、item的數量非常少,listview占不滿屏幕的情況
在這種情況下,下拉刷新還是有必要的,但是上拉加載就不一定了,那怎樣判斷item非常少時,listview要不要響應手指的滑動事件呢?
listview占不滿屏幕時,log的打印上有幾個比較特殊的地方:
A、剛一進入界面時,在onScroll方法中就會打印類似

滑動到頂部了

607...滾到底部了...=..607

這樣的log(測試時有3個item)
B、在手指滑動過程中,onScrollStateChanged 方法中則
只有

SCROLL_STATE_TOUCH_SCROLL........

SCROLL_STATE_IDLE........

沒有

SCROLL_STATE_FLING........

其實,在item很多且屏幕都顯示不完的情況下,剛進入界面也會在onScroll方法中打印log

滑動到頂部了

把這些特點利用起來,完成普通的需求,還是比較容易的。





 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM