Android RecycleView实现平滑滚动置顶和调整滚动速度

这篇具有很好参考价值的文章主要介绍了Android RecycleView实现平滑滚动置顶和调整滚动速度。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、滑动到指定位置(target position)并且置顶

1. RecycleView默认的几个实现方法及缺陷

2. 优化源码实现置顶方案

二、调整平移滑动速率

三、其他方案:置顶、置顶加偏移、居中

1. 其他置顶方案

2. 置顶加偏移

3. 滚动居中


    在实际项目里,RecycleView 可以说是我们最常用到的组件,作为绑定并展示LIST数据的组件,经常需要实现平滑滚动到列表里的某个目标ITEM,并且将其置顶在屏幕最上方,而且在特殊情形下,我们需要控制滑动速度,来控制滚动的时长。

一、滑动到指定位置(target position)并且置顶

1. RecycleView默认的几个实现方法及缺陷

((LinearLayoutManager)recycleView.getLayoutManager()).scrollToPositionWithOffset(int position, int offset);

    如果你没有滑动过程动画的要求,那上面这行代码将offset的值设置为0,就一步到位地满足需求了。

recycleView.scrollToPosition(int position);
recycleView.smoothScrollToPosition(int position);

    以上两个方法遵循的是最少滑动原则,只要target position那项item已经完全可见了,就马上停止滑动;要是target position已经可见了,那根本不会滑动。所以按不同的滑动方向,会出现不同的结果,如果target position在屏幕可视范围的上方,则它默认会将target position置顶;反之,target position在屏幕可视范围的下方,则滚动完成后,target postion会处于屏幕的最下方,无法实现我们的置顶需求。所以缺陷很明显:要么不动,要么无法置顶

2. 优化源码实现置顶方案

    我们看下recycleview提供的方法的源代码,看看是否可以进行改进:

    public void smoothScrollToPosition(int position) {
        if (mLayoutSuppressed) {
            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);
    }

    由代码可以看出,RecyclerView的滑动方法是调用LayoutManager的smoothScrollToPosition方法:

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

    其中LinearSmoothScroller提供了三个滑动策略:

    /**
     * Align child view's left or top with parent view's left or top
     *
     * @see #calculateDtToFit(int, int, int, int, int)
     * @see #calculateDxToMakeVisible(android.view.View, int)
     * @see #calculateDyToMakeVisible(android.view.View, int)
     */
    public static final int SNAP_TO_START = -1;

    /**
     * Align child view's right or bottom with parent view's right or bottom
     *
     * @see #calculateDtToFit(int, int, int, int, int)
     * @see #calculateDxToMakeVisible(android.view.View, int)
     * @see #calculateDyToMakeVisible(android.view.View, int)
     */
    public static final int SNAP_TO_END = 1;

    /**
     * <p>Decides if the child should be snapped from start or end, depending on where it
     * currently is in relation to its parent.</p>
     * <p>For instance, if the view is virtually on the left of RecyclerView, using
     * {@code SNAP_TO_ANY} is the same as using {@code SNAP_TO_START}</p>
     *
     * @see #calculateDtToFit(int, int, int, int, int)
     * @see #calculateDxToMakeVisible(android.view.View, int)
     * @see #calculateDyToMakeVisible(android.view.View, int)
     */
    public static final int SNAP_TO_ANY = 0;

    LinearSmoothScroller确定滑动方案的方法:

    /**
     * When scrolling towards a child view, this method defines whether we should align the top
     * or the bottom edge of the child with the parent RecyclerView.
     *
     * @return SNAP_TO_START, SNAP_TO_END or SNAP_TO_ANY; depending on the current target vector
     * @see #SNAP_TO_START
     * @see #SNAP_TO_END
     * @see #SNAP_TO_ANY
     */
    protected int getVerticalSnapPreference() {
        return mTargetVector == null || mTargetVector.y == 0 ? SNAP_TO_ANY :
                mTargetVector.y > 0 ? SNAP_TO_END : SNAP_TO_START;
    }

    重写LinearSmoothScroller的getVerticalSnapPreference方法:

class LinearTopSmoothScroller extends LinearSmoothScroller {

        public LinearTopSmoothScroller(Context context) {
            super(context);
        }

        @Override
        protected int getVerticalSnapPreference() {
            return SNAP_TO_START;
        }
    }

    这里为什么返回 SNAP_TO_START?可以看到LinearSmoothScrollerl的方法calculateDtToFit()根据不同滚动策略获取到需要滚动的距离,SNAP_TO_START是按置顶的方案来计算的。所以我们在getVerticalSnapPreference方法里固定返回SNAP_TO_START就可以实现目的。

public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int
            snapPreference) {
        switch (snapPreference) {
            case SNAP_TO_START:
                return boxStart - viewStart;
            case SNAP_TO_END:
                return boxEnd - viewEnd;
            case SNAP_TO_ANY:
                final int dtStart = boxStart - viewStart;
                if (dtStart > 0) {
                    return dtStart;
                }
                final int dtEnd = boxEnd - viewEnd;
                if (dtEnd < 0) {
                    return dtEnd;
                }
                break;
            default:
                throw new IllegalArgumentException("snap preference should be one of the"
                        + " constants defined in SmoothScroller, starting with SNAP_");
        }
        return 0;
    }

    调用方式一

    void scrollItemToTop(int position) {
        LinearSmoothScroller smoothScroller = new LinearTopSmoothScroller(this);
        smoothScroller.setTargetPosition(position);
        linearLayoutManager.startSmoothScroll(smoothScroller);
    }

    调用方式二

    自定义一个类继承自 LinearLayoutManager:

private class TopLayoutManager extends LinearLayoutManager {

    public TopLayoutManager(Context context) {
        super(context);
    }

    public TopLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }


    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
        RecyclerView.SmoothScroller smoothScroller = new LinearTopSmoothScroller(recyclerView.getContext());
        smoothScroller.setTargetPosition(position);
        startSmoothScroll(smoothScroller);
    }

    private class LinearTopSmoothScroller extends LinearSmoothScroller {

        public LinearTopSmoothScroller(Context context) {
            super(context);
        }

        @Override
        protected int getVerticalSnapPreference() {
            return SNAP_TO_START;
        }
    }
}

    调用代码:

TopLayoutManager topLayoutManager = new TopLayoutManager(this);
recycleview.setLayoutManager(topLayoutManager);
recycleview.smoothScrollToPosition(position);

二、调整平移滑动速率

    同理,可以在LinearSmoothScroller类找到决定滚动速度的方法并修改。

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

    上面的MILLISECONDS_PER_INCH的值为25F,如果希望更快可以将值改小,这个方法的返回值表示滚动一个像素需要的时间,单位ms,如果返回值为2ms,表示滚动1000个像素需要花费2秒时长。

    平滑滚动到target position,【置顶+调速】的调用方式:

        RecyclerView.SmoothScroller smoothScroller = new LinearSmoothScroller(this) {
            @Override protected int getVerticalSnapPreference() {
                return LinearSmoothScroller.SNAP_TO_START;
            }

            @Override
            protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
                //默认值是25F(MILLISECONDS_PER_INCH),值越小滑动速度越快,值越大则越慢
                return 100F / displayMetrics.densityDpi;
            }
        };
        smoothScroller.setTargetPosition(position);
        linearLayoutManager.startSmoothScroll(smoothScroller);

    目前还有一个问题,虽然我们可以调整速度,但是这里始终是一个固定的滚动速度,试想如果滚动的距离特别远,仍然需要滚动很长的时间;又或者滚动距离太近,那么滚动动画一瞬间就结束了,缺少了流畅感。

    所以我们可以根据需要滚动的远或近来设置不同的滚动速度:

        RecyclerView.SmoothScroller smoothScroller = new LinearSmoothScroller(this) {
            @Override protected int getVerticalSnapPreference() {
                return LinearSmoothScroller.SNAP_TO_START;
            }

            @Override
            protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
                // 第一个可见位置
                int firstItem = linearLayoutManager.findFirstVisibleItemPosition();
                int diff = Math.abs(position - firstItem);
                // 将 diff 作分母:滚动距离越远,速度越快。 (100f/diff) 数值如果过小会导致速度过快, 可以再乘一个速度因子变量(speedFactor)来调整
                int speedFactor = 5;
                float speed = (100f / diff) * speedFactor;
                return speed / displayMetrics.densityDpi;
            }
        };

三、其他方案:置顶、置顶加偏移、居中

1. 其他置顶方案

    另外一个实现置顶方案:可以参考这篇文章,Android RecyclerView滚动定位 ,它主要解决的是滚动到屏幕下面ITEM,无法置顶的问题,思路是:先用scrollToPosition,将要置顶的项先移动显示出来,然后计算这一项离顶部的距离,用scrollBy完成最后的100米!

    这个方案还有个好处就是,如果target position很远(滑动距离很长),也不会导致屏幕滚动过长的时间。向上向下动态滚动(动画过程)距离都不超过一个屏幕的距离。

2. 置顶加偏移

     另外,如果希望置顶后,可以有一定的偏移量(离顶部有一定距离),可以参考这篇文章:

RecyclerView的smooth scroller -- 诸多案例

3. 滚动居中

    如果希望target position在滚动结束后,停留在屏幕中间,可以参考下这篇文章:

RecyclerView smoothScroll to position in the center. android文章来源地址https://www.toymoban.com/news/detail-844428.html

到了这里,关于Android RecycleView实现平滑滚动置顶和调整滚动速度的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android Studio安卓开发-RecycleView新闻栏设计

    在上一博客中,我们完成类微信UI开发,在此基础上,在联系人界面实现RecycleView的简单用例,在发现界面实现RecycleView的流式布局。如下图所示。 对于RecycleView的基础布局学习我们先到这,现在需要我们对每一个RecycleView的Item实现点击操作,能够跳转至Item的详情界面,下面我

    2024年02月16日
    浏览(32)
  • MacOS 如何选择鼠标不飘滚动平滑

    今天不务正业的聊聊 macos 下的鼠标的事情,群里也有朋友和我聊,正好说说这事。 我在很长的时间里都在用 macbook pro 的触控板 + 键盘的高效模式,因为触控板和键盘很近所以效率很高。 但是有一个问题就是在高强度的码字编码后,手指有点磨皮了,你懂的这个触控板是光面

    2024年02月08日
    浏览(26)
  • Three.js 进阶之旅:页面平滑滚动-王国之泪 ?

    声明:本文涉及图文和模型素材仅用于个人学习、研究和欣赏,请勿二次修改、非法传播、转载、出版、商用、及进行其他获利行为。 浏览网页时,常被一些基于鼠标滚轮控制的页面动画所惊艳到,比如greensock 官网这些 showcase 案例页面就非常优秀,它们大多数都是使用 Tw

    2024年02月02日
    浏览(33)
  • js:scroll平滑滚动页面或元素到顶部或底部的方案汇总

    准备知识: scrollWidth: 是元素全部内容的宽度,包括由于overflow溢出而在屏幕上不可见的内容 scrollHeight: 是元素全部内容的高度,包括由于overflow溢出而在屏幕上不可见的内容 scrollTop: 纵向滚动条距离元素最顶部的距离 scrollLeft: 横向滚动条距离元素最左侧的距离 语法 参数 -

    2024年02月09日
    浏览(28)
  • 前端调整滚动条的外观样式

    css主要分三个步骤 1、设置滚动条宽度 ::-webkit-scrollbar { width: 5px; } 效果:分别设置50px和5px宽度 2、设置里面小滑块的样式 不设置background你将看不到小滑块,可以通过background设置小滑块的颜色 border-radius设置小滑块的圆角 3、设置导轨的样式,这一步不是必须 设置了background导

    2024年02月15日
    浏览(26)
  • Android中TextView滚动实现方式

    下面展示一些 内联代码片 。

    2024年02月21日
    浏览(27)
  • echarts tree自适应高度调整,加滚动条

    vue中, 至于滚动条

    2024年01月19日
    浏览(33)
  • 一个Adapter+recycleview实现多种布局,区分布局中

    最近因为需要所以学习了一下recycleview,使用Adapter修饰器修饰,使用一个Adapter+recycleview实现多种布局,而不是之前的三个Adapter在同一个recycleview中实现三个布局。点击区分布局中的gridview的图片和姓名。 嗯~,算是之前的在同一个recycleview中实现三个不同布局的升级版吧。因为

    2024年01月20日
    浏览(35)
  • 【Unity游戏开发基础】如何做可以调整音量的UI滚动条组件

    游戏的设置列表中,调整游戏声音大小的选项是必备的,如何实现拖动滚动条后音量相应改变大小呢?这里介绍一下相关的脚本和步骤 首先,新建一个调整音量的脚本,名叫SetVolume,把预置的Update方法和Start方法删除。 然后,这个脚本需要挂载在滚动元素的Slider组件下。这个

    2024年02月03日
    浏览(39)
  • Android——使用ScrollView实现滚动效果,当内容超出屏幕范围时自动滑动显示

    Android——使用ScrollView实现滚动效果,当内容超出屏幕范围时自动滑动显示 ScrollView是Android中常用的布局容器,用于在屏幕空间有限的情况下实现内容的滑动显示。当内容超出屏幕范围时,用户可以通过滑动屏幕来查看更多内容,提供了更好的用户体验。 在Android中,使用Sc

    2024年01月16日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包