教你控制 RecyclerView 滑动的节奏

教你控制 RecyclerView 滑动的节奏,第1张

概述最近,PM升级改版落地页,其中有一个很奇怪的交互需求,需求是这样的: 用户在该页面可以上下无限滑动,但是,在上拉滑动过程中,当内容切换为另一个内容的时候,新的内容先吸顶,然后停止滑动,让用户知道他已经

最近,PM升级改版落地页,其中有一个很奇怪的交互需求,需求是这样的:

  用户在该页面可以上下无限滑动,但是,在上拉滑动过程中,当内容切换为另一个内容的时候,新的内容先吸顶,然后停止滑动,让用户知道他已经滑到一个新的内容区了。同一个内容里面,没有该约束。下拉滑动过程也没有这种约束。

  或者用户没有滑动,但是点击到了新的内容区,也需要将新的内容缓慢吸顶,方便用户阅读。

作为RD的我,表示非常蛋疼啊,既然需求是这样的,作为万能的RD的我,当然得想办法去解决呢。

首先,选择 RecyclerVIEw 作为滑动的容器,难点就是怎么在滑动过程中,将新的内容页吸顶,停止滑动。对于 RecyclerVIEw, 当用户滑动后,最终通过 fling 方法来实现惯性滑动的,因此,必须拦截该方法,做一些特有的 *** 作。

试了试 RecyclerVIEw 自身的方法和管理器,都不能实现缓慢吸顶,看来只能重写其中的一些方法了。

public class MylinearLayoutmanger extends linearlayoutmanager {    private float MILliSECONDS_PER_INCH = 0.03f;    private Context contxt;    public MylinearLayoutmanger(Context context) {        super(context);        this.contxt = context;       // setSpeedSlow();    }    @OverrIDe    voID smoothScrollToposition(RecyclerVIEw recyclerVIEw,RecyclerVIEw.State state,int position) {        linearSmoothScroller linearSmoothScroller =                new linearSmoothScroller(recyclerVIEw.getContext()) {                    @OverrIDe                    public PointF computeScrollVectorForposition( targetposition) {                        return MylinearLayoutmanger.this.computeScrollVectorForposition(targetposition);                    }                    This returns the milliseconds it takes to                    scroll one pixel.                    @OverrIDe                    protected float calculateSpeedPerPixel(displayMetrics displayMetrics) {                        return MILliSECONDS_PER_INCH / displayMetrics.density;                        返回滑动一个pixel需要多少毫秒                    }                    @OverrIDe                    int calculateDtToFit(int vIEwStart,1)">int vIEwEnd,1)">int BoxStart,1)">int BoxEnd,1)"> snapPreference) {                        return BoxStart - vIEwStart;                    }                };        linearSmoothScroller.setTargetposition(position);        startSmoothScroll(linearSmoothScroller);    }}

我们来看 linearSmoothScroller 中的一段源码:

/**     * Helper method for {@link #calculateDxTomakeVisible(androID.vIEw.VIEw,int)} and     * { #calculateDyTomakeVisible(androID.vIEw.VIEw,int)}     */                snapPreference) {        switch (snapPreference) {            case SNAP_TO_START:                return BoxStart - vIEwStart;             SNAP_TO_END:                return BoxEnd - vIEwEnd;             SNAP_TO_ANY:                final int dtStart = BoxStart - vIEwStart;                if (dtStart > 0) {                    return dtStart;                }                int dtEnd = BoxEnd - vIEwEnd;                if (dtEnd < 0 dtEnd;                }                break;            default:                throw new IllegalArgumentException("snap preference should be one of the"                        + " constants defined in SmoothScroller,starting with SNAP_");        }        return 0;    }

可以看到,SNAP_TO_START 就是上边或者左边吸顶的意思。这样只要重写 calculateDtToFit 方法就实现了缓慢吸顶。

       * Align child vIEw's left or top with parent vIEw's left or top     *     * @see #calculateDtToFit(int,int,int)     * static int SNAP_TO_START = -1;

那什么时候调用呢?一个是点击新的内容区时候调用,另一个是再滑动过程中判断是否要切换内容区了。这时候就要重写 

package com.sjq.recycletest;import androID.content.Context; androID.harDWare.SensorManager; androID.support.annotation.Nullable; androID.support.v7.Widget.linearlayoutmanager; androID.support.v7.Widget.RecyclerVIEw; androID.util.AttributeSet; androID.util.Log; androID.vIEw.MotionEvent; androID.vIEw.VIEw; androID.vIEw.VIEwConfiguration; * Created by shenjiaqi on 2018/7/18. */class MyRecyclerVIEw  RecyclerVIEw {    float DECELERATION_RATE = (float) (Math.log(0.78) / Math.log(0.9));    float INFLEXION = 0.35f;  Tension lines cross at (INFLEXION,1)    float mFlingFriction = VIEwConfiguration.getScrollFriction();     mPhysicalCoeff;    final String TAG = "MyRecyclerVIEw" height;     MyRecyclerVIEw(Context context) {        this(context,1)">null);    }     MyRecyclerVIEw(Context context,@Nullable AttributeSet attrs) {        public MyRecyclerVIEw(Context context,@Nullable AttributeSet attrs,1)"> defStyle) {        (context,defStyle);        float ppi = context.getResources().getdisplayMetrics().density * 160.0f;        mPhysicalCoeff = SensorManager.GraviTY_EARTH  g (m/s^2)                * 39.37f  inch/meter                * ppi                * 0.84f;  look and feel tuning        height = context.getResources().getdisplayMetrics().heightPixels;    }    double getSplineDeceleration( veLocity) {        return Math.log(INFLEXION * Math.abs(veLocity) / (mFlingFriction * mPhysicalCoeff));    }  // 惯性滑动的距离    double getSplineFlingdistance(double l = getSplineDeceleration(veLocity);        double decelMinusOne = DECELERATION_RATE - 1.0;        return mFlingFriction * mPhysicalCoeff * Math.exp(DECELERATION_RATE / decelMinusOne * l);    }    /* Returns the duration,ex@R_403_4848@ in milliseconds int getSplineFlingDuration(return (int) (1000.0 * Math.exp(l / decelMinusOne));    }    @OverrIDe    voID onnestedPreScroll(VIEw target,1)">int dx,1)">int dy,1)">[] consumed) {        .onnestedPreScroll(target,dx,dy,consumed);    }    @OverrIDe    boolean onnestedPreFling(VIEw target,1)">float veLocityX,1)"> veLocityY) {        return .onnestedPreFling(target,veLocityX,veLocityY);    }    @OverrIDe    boolean fling(int veLocityX,1)">if (veLocityY > 0) {            if (getLayoutManager() != null                    && getLayoutManager() instanceof linearlayoutmanager) {                linearlayoutmanager manager = (linearlayoutmanager) getLayoutManager();                int firstposition = manager.findFirstVisibleItemposition();                int lastposition = manager.findLastVisibleItemposition();
          // 假设一个item高度为 500, 通过惯性滑动距离和高度可以计算出来会经过多少个item
int position = (int) (firstposition + getSplineFlingdistance((int) veLocityY) / 500);
          // 以8个item为一个内容区
int s1 = firstposition / 8; int s = lastposition > position ? lastposition / 8 : position / 8if (s > s1) { s = s1 + 1; } int pos = s * 8;int top = height; if (s > s1 && lastposition >= pos && pos > firstposition) { top = getChildAt(pos - firstposition).gettop(); } if (s > 0 && s > s1) { smoothScrollToposition(pos); true; } } } .fling(veLocityX,veLocityY); } getdisplyHeight() { height; }}

但是大家会发现,如果 item 高度值估计过小,会导致,一滑动就会立马切换到新的内容区,体验还是不好。

眼尖的人一定会发现,我在前面 MylinearLayoutmanger 构造函数中注释掉了一个方法。这个方法就是用来改善滑动效果的。

 voID setSpeedSlow() {        自己在这里用density去乘,希望不同分辨率设备上滑动速度相同        0.3f是自己估摸的一个值,可以根据不同需求自己修改        MILliSECONDS_PER_INCH = contxt.getResources().getdisplayMetrics().density * 0.03f;    }

调用该方法后,大家会发现效果好多了,但是其实默认值  MILliSECONDS_PER_INCH 是 25f;

你会发现效果似乎更好了。

 

希望本文对大家进一步了解 RecyclerVIEw 有帮助。

总结

以上是内存溢出为你收集整理的教你控制 RecyclerView 滑动的节奏全部内容,希望文章能够帮你解决教你控制 RecyclerView 滑动的节奏所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/1121336.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-29
下一篇 2022-05-29

发表评论

登录后才能评论

评论列表(0条)

保存