Preface:

When RecyclerView is used, smoothScrollToPostion() method is called to slide to the specified position, but the sliding is very slow when there are many items. This article is to realize the fast sliding of RecyclerView.

How to do this first, and then the principle.

1. Implementation code

  1. Create FastScrollLinearLayoutManager, LinearLayoutManager inheritance
  2. Replicating smoothScrollToPosition() method, mainly replicating the method in LinearSmoothScroller

The code is as follows, explained in the comments:

public class FastScrollLinearLayoutManager extends LinearLayoutManager { public FastScrollLinearLayoutManager(Context context) { super(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 FastScrollLinearLayoutManager.this.computeScrollVectorForPosition(targetPosition); } // This method controls the speed. //if returned value is 2 ms, it means scrolling 1000 pixels with LinearInterpolation should take 2 seconds. @Override protected float CalculateSpeedPerPixel (DisplayMetrics DisplayMetrics) {/* Control unit speed, ms/pixel, how many milliseconds it takes to slide 1 pixel. Default is (25F/densityDpi) ms/pixel on MDPI,1 inch has 160 pixels, 25/160, xxhdPI,1 inch has 480 pixels, 25/480, */ //return 10F / displayMetrics.densityDpi; / / time can be reduced, the default of 25 f return. Super calculateSpeedPerPixel (displayMetrics); } // This method calculates the time required to slide. Speed is indirectly controlled here. //Calculates the time it should take to scroll the given distance (in pixels) @Override protected int CalculateTimeForScrolling (int dx) {/ * control distance, then according to the above the parties (calculateSpeedPerPixel ()) provide calculate the speed of the time, By default, a single scroll TARGET_SEEK_SCROLL_DISTANCE_PX = 10000 pixels, which can be reduced here to reduce the scrolling time. CalculateSpeedPerPixel can also be directly in the if (dx > 3000) {dx = 3000; } int time = super.calculateTimeForScrolling(dx); LogUtil.d(time); Return time; }}; linearSmoothScroller.setTargetPosition(position); startSmoothScroll(linearSmoothScroller); }}Copy the code

As can be seen from the two methods of copying, both are to improve the sliding speed. One is to modify the speed directly, and the other is to increase the speed indirectly by reducing the distance required to reduce the time.

You can go either way, depending on what you need. Let’s talk about implementation. I’m just going to go through the general process here, but so far you can do a quick swipe.

2. RecyclerView sliding process combing

1. Call RecyclerView. SmoothScrollToPosition (position)

public void smoothScrollToPosition(int position) { //... Direct call the LayoutManager mLayout. This method smoothScrollToPosition (this, mState, position); }Copy the code

2. LinearLayoutManager

@Override public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) { LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext()) { //... }; / / set the end position linearSmoothScroller. SetTargetPosition (position); // Start scrolling, use the LinearSmoothScroller(keep sliding at a constant speed, slow down when targetPosition appears on the screen), StartSmoothScroll () is the method in LayoutManager startSmoothScroll(linearSmoothScroller); }Copy the code

3. The LayoutManager

public void startSmoothScroll(SmoothScroller smoothScroller) { //... mSmoothScroller = smoothScroller; // Call the smoothScroll.start () method to start scrolling,this parameter refers to the current LayoutManager mSmoothScroll.start (mRecyclerView, this); }Copy the code

4. SmoothScroller

void start(RecyclerView recyclerView, LayoutManager layoutManager) { //... ViewFlinger implements Runnable interface and uses Scroller internally. So you can post their and then to sliding mRecyclerView RecyclerView constantly layout can be achieved. MViewFlinger. PostOnAnimation (); }Copy the code

5.ViewFlinger, which is the focus of sliding, omits a lot of code logic

private class ViewFlinger implements Runnable { @Override public void run() { if (scroller.computeScrollOffset()) { / / call SmoothScroller onAnimation method SmoothScroller. OnAnimation (dx - overscrollX, dy - overscrollY); }}}Copy the code

6. SmoothScroller

private void onAnimation(int dx, int dy) { //... if (mTargetView ! If (getChildPosition(mTargetView) == mTargetPosition) {if (getChildPosition(mTargetView) == mTargetPosition) The onTargetFound() method updates the differentiator from a linear differentiator to a decelerator. onTargetFound(mTargetView, recyclerView.mState, mRecyclingAction); mRecyclingAction.runIfNecessary(recyclerView); } / /... If (mRunning) {// Next time swipe onSeekTargetStep(dx, dy, recyclerView.mState, mRecyclingAction); / / call the inner class Action runIfNecessary method mRecyclingAction. RunIfNecessary (recyclerView); }}Copy the code

7. The Action

Private void runIfNecessary (RecyclerView RecyclerView) {/ / call the ViewFlinger smoothScrollBy () method, and the incoming mDuration, MDuration is passed in on a SmoothScroller upDate(), Is determined by the mentioned about the two methods of common recyclerView. MViewFlinger. SmoothScrollBy (mDx, mDy, mDuration mInterpolator); }Copy the code

8. Start scrolling in ViewFlinger

public void smoothScrollBy(int dx, int dy, int duration, Interpolator interpolator) { if (mInterpolator ! = interpolator) { mInterpolator = interpolator; mScroller = ScrollerCompat.create(getContext(), interpolator); } setScrollState(SCROLL_STATE_SETTLING); mLastFlingX = mLastFlingY = 0; Duration mScroller. StartScroll (0, 0, dx, dy, duration); postOnAnimation(); }Copy the code

This part roughly describes the rolling process according to the process, involving many analogies, and finally rolling through Scroller.

Conclusion:

This article realized RecyclerView fast scrolling, but need to pay attention to a problem: if your Item is more complex, scrolling will be slow. This was mentioned in a comment when looking at the source code, and was indeed found later in practice. I have to say that wechat circle of friends is really fast, it is using ListView, seems to open the FastEnable attribute. At the same time also can imitate zhihu, use first RecyclerView. ScrollToPosition (position) sliding directly to a particular position before using smoothScrollToPosition slide (0) to the top. This is mentioned in the jumpTo() comment of the Action class in RecyclerView, and if it’s too far away you can go to a location and then slide.

Both of these slides to the top make for a small Demo. Test code FastScrollFragment on GitHub. In addition, I have also used ZhihuDaily in my own small projects. You can check out these two demos to learn more about it.