RecyclerView滑動到指定Position的方法

yangxi_001發表於2017-09-12

Question

最近在寫 SideBar 的時候遇到一個問題,當執行 Recyclerview 的 smoothScrollToPosition(position) 的時候,Recyclerview 看上去並沒有滾動到指定位置。

Analysis

當然,這並不是方法的bug,而是 smoothScrollToPosition(position) 的執行效果有三種情況,需要區分。

  • 目標position在第一個可見項之前 。 
    這種情況呼叫smoothScrollToPosition能夠平滑的滾動到指定位置,並且置頂。

  • 目標position在第一個可見項之後,最後一個可見項之前。 
    這種情況下,呼叫smoothScrollToPosition不會有任何效果···

  • 目標position在最後一個可見項之後。 
    這種情況呼叫smoothScrollToPosition會把目標項滑動到螢幕最下方···

Solution

鑑於這三種情況,我想大多數情況下都無法滿足我們的滑動要求。為了實現 Recyclerview 把指定 item 滑動到螢幕頂端的需求,我們需要對上面三種情況分別處理。


    /** 目標項是否在最後一個可見項之後*/
    private boolean mShouldScroll;
    /** 記錄目標項位置*/
    private int mToPosition;

    /**
     * 滑動到指定位置
     * @param mRecyclerView
     * @param position
     */
    private void smoothMoveToPosition(RecyclerView mRecyclerView, final int position) {
        // 第一個可見位置
        int firstItem = mRecyclerView.getChildLayoutPosition(mRecyclerView.getChildAt(0));
        // 最後一個可見位置
        int lastItem = mRecyclerView.getChildLayoutPosition(mRecyclerView.getChildAt(mRecyclerView.getChildCount() - 1));

        if (position < firstItem) {
            // 如果跳轉位置在第一個可見位置之前,就smoothScrollToPosition可以直接跳轉
            mRecyclerView.smoothScrollToPosition(position);
        } else if (position <= lastItem) {
            // 跳轉位置在第一個可見項之後,最後一個可見項之前
            // smoothScrollToPosition根本不會動,此時呼叫smoothScrollBy來滑動到指定位置
            int movePosition = position - firstItem;
            if (movePosition >= 0 && movePosition < mRecyclerView.getChildCount()) {
                int top = mRecyclerView.getChildAt(movePosition).getTop();
                mRecyclerView.smoothScrollBy(0, top);
            }
        }else {
            // 如果要跳轉的位置在最後可見項之後,則先呼叫smoothScrollToPosition將要跳轉的位置滾動到可見位置
            // 再通過onScrollStateChanged控制再次呼叫smoothMoveToPosition,執行上一個判斷中的方法
            mRecyclerView.smoothScrollToPosition(position);
            mToPosition = position;
            mShouldScroll = true;
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

再通過onScrollStateChanged控制再次呼叫smoothMoveToPosition

        mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                if (mShouldScroll){
                    mShouldScroll = false;
                    smoothMoveToPosition(mRecyclerView,mToPosition);
                }
            }
        });
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

目前這個解決方法有兩個已知的問題

  1. 當目標項在最後一個可見項之後的時候,由於我們先執行smoothScrollToPosition方法,然後在OnScrollListener中執行smoothMoveToPosition方法,在滑動的時候不夠連貫。
  2. 在手動滑動的時候執行該方法,會有極小的概率滑動的位置出現偏差。

如果你有更好解決辦法,希望不吝指教。

相關文章