大家好,在前段時間我寫過用ScrollView實現了自定義滾輪,但是在迴圈的效果不是特別好。(這次文章底部附上了Demo。O(∩_∩)O~)
用ScrollView 迴圈有什麼問題呢。
- 因為我們是重複建立資料,比如資料是[A,B,C,D,E,F],你可以做成假迴圈,比如變為[A,B,C,D,E,F][A,B,C,D,E,F][A,B,C,D,E,F],變為三遍,但是變到上面一組後,因為要重新回到中間,所以你會發現閃動一下的感覺,因為比如衝第一組的A(position為0)到了第二組的A(position為6)。
- 而且如果你手指快速的滑動,不停的滾動,你就會滑到頂部的位置。因為我們的是ScrollView 最後選中哪一項,才讓它滾動到中間相應的那一項。
- 那有些人可能會說,那我就不只弄這幾組。我就多弄幾組不就好了。別人快速滑動也滑不到頂部了。Too young Too Simple。比如我用11組。但是你會發現,你的介面載入直接很久很久,因為ScrollView內的控制元件都直接要初始化好,因為你設定了11組。等於有66個Item在載入完。就會讓介面卡死在那裡。所以體驗就更差了。
最後感謝黑馬飛馬同學給的意見。
對啊。我們的RecyclerView 是隻會載入介面當前顯示的Item,然後不管數量再多,也只是在複用相同的View而已。這樣我們上面的問題不就解決了。因為比如我們建立一千組一萬組資料,我不需要考慮要重新滾回中間,問題1和2就解決了。問題3因為RecyclerView 的特性,也被解決了。是一個很理想的迴圈滾動的滾輪。
於是就使用RecycleViewer來進行相關的開發。正式起航。
原理分析
滾輪的高度和Item的高度
比如我們確定一個頁面顯示5項,item的佈局高度為100dp,那滾輪高度就設定為500dp.怎麼確定RecyclerView 停止滾動
自定義ScrollerListener 繼承RecyclerView.OnScrollListener
,複寫裡面的@Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); switch (newState) { case RecyclerView.SCROLL_STATE_IDLE: ..... ..... ..... break; } }複製程式碼
當state變為了
RecyclerView.SCROLL_STATE_IDLE
就說明了RecyclerView已經停止了。
3.比如只劃一部分,如何讓它自動滾到相應的Item(重點)
方法還是一樣,通過當前獲取到的滾到的Y值,然後除以每項的Item的高度,就能知道當前頂部是處於第幾項,然後求餘數就知道了當前頂部那項有多少是顯示的。在上文我們ScrollView 中,我們使用的是getScrollY()
方法來獲取的,我本來在
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
switch (newState) {
case RecyclerView.SCROLL_STATE_IDLE:
recyclerView.getScrollY();
break;
}
}複製程式碼
所以我在onScrollStateChanged
方法中通過getScrollY()
方法去獲取,多麼Easy,哈哈,結果這次是我Too young Too simple,獲取到的值一直為0。WTF!!然後就只能通過其他方式來獲取滾動的距離。
獲取滾動的距離:
public int getScollYDistance(RecyclerView recyclerView) {
LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
int position = layoutManager.findFirstVisibleItemPosition();
View firstVisiableChildView = layoutManager.findViewByPosition(position);
int itemHeight = firstVisiableChildView.getHeight();
return (position) * itemHeight - firstVisiableChildView.getTop();
}複製程式碼
我想這個大家應該看得懂吧。我畫個圖解釋一下就可以了:
我來大致解釋下:如上圖所示,我們現在一個Item是100的高度,那我們現在滑到了第二個的20的位置,那是不是一共滑動了120的距離。因為我們當前獲取到該手機介面上顯示的第一個的position是1,說明position為0的已經被滑出去了。外加這個當前介面的顯示的position為1的item有部分被滑出去,所以我們獲取它的getTop值為-20,所以是不是正好是當前介面顯示的第一個Item的position,乘以itemHeight,減去這個item 的getTop的值。(1 * 100 - (-20) = 120)
好的,我們已經解決了滾動距離的問題。那現在就是我們要讓他滾動到一定距離,自動調整自己的位置,來正好顯示某個Item項,而不會出現某個Item在介面上顯示一半。
滾動後調整距離讓RecyclerView 滾到特定的position位置:
我簡單介紹,就只分二種情況來談下(正好滑到一個標準的距離,讓Item正好完全顯示這種情況我就去除了):
頂部的Item有小於一半ItemHeight的距離滾到了螢幕的外面:
這時候很簡單,大家說獲取到第一個Item的Position
值,然後呼叫RecyclerView.smoothScrollToPosition(Position)
,跳到這個positionItem就可以了麼。沒錯。這個是可以。但是呼叫這個方法,在接下去的第二種情況下就出現問題了。頂部的Item有大於一半ItemHeight的距離滾到了螢幕外面:
這時候大家也知道,應該是讓當前的螢幕內獲取到的first Item 滾動出介面,所以大家一想就說獲取第一個Item的Position值,然後呼叫RecyclerView.smoothScrollToPosition(Position + 1)
不就可以了麼。。完美!!。但是結果是不會滾動,原來這個方法當我們的Position + 1
已經出現在螢幕上了。不管是不是第一個,不管處於螢幕的哪個位置,這個RecyclerView就不會滾動。我忍不住又一句 WHF!!。那應該怎麼處理呢。
RecyclerView.ScrollBy方法
其實很簡單。我直接拋棄了RecyclerView.smoothScrollToPosition
方法,我們看到了,其實我們是不是可以通過判斷,第一個Item有沒有滾出一半的ItemHeight的距離在外面。無非是二種情況(假設一個ItemHeight為100):
- 已經有80滾動在外面了。我就通過ScrollBy 再向上過給它滾動20到外面。
- 已經有20滾動在外面了。我就通過ScrollBy 再向下返回20到裡面。
所以程式碼如下:
private void smoothMoveToPosition(int n, RecyclerView mRecyclerView, LinearLayoutManager mLinearLayoutManager) {
int firstItem = mLinearLayoutManager.findFirstVisibleItemPosition();
if (firstItem == n) {
int top = mRecyclerView.getChildAt(n - firstItem + 1).getTop();
mRecyclerView.smoothScrollBy(0, -(itemHeight - top));
} else {
int top = mRecyclerView.getChildAt(n - firstItem).getTop();
mRecyclerView.smoothScrollBy(0, top);
}
}複製程式碼
SO EASY..媽媽再也不用擔心我的學習了。步步高打火機,哪裡不會點哪裡