專案需求討論- 自定義滾輪(第二波新實現)

青蛙要fly發表於2017-07-12

大家好,在前段時間我寫過用ScrollView實現了自定義滾輪,但是在迴圈的效果不是特別好。(這次文章底部附上了Demo。O(∩_∩)O~

專案需求討論-自定義滾輪

用ScrollView 迴圈有什麼問題呢。

  1. 因為我們是重複建立資料,比如資料是[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)。
  2. 而且如果你手指快速的滑動,不停的滾動,你就會滑到頂部的位置。因為我們的是ScrollView 最後選中哪一項,才讓它滾動到中間相應的那一項。
  3. 那有些人可能會說,那我就不只弄這幾組。我就多弄幾組不就好了。別人快速滑動也滑不到頂部了。Too young Too Simple。比如我用11組。但是你會發現,你的介面載入直接很久很久,因為ScrollView內的控制元件都直接要初始化好,因為你設定了11組。等於有66個Item在載入完。就會讓介面卡死在那裡。所以體驗就更差了。

最後感謝黑馬飛馬同學給的意見。

對啊。我們的RecyclerView 是隻會載入介面當前顯示的Item,然後不管數量再多,也只是在複用相同的View而已。這樣我們上面的問題不就解決了。因為比如我們建立一千組一萬組資料,我不需要考慮要重新滾回中間,問題1和2就解決了。問題3因為RecyclerView 的特性,也被解決了。是一個很理想的迴圈滾動的滾輪。

於是就使用RecycleViewer來進行相關的開發。正式起航。


原理分析

  1. 滾輪的高度和Item的高度
    比如我們確定一個頁面顯示5項,item的佈局高度為100dp,那滾輪高度就設定為500dp.

  2. 怎麼確定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正好完全顯示這種情況我就去除了):

  1. 頂部的Item有小於一半ItemHeight的距離滾到了螢幕的外面:


    這時候很簡單,大家說獲取到第一個Item的Position值,然後呼叫RecyclerView.smoothScrollToPosition(Position),跳到這個positionItem就可以了麼。沒錯。這個是可以。但是呼叫這個方法,在接下去的第二種情況下就出現問題了。

  2. 頂部的Item有大於一半ItemHeight的距離滾到了螢幕外面:

這時候大家也知道,應該是讓當前的螢幕內獲取到的first Item 滾動出介面,所以大家一想就說獲取第一個Item的Position值,然後呼叫RecyclerView.smoothScrollToPosition(Position + 1)不就可以了麼。。完美!!。但是結果是不會滾動,原來這個方法當我們的Position + 1已經出現在螢幕上了。不管是不是第一個,不管處於螢幕的哪個位置,這個RecyclerView就不會滾動。我忍不住又一句 WHF!!。那應該怎麼處理呢。

RecyclerView.ScrollBy方法
其實很簡單。我直接拋棄了RecyclerView.smoothScrollToPosition方法,我們看到了,其實我們是不是可以通過判斷,第一個Item有沒有滾出一半的ItemHeight的距離在外面。無非是二種情況(假設一個ItemHeight為100):

  1. 已經有80滾動在外面了。我就通過ScrollBy 再向上過給它滾動20到外面。
  2. 已經有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..媽媽再也不用擔心我的學習了。步步高打火機,哪裡不會點哪裡


附上DEMO

相關文章