圣光啊!小肉看我敲代码----Android RecyclerView 代码控制scroll时不能平缓滑动解决方案

Android RecyclerView 代码控制scroll时不能平缓滑动解决方案

作者:圣光啊那个敌人值得一战

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

因为现在所在的公司的Android产品是发布在自己生产的设备上的,所以有时候会碰到比较奇葩的需求,比如周五就有一个,因为现在在做的是一款考勤软件(其实早做好了给别人了但是里面有好多bug),而且它,不!带!触!摸!屏!(不要问我是怎么实现界面跳转的,它带物理键。。。我能怎么办,我也很绝望啊)其中的课表在某一个区域显示不全,所以让它在自动滚动,本来当时就是简单的一秒走一个item,但是吧。。周五让产品经理挑刺了(话说我做这个项目的时候我可从来没见过这公司神出鬼没的产品经理,就连产品经理是谁都是各种小道消息,简直服气)"这个课表滚动的效果不好,一下走一格跟屎一样!"

好吧,确实跟屎一样,我自己仔细瞅了瞅也觉得这么不是回事,那就改呗。回去工位思索了了一下(其实就是百度来着),发现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);
    }

可以看见它的最后调用了mlayout这个类的smoothScrollToPosition方法,而点一下这个mlayout发现它的类型是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 类,点进去看注释,可以,没看懂兄弟,那我们点进构造函数看看

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;
    }

来我们看这注释,恩,计算滚动速度 参数为dpi,返回参数为1px滚动所需的时间(ms)

哎~这波就很nice了各位,这就意味着我们只需要重写smoothScrollToPosition这个方法,然后在里面再重写calculateSpeedPerPixel这个方法就可以大概理论上动态改变显示位置时的滚动速度了,我们首先继承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的开发者的,所以我相信他们绝对留下了接口来让我知道滚动什么停止了的,回到刚才的smoothScrollToPosition方法,可以看见最后一行的startSmoothScroll方法,套路不变,点进去

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

恩,这里在判断smoothScroller的实例在和以前不一样以及其在滚动时会让其立马停止滚动,也就是调了一下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;
        }

恩。。道理我都懂,这里各种初始化各种配置完了,但是第一行就调了个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";

    /**
     * 滚动完成回调
     */
    private StopScrollCallBack outStopScrollCallBack;

    /**
     * 滚动速度
     */
    private String speed;

    /**
     *
     * @param context 上下文
     * @param stopScrollCallBack 滚动完成回调
     * @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);
    }

}

这里我们传入了一个接口回调StopScrollCallBack 好让外面来实现具体在停止时的操作,使用方法如下:

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

但是啊各位,不得不说一个坑,在回调回来的瞬间,不要立即进行任何的让其滚动的操作,因为各位看这一段代码

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

在座的各位,看见没,它会在判断其还在滚动的时候调用stop,而走我们回调的一瞬间它判断就是在滚动,然后就会不停的走stop,马上就抛stackoverflow了,所以各位。。。来个handler吧。。。。

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,398评论 25 707
  • 简介 RecyclerView在24.2.0版本中新增了SnapHelper这个辅助类,用于辅助RecyclerV...
    辰之猫阅读 153,991评论 65 617
  • 这篇文章分三个部分,简单跟大家讲一下 RecyclerView 的常用方法与奇葩用法;工作原理与ListView比...
    LucasAdam阅读 4,374评论 0 27
  • 侠客行 (七言古诗) 鲜衣怒马绝尘去,江头日下鞭未及。 衙内抽刀绝命客,百骑穷追战村西。 回马一跃三人倒,侧身两镖...
    长安旧人阅读 550评论 3 9
  • 年,情怀依旧 腹稿早已打好,本想着开工前一天码出来的; 但,离开家,年,好像就过完了; 现在提起,似乎有点在不对的...
    筗_z阅读 298评论 0 0