最近,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 滑动的节奏所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)