簡單的橫向ListView實現(version 4.0),listviewversion
這個版本的部落格寫起來頗費口舌,有些代碼自己語言群組織能力有限,感覺描述起來很費勁,前前後後改了五六遍稿子還是不盡人意 ,不過我還是堅持寫出來自己當初的思路,如果看得不明白的地方我在文章最後仍然會上傳原始碼,可以直接運行看效果,看過啟動並執行效果後對文中有些彆扭的語言估計會能直觀的瞭解。在版本3.0的雖然實現了隨著手指的左右移動listView中的item也隨著滾動,但是會出現如下的情況:
當左邊已經是第一個的時候,會出現如下的情況(仍然可以向右移動):
當右邊是adapter最後一個item的時候,會出現如下的情況(仍然可以向左移動):
正常來說,當左邊第一個和右邊最後一個應該不能滾動才是,本4.0版本將解決這個問題。在解決這個問題之前先說說相關的知識點:
知識點1):手指在螢幕上移動的時候,會發生 其他相關方法...>onScroll-->onScroll-->onScroll ..-->其他相關方法.這樣的調用
知識點2):3.0版本在處理手指滾動的時候用到了onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY)這個方法,這個方法中有個distanceX參數,這個參數我們知道,當手指想做移動的時候distanceX>0;而手指向右移動的時候distanceX<0;這是因為distanceX的值是由第二個參數決定的:distanceX = 上次掉用onScroll方法的e2.getX()-當前onScroll方法的e2.getX()或者簡寫成 lastEvent2.getX() - currentEvent2.getX() = distanceX;
在版本3.0的時候我們直接用distanceX這個變數(確切的說這個變數值的相反數來類比左右移動滾動的距離),但是這次為瞭解決上面的問題我們將不再直接用distanceX,而是新定義了兩個變數:
變數:preTotalDistanceX :相對於當前onScroll方法調用之前,之前所有onScroll調用所產生的距離之和;比如當前是第n次調用了onScroll,那麼preTotalDistanceX=前n-1次調用onScroll中distanceX的累加和;
變數:totalDistanceX:當前調用onScroll方法方法調用中所有distanceX的累加和;比如當前是第n次調用了onScroll方法,那麼totalDistanceX就是n個onScrll方法調用中參數裡distanceX的累加和;
那麼滾動的距離distanceX = totalDistanceX - preTotalDistanceX
用代碼錶示總距離如下:
public boolean onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY) {totalDistanceX += distanceX;requestLayout();return true;};
該版本onLayout的方法就修改如下:
int distanceX = totalDistanceX - preTotalDistanceX; removeAnvisiableViews(-distanceX);addRightChildViews(-distanceX);addLeftChildViews(-distanceX);layoutChildViews(-distanceX); preTotalDistanceX = totalDistanceX;
要解決文章開頭提出的問題,
關鍵是讓distanceX = 0;即pretotalDistanceX=totalDistanceX;所以解決問題的關鍵就是如何讓這兩個變數相等;從知識點2可以知道,假設使用者的
手指先右後左:此時最左邊的item是adapter的第一個item,所以手指向右移動的時候不能讓listView向右滾動,但是實際上隨著手指的向右移動totalDistanceX變數是<0,並沿著平面直角座標系遠離0點而負遞增,所以在這裡可以初步判斷:當totalDistanceX<=0的時候,讓totalDistanceX = 0;這樣preoTotalDistanceX=totalDistanceX=0,這樣就會讓distanceX=0,從而不會讓我們的ListView不會向右移動;假設使用者的手
指先左後右的移動方式:當手指向左移動的時候totalDistanceX>0且不斷增大,是遞增的;而當手指右移動的時候totalDistanceX是在原來的基礎上不斷減少的過程,當較少到0的時候繼續向右移動的話totalDistanceX變為負數,繼續向右移動的話就類似與手指先右後左的現象了;這個是關鍵的地方,整個左右移動的過程發生詭異的變化類似於物理中的位移,當totalDistanceX=0的時候我們可以斷定不能在向右滾動。
所以在onLayout代碼中我做了如下判斷:
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { if (listAdapter == null) { return; } /**確保黨左邊是第一個的時候不再滾動*/ if(totalDistanceX<=0) { totalDistanceX = 0; } int distanceX = totalDistanceX - preTotalDistanceX; removeAnvisiableViews(-distanceX); addRightChildViews(-distanceX); addLeftChildViews(-distanceX); layoutChildViews(-distanceX); preTotalDistanceX = totalDistanceX; }
運行一把發現上面的設想是成立的,到此為止,部落格開頭的第一個問題得到瞭解決。
下面將解決第二個問題:當adapter裡面最後一個item添加到viewGroup裡面並且完全顯示的時候,禁止listView繼續向左滾動。
先看最有一個item沒有顯示完全的情況,如:
解決第二個問題的關鍵仍然是如何讓distanceX= totalDistanceX-pretotalDistanceX=0;也就是如何讓totalDistanceX=preototalDistanceX;根據圖示我們很容易得出如下結論:要讓最後一個item顯示完,在之前滾動距離總和的基礎上再讓listView滾動X大小的距離可以了,用上面的等式表示為totalDistanceX=X+pretotalDistanceX,所以當totalDistanceX>X+pretotalDistanceX的時候我們讓 totalDistanceX = X+preototalDistanceX;在程式用我是用scrollXMax來表示X+preototalDistanceX的值(此變數名起的有點垃圾);在代碼用為:
//scrollXMax = X + pretotalDistanceXif(totalDistanceX>scrollXMax) { totalDistanceX = scrollXMax;}......preototalDistanceX = totalDistanceX;這樣當最後一個item滾動了X距離的情況下調用layout繪製到listView之後,手指在移動的情況下if(totalDistanceX>scrollXMax仍然成立)。這樣計算的distanceX=0.但是話有說回來了,X+preDistanceX這段代碼添加到哪兒呢?結合之前的幾篇部落格,不難分析出應該在addRightChildViews方法裡面添加,代碼如下:
private void addRightChildViews(int distanceX) {// 2.讓螢幕儘可能的顯示Item。注意剛開始的時候是沒有View rightChildView = getChildAt(getChildCount() - 1);// 擷取此childView右邊框距離parentView左邊框的距離int rightEdge = rightChildView != null ? rightChildView.getRight() : 0;while (rightEdge + distanceX < getWidth()&& rightIndex < listAdapter.getCount()) {View child = listAdapter.getView(rightIndex, null, null);child = measureChild(child);addViewInLayout(child, -1, child.getLayoutParams(), true);rightEdge += child.getMeasuredWidth(); //判斷最後一個itemif (rightIndex == listAdapter.getCount() - 1) {scrollXMax = rightEdge +preTotalDistanceX- getWidth();}rightIndex++;}}到此位置,上面的的兩個問題都得到圓滿解決;自己在整這個版本的時候走了不少彎路,搗鼓了半天,到最後完成還是有種小小的成就感;何為編程,說白了就是在遵循特性程式設計語言規則的情況下,用該程式設計語言描述或者表達程式員思路的過程。在4.0版本的實現中,自己在紙上又是寫又是畫的,思路清晰瞭然後就很自然而然的用程式設計語言把這個思路表達出來,這就是編程吧!囉嗦完畢,當然該版本還沒有完善完畢,比如說不能點擊就是這個,將在下一個版本解決剩餘的問題,時間允許的話直接寫完,此處為項目源碼,歡迎批評指正