圣光??!小肉看我敲代碼----Android RecyclerView 代碼控制scroll時不能平緩滑動解決方案

Android RecyclerView 代碼控制scroll時不能平緩滑動解決方案

作者:圣光啊那個敵人值得一戰(zhàn)

參考文章:http://blog.csdn.net/a86261566/article/details/50906456
參考文章作者:AndroidArenas

因為現(xiàn)在所在的公司的Android產(chǎn)品是發(fā)布在自己生產(chǎn)的設(shè)備上的,所以有時候會碰到比較奇葩的需求,比如周五就有一個,因為現(xiàn)在在做的是一款考勤軟件(其實早做好了給別人了但是里面有好多bug),而且它,不!帶!觸!摸!屏!(不要問我是怎么實現(xiàn)界面跳轉(zhuǎn)的,它帶物理鍵。。。我能怎么辦,我也很絕望啊)其中的課表在某一個區(qū)域顯示不全,所以讓它在自動滾動,本來當(dāng)時就是簡單的一秒走一個item,但是吧。。周五讓產(chǎn)品經(jīng)理挑刺了(話說我做這個項目的時候我可從來沒見過這公司神出鬼沒的產(chǎn)品經(jīng)理,就連產(chǎn)品經(jīng)理是誰都是各種小道消息,簡直服氣)"這個課表滾動的效果不好,一下走一格跟屎一樣!"

好吧,確實跟屎一樣,我自己仔細(xì)瞅了瞅也覺得這么不是回事,那就改唄?;厝スの凰妓髁肆艘幌?其實就是百度來著),發(fā)現(xiàn)RecycleView滾動起來好像有點(diǎn)不按套路出牌,順著smoothScrollToPosition方法點(diǎn)進(jìn)去看了下,它里面長這樣:

public void smoothScrollToPosition(int position) {
        if (mLayoutFrozen) {
            return;
        }
        if (mLayout == null) {
            Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. " +
                    "Call setLayoutManager with a non-null argument.");
            return;
        }
        mLayout.smoothScrollToPosition(this, mState, position);
    }

可以看見它的最后調(diào)用了mlayout這個類的smoothScrollToPosition方法,而點(diǎn)一下這個mlayout發(fā)現(xiàn)它的類型是LayoutManager的,可以,那除了我們設(shè)給它的LayoutManager沒其他的了,馬上去看了下LinearLayoutManager里面的對應(yīng)方法

    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
            int position) {
        LinearSmoothScroller linearSmoothScroller =
                new LinearSmoothScroller(recyclerView.getContext());
        linearSmoothScroller.setTargetPosition(position);
        startSmoothScroll(linearSmoothScroller);
    }

來大家,我們看這個方法,他首先new了一個LinearSmoothScroller 類,點(diǎn)進(jìn)去看注釋,可以,沒看懂兄弟,那我們點(diǎn)進(jìn)構(gòu)造函數(shù)看看

public LinearSmoothScroller(Context context) {
        MILLISECONDS_PER_PX = calculateSpeedPerPixel(context.getResources().getDisplayMetrics());
    }

兄弟們!不覺得返回的這個變量名很可疑嗎!再點(diǎn)擊去看看。。

/**
     * Calculates the scroll speed.
     *
     * @param displayMetrics DisplayMetrics to be used for real dimension calculations
     * @return The time (in ms) it should take for each pixel. For instance, if returned value is
     * 2 ms, it means scrolling 1000 pixels with LinearInterpolation should take 2 seconds.
     */
    protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
        return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
    }

來我們看這注釋,恩,計算滾動速度 參數(shù)為dpi,返回參數(shù)為1px滾動所需的時間(ms)

哎~這波就很nice了各位,這就意味著我們只需要重寫smoothScrollToPosition這個方法,然后在里面再重寫calculateSpeedPerPixel這個方法就可以大概理論上動態(tài)改變顯示位置時的滾動速度了,我們首先繼承LinearLayoutManager類,然后重寫其中的smoothScrollToPosition方法,例子如下:

@Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, final int position) {

        LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {

            @Override
            protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
                return 5;//返回滾過1px需要多少ms
            }
        };
        linearSmoothScroller.setTargetPosition(position);
        startSmoothScroll(linearSmoothScroller);
    }

然后將我們重寫的好的LayoutManager設(shè)到RecyclerView上,試驗了一下,滾動速度確實的變慢了,但是!我的需求并不只是僅僅變慢而已,我那是課表啊各位!所以我得知道它什么時候滾動到了最底部好讓我返回第一列重新滾動。

恩。。。。我是相信Google的開發(fā)者的,所以我相信他們絕對留下了接口來讓我知道滾動什么停止了的,回到剛才的smoothScrollToPosition方法,可以看見最后一行的startSmoothScroll方法,套路不變,點(diǎn)進(jìn)去

public void startSmoothScroll(SmoothScroller smoothScroller) {
            if (mSmoothScroller != null && smoothScroller != mSmoothScroller
                    && mSmoothScroller.isRunning()) {
                mSmoothScroller.stop();
            }
            mSmoothScroller = smoothScroller;
            mSmoothScroller.start(mRecyclerView, this);
        }

恩,這里在判斷smoothScroller的實例在和以前不一樣以及其在滾動時會讓其立馬停止?jié)L動,也就是調(diào)了一下stop方法,我們再進(jìn)stop方法看看?

final protected void stop() {
            if (!mRunning) {
                return;
            }
            onStop();
            mRecyclerView.mState.mTargetPosition = RecyclerView.NO_POSITION;
            mTargetView = null;
            mTargetPosition = RecyclerView.NO_POSITION;
            mPendingInitialRun = false;
            mRunning = false;
            // trigger a cleanup
            mLayoutManager.onSmoothScrollerStopped(this);
            // clear references to avoid any potential leak by a custom smooth scroller
            mLayoutManager = null;
            mRecyclerView = null;
        }

恩。。道理我都懂,這里各種初始化各種配置完了,但是第一行就調(diào)了個onStop什么意思?點(diǎn)進(jìn)去

/**
         * Called when smooth scroller is stopped. This is a good place to cleanup your state etc.
         * @see #stop()
         */
        abstract protected void onStop();

你看各位,我就說他們肯定留接口了吧?

這樣我們就能知道什么時候滾動停止了,完整例子如下

/**
 * Created by lip on 2017/2/24.
 * 控制recycler滾動速度manager
 */

public class ControlRvSpeedLinearLayoutManager extends LinearLayoutManager {

    public static final String NORMAL = "normal";

    public static final String SLOW = "slow";

    public static final String EXTREMELY_SLOW = "extremelySlow";

    /**
     * 滾動完成回調(diào)
     */
    private StopScrollCallBack outStopScrollCallBack;

    /**
     * 滾動速度
     */
    private String speed;

    /**
     *
     * @param context 上下文
     * @param stopScrollCallBack 滾動完成回調(diào)
     * @param speed 滾動速度
     */
    public ControlRvSpeedLinearLayoutManager(Context context, StopScrollCallBack stopScrollCallBack,String speed) {
        super(context);
        this.outStopScrollCallBack = stopScrollCallBack;
        this.speed = speed;
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, final int position) {

        LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {

            @Override
            protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
                Log.i("滾動demo","======="+displayMetrics.densityDpi);
                float ms = 1f;
                switch (speed){
                    case NORMAL:
                        ms = 1f;
                        break;
                    case SLOW:
                        ms = 5f;
                        break;
                    case EXTREMELY_SLOW:
                        ms = 10f;
                        break;
                }
                return ms;//返回滾過1px需要多少ms
            }

            @Override
            protected void onStop() {
                super.onStop();
                outStopScrollCallBack.scrollStop(position);
            }

        };
        linearSmoothScroller.setTargetPosition(position);
        startSmoothScroll(linearSmoothScroller);
    }

    public interface StopScrollCallBack {
        void scrollStop(int position);
    }

}

這里我們傳入了一個接口回調(diào)StopScrollCallBack 好讓外面來實現(xiàn)具體在停止時的操作,使用方法如下:

BaseRecyclerAdapter<String> recyclerAdapter = new BaseRecyclerAdapter<String>(MainActivity.this, strings) {
            @Override
            public int getItemLayoutId(int viewType) {
                return R.layout.text_rv_item_layout;
            }

            @Override
            public void bindData(RecyclerView.ViewHolder holder, int position, String item) {
                RecyclerViewHolder rv = (RecyclerViewHolder) holder;
                rv.setText(R.id.tv_text, item);
            }
        };
        ControlRvSpeedLinearLayoutManager controlRvSpeedLinearLayoutManager = new ControlRvSpeedLinearLayoutManager(MainActivity.this, new ControlRvSpeedLinearLayoutManager.StopScrollCallBack() {
            @Override
            public void scrollStop(final int position) {
                handler.sendEmptyMessageDelayed(0,1000);
            }
        },ControlRvSpeedLinearLayoutManager.EXTREMELY_SLOW);
        rvTextList.setLayoutManager(controlRvSpeedLinearLayoutManager);
        rvTextList.addItemDecoration(new SpaceItemDecoration(20));
        rvTextList.setAdapter(recyclerAdapter);

demo效果如下:

GIF.gif

但是啊各位,不得不說一個坑,在回調(diào)回來的瞬間,不要立即進(jìn)行任何的讓其滾動的操作,因為各位看這一段代碼

public void startSmoothScroll(SmoothScroller smoothScroller) {
            if (mSmoothScroller != null && smoothScroller != mSmoothScroller
                    && mSmoothScroller.isRunning()) {
                mSmoothScroller.stop();
            }
            mSmoothScroller = smoothScroller;
            mSmoothScroller.start(mRecyclerView, this);
        }

在座的各位,看見沒,它會在判斷其還在滾動的時候調(diào)用stop,而走我們回調(diào)的一瞬間它判斷就是在滾動,然后就會不停的走stop,馬上就拋stackoverflow了,所以各位。。。來個handler吧。。。。

具體demo在git上 https://github.com/LIPKKKK/RecyclerViewSmoothScrollDemo
感興趣的可以去看一下,謝謝支持

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

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,115評論 25 709
  • 簡介 RecyclerView在24.2.0版本中新增了SnapHelper這個輔助類,用于輔助RecyclerV...
    辰之貓閱讀 155,924評論 66 618
  • 這篇文章分三個部分,簡單跟大家講一下 RecyclerView 的常用方法與奇葩用法;工作原理與ListView比...
    LucasAdam閱讀 4,705評論 0 27
  • 俠客行 (七言古詩) 鮮衣怒馬絕塵去,江頭日下鞭未及。 衙內(nèi)抽刀絕命客,百騎窮追戰(zhàn)村西。 回馬一躍三人倒,側(cè)身兩鏢...
    長安舊人閱讀 616評論 3 9
  • 年,情懷依舊 腹稿早已打好,本想著開工前一天碼出來的; 但,離開家,年,好像就過完了; 現(xiàn)在提起,似乎有點(diǎn)在不對的...
    筗_z閱讀 372評論 0 0

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