RecycleView滑動(dòng)錨點(diǎn)定位

RecycleView滑動(dòng),頂部tab跟著變化位置,類似于淘寶詳情頁,效果是這樣的


Tab跟隨RecycleView滑動(dòng)

1.實(shí)現(xiàn)思路

簡單講就是監(jiān)聽RecycleView的滑動(dòng),根據(jù)滑動(dòng)調(diào)整tab位置。相應(yīng)的監(jiān)聽tab點(diǎn)擊事件,滑動(dòng)recycleView到相應(yīng)位置。

2.滑動(dòng)RecycleView調(diào)整tab位置

2.1獲得滑動(dòng)位置

LayoutManager提供了獲得首個(gè)可見item的方法
int position = layoutManager.findFirstVisibleItemPosition();

2.2將滑動(dòng)位置轉(zhuǎn)換為類型位置

這里我item類型有普通類型和大類的標(biāo)題類型,通過instanceof判斷類型是否是我所屬的大類的標(biāo)題類型,然后根據(jù)大類標(biāo)題類型到頂部的距離判斷,再根據(jù)類型名稱進(jìn)行定位。
然后通過getTop獲得到頂部的距離,根據(jù)距離進(jìn)行分類間的跳轉(zhuǎn)。
int top = mRecyclerView.getChildAt(0).getTop();

2.3完整代碼

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                //不捕獲自動(dòng)滾動(dòng),非用戶觸摸產(chǎn)生的滑動(dòng)
                if (isFormTabScroll) {
                    return;
                }

                //監(jiān)聽滑動(dòng)距離,改變tab選中位置
                int position = layoutManager.findFirstVisibleItemPosition();
                int realPosition = position - mAdapter.getHeaderSize();

                //因?yàn)橛?個(gè)header所以>=-1
                if (realPosition >= -1 && realPosition < mAdapter.getList().size()) {
                    //通過判斷分類卡片位置定位tab
                    if (mealCardIndxList.size() == 0) {//記錄大類的流量卡片位置
                        for (int i = 0; i < mAdapter.getList().size(); i++) {
                            if (mAdapter.getList().get(i) instanceof CardFlowMeal) {
                                mealCardIndxList.add(i);
                            }
                        }
                    }

                    if (mealCardIndxList.size() + 1 == indicator.getTitles().size()) {//防止下標(biāo)越界異常
                        //判斷當(dāng)前item所處類定位tab
                        for (int i = 0; i < mealCardIndxList.size(); i++) {
                            if (i == 0) {
                                if (realPosition >= -1 && realPosition < mealCardIndxList.get(i)) {
                                    if (indicator.getCurrentPosition() != 0) {
                                        indicator.setCurrentItem(0);
                                        break;
                                    }
                                }
                            } else if (realPosition >= mealCardIndxList.get(i - 1) && realPosition < mealCardIndxList.get(i)) {
                                if (indicator.getCurrentPosition() != i) {
                                    indicator.setCurrentItem(i);
                                    break;
                                }
                            }
                            //可能會有i==0的情況下屬于如下情況 所以不加else if
                            if (i == mealCardIndxList.size() - 1) {
                                if (realPosition >= mealCardIndxList.get(i)) {
                                    if (indicator.getCurrentPosition() != i + 1) {
                                        indicator.setCurrentItem(i + 1);
                                    }
                                }
                            }
                        }
                    }
                }
            }

            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);

                switch (newState) {
                    case RecyclerView.SCROLL_STATE_DRAGGING:
                        isFormTabScroll = false;
                        break;
                    case RecyclerView.SCROLL_STATE_IDLE:
                        if (flowDetailActivity != null && !flowDetailActivity.isCloseNps()) {

                            UIUtils.postDelayRunnable(new Runnable() {
                                @Override
                                public void run() {
                                    if (!flowDetailActivity.isShowNps()) {
                                        flowDetailActivity.showNPS();
                                    }
                                }
                            }, 100);
                        }
                        break;
                    default:
                        break;
                }
            }
        });

3.點(diǎn)擊tab位置RecycleView滑動(dòng)到相應(yīng)位置

通過RecycleView的scrollBy()scrollToPosition()兩個(gè)方法實(shí)現(xiàn)。為scrollToPosition()只會保證滑動(dòng)的position出現(xiàn)在視野中,不會保證該position在頂端,所以需要通過scrollBy()完成置頂?shù)幕瑒?dòng)。

    public void moveRecycleViewToPosition(int n) {
        isFormTabScroll = true;

        //先從RecyclerView的LayoutManager中獲取第一項(xiàng)和最后一項(xiàng)的Position
        int firstItem = layoutManager.findFirstVisibleItemPosition();
        int lastItem = layoutManager.findLastVisibleItemPosition();
        //然后區(qū)分情況
        if (n < firstItem) {
            //當(dāng)要置頂?shù)捻?xiàng)在當(dāng)前顯示的第一個(gè)項(xiàng)的前面時(shí)
            mRecyclerView.scrollToPosition(n);
            if (n != 0) {
                mIndex = n;
                needMoveToTop = true;
            }
        } else if (n <= lastItem) {
            //當(dāng)要置頂?shù)捻?xiàng)已經(jīng)在屏幕上顯示時(shí)
            int top = mRecyclerView.getChildAt(n - firstItem).getTop();
            mRecyclerView.scrollBy(0, top);
        } else {
            //當(dāng)要置頂?shù)捻?xiàng)在當(dāng)前顯示的最后一項(xiàng)的后面時(shí)
            mRecyclerView.scrollToPosition(n);
            //這里這個(gè)變量是用在RecyclerView滾動(dòng)監(jiān)聽里面的
            mIndex = n;
            needMoveToTop = true;
        }
    }

可以看到在該position不在屏幕中時(shí),指定了一個(gè)字段needMoveToTop = true,因?yàn)樾枰?code>scrollToPosition()方法后再執(zhí)行scrollBy()完成置頂操作。這個(gè)標(biāo)記是監(jiān)聽RecycleView滑動(dòng)完成之后用來標(biāo)識完成scrollBy()最后置頂滑動(dòng)的,具體代碼如下:

        mRecyclerView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
            @Override
            public void onScrollChanged() {

                //將改item滑動(dòng)到頂部
                if (needMoveToTop) {
                    needMoveToTop = false;
                    //獲取要置頂?shù)捻?xiàng)在當(dāng)前屏幕的位置,mIndex是記錄的要置頂項(xiàng)在RecyclerView中的位置
                    int n = mIndex - layoutManager.findFirstVisibleItemPosition();
                    if (0 <= n && n < mRecyclerView.getChildCount()) {
                        //獲取要置頂?shù)捻?xiàng)頂部離RecyclerView頂部的距離
                        int top = mRecyclerView.getChildAt(n).getTop();
                        //最后的移動(dòng)
                        mRecyclerView.scrollBy(0, top);
                    }
                }
            }
        });

總體實(shí)現(xiàn)難度并不大,至此就完成了滑動(dòng)錨點(diǎn)定位的邏輯。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容