RecyclerView调用smoothScrollToPosition() 控制滑动速度

前言

本文是自己学习这篇文章的总结,
https://mcochin.wordpress.com/2015/05/13/android-customizing-smoothscroller-for-the-recyclerview/ 《ANDROID: CUSTOMIZING SMOOTHSCROLLER FOR THE RECYCLERVIEW》
调用RecyclerView的smoothScrollToPositon可以指定RecyclerView滑动到某个item,但这个滑动的速度很快,如果想要滑动速度可以控制,那么应该怎么办呢?

思路

smoothScrollToPosition()方法

我们知道在调用RecyclerView的前,应该先为RecyclerView设定一个LayoutManager。举例如下:
mRecyclerView.setLayoutManager(mLinearLayoutManager);
调用mRecyclerView.smoothScrollToPosition 时候,RecyclerView实际调用的是LayoutManager中的代码。从RecyclerView的源码可以看出:

  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就是初始化RecyclerView设置的LayoutManager
        mLayout.smoothScrollToPosition(this, mState, position);
    }

自定义LayoutManager

接下来我们可以自定一个LayoutManager,然后复写LayoutManager中的smoothScrollToPosition(this, mState, position) 方法,来达到控制滑动速度的目的。以LinearLayoutManager为例子,代码如下:

/**
 * 控制滑动速度的LinearLayoutManager
 */
public class ScrollSpeedLinearLayoutManger extends LinearLayoutManager {
    private float MILLISECONDS_PER_INCH = 0.03f;
    private Context contxt;

    public ScrollSpeedLinearLayoutManger(Context context) {
        super(context);
        this.contxt = context;
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
        LinearSmoothScroller linearSmoothScroller =
                new LinearSmoothScroller(recyclerView.getContext()) {
                    @Override
                    public PointF computeScrollVectorForPosition(int targetPosition) {
                        return ScrollSpeedLinearLayoutManger.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需要多少毫秒
                    }

                };
        linearSmoothScroller.setTargetPosition(position);
        startSmoothScroll(linearSmoothScroller);
    }


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

    public void setSpeedFast() {
        MILLISECONDS_PER_INCH = contxt.getResources().getDisplayMetrics().density * 0.03f;
    }
}

附上LinearLayoutManager中的 smoothScrollToPosition 源码,对比可以看出我们仅仅是覆写了LinearSmoothScroller中的calculateSpeedPerPixel 这个方法而已。

//LinearLayoutManager中源码
 @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
            int position) {
        LinearSmoothScroller linearSmoothScroller =
                new LinearSmoothScroller(recyclerView.getContext()) {
                    @Override
                    public PointF computeScrollVectorForPosition(int targetPosition) {
                        return LinearLayoutManager.this
                                .computeScrollVectorForPosition(targetPosition);
                    }
                };
        linearSmoothScroller.setTargetPosition(position);
        startSmoothScroll(linearSmoothScroller);
    } 

核心方法:

   //This returns the milliseconds it takes to
                    //scroll one pixel.
                    @Override
                    protected float calculateSpeedPerPixel
                    (DisplayMetrics displayMetrics) {
                        return MILLISECONDS_PER_INCH / displayMetrics.density;
                        //返回滑动一个pixel需要多少毫秒
                    }

以上就是自己总结,大家也可以去看前言贴出的原文。

顺便介绍下上面涉及的PointF这个类。

/**
 * PointF holds two float coordinates
 */
public class PointF implements Parcelable {
    public float x;
    public float y;

    public PointF() {}

    public PointF(float x, float y) {
        this.x = x;
        this.y = y; 
    }

    public PointF(Point p) { 
        this.x = p.x;
        this.y = p.y;
    }

    //...一些set get 和 Parcelable相关的方法
}

这个是一个坐标类,x,y两点表示坐标位置。
在一些触摸相关的例子中,比较常见。在这里则是用来表示RecyclerView的滑动方向。
//for y: use -1 for up direction, 1 for down direction.
//for x (did not test): use -1 for left direction, 1 for right direction.
举例,从item50滑动到item100,那么此时pointF就是(0,-1)。

你可能感兴趣的:(UI)