圣光霸沙余掖!小肉看我敲代碼----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)的設備上的,所以有時候會碰到比較奇葩的需求禀崖,比如周五就有一個衩辟,因為現(xiàn)在在做的是一款考勤軟件(其實早做好了給別人了但是里面有好多bug),而且它波附,不艺晴!帶!觸掸屡!摸封寞!屏!(不要問我是怎么實現(xiàn)界面跳轉(zhuǎn)的仅财,它帶物理鍵狈究。。盏求。我能怎么辦抖锥,我也很絕望啊)其中的課表在某一個區(qū)域顯示不全亿眠,所以讓它在自動滾動,本來當時就是簡單的一秒走一個item磅废,但是吧缕探。。周五讓產(chǎn)品經(jīng)理挑刺了(話說我做這個項目的時候我可從來沒見過這公司神出鬼沒的產(chǎn)品經(jīng)理还蹲,就連產(chǎn)品經(jīng)理是誰都是各種小道消息爹耗,簡直服氣)"這個課表滾動的效果不好,一下走一格跟屎一樣谜喊!"

好吧潭兽,確實跟屎一樣,我自己仔細瞅了瞅也覺得這么不是回事斗遏,那就改唄山卦。回去工位思索了了一下(其實就是百度來著)诵次,發(fā)現(xiàn)RecycleView滾動起來好像有點不按套路出牌账蓉,順著smoothScrollToPosition方法點進去看了下,它里面長這樣:

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方法逾一,而點一下這個mlayout發(fā)現(xiàn)它的類型是LayoutManager的铸本,可以,那除了我們設給它的LayoutManager沒其他的了遵堵,馬上去看了下LinearLayoutManager里面的對應方法

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

來大家箱玷,我們看這個方法,他首先new了一個LinearSmoothScroller 類,點進去看注釋陌宿,可以锡足,沒看懂兄弟,那我們點進構(gòu)造函數(shù)看看

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

兄弟們壳坪!不覺得返回的這個變量名很可疑嗎舶得!再點擊去看看。爽蝴。

/**
     * 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設到RecyclerView上,試驗了一下,滾動速度確實的變慢了顾患,但是番捂!我的需求并不只是僅僅變慢而已,我那是課表啊各位江解!所以我得知道它什么時候滾動到了最底部好讓我返回第一列重新滾動设预。

恩。犁河。鳖枕。。我是相信Google的開發(fā)者的桨螺,所以我相信他們絕對留下了接口來讓我知道滾動什么停止了的宾符,回到剛才的smoothScrollToPosition方法,可以看見最后一行的startSmoothScroll方法灭翔,套路不變魏烫,點進去

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方法哄褒,我們再進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什么意思罚舱?點進去

/**
         * 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)回來的瞬間,不要立即進行任何的讓其滾動的操作刷允,因為各位看這一段代碼

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)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市憔鬼,隨后出現(xiàn)的幾起案子龟劲,更是在濱河造成了極大的恐慌,老刑警劉巖轴或,帶你破解...
    沈念sama閱讀 210,835評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咸灿,死亡現(xiàn)場離奇詭異,居然都是意外死亡侮叮,警方通過查閱死者的電腦和手機避矢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,900評論 2 383
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來囊榜,“玉大人,你說我怎么就攤上這事卸勺。” “怎么了曙求?”我有些...
    開封第一講書人閱讀 156,481評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長悟狱。 經(jīng)常有香客問我静浴,道長,這世上最難降的妖魔是什么苹享? 我笑而不...
    開封第一講書人閱讀 56,303評論 1 282
  • 正文 為了忘掉前任浴麻,我火速辦了婚禮得问,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘软免。我一直安慰自己宫纬,他們只是感情好,可當我...
    茶點故事閱讀 65,375評論 5 384
  • 文/花漫 我一把揭開白布漓骚。 她就那樣靜靜地躺著,像睡著了一般认境。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上亩冬,一...
    開封第一講書人閱讀 49,729評論 1 289
  • 那天硼身,我揣著相機與錄音硅急,去河邊找鬼佳遂。 笑死,一個胖子當著我的面吹牛荚板,可吹牛的內(nèi)容都是我干的吩屹。 我是一名探鬼主播跪另,決...
    沈念sama閱讀 38,877評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼免绿,長吁一口氣:“原來是場噩夢啊……” “哼擦盾!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起迹卢,我...
    開封第一講書人閱讀 37,633評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎榕暇,沒想到半個月后喻杈,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體狰晚,經(jīng)...
    沈念sama閱讀 44,088評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,443評論 2 326
  • 正文 我和宋清朗相戀三年瓷们,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谬晕。...
    茶點故事閱讀 38,563評論 1 339
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖攒钳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情文兢,我是刑警寧澤焕檬,帶...
    沈念sama閱讀 34,251評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站实愚,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏萍程。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,827評論 3 312
  • 文/蒙蒙 一茫负、第九天 我趴在偏房一處隱蔽的房頂上張望乎赴。 院中可真熱鬧,春花似錦榕吼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,712評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至胁出,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間全蝶,已是汗流浹背寺枉。 一陣腳步聲響...
    開封第一講書人閱讀 31,943評論 1 264
  • 我被黑心中介騙來泰國打工绷落, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人筐喳。 一個月前我還...
    沈念sama閱讀 46,240評論 2 360
  • 正文 我出身青樓往弓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親函似。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,435評論 2 348

推薦閱讀更多精彩內(nèi)容

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