Android Recycler View slides quickly to the top

Keywords: github Fragment

Reproduced at https://github.com/baiiu


When using RecyclerView, the smoothScrollToPostion() method is called to slide to the specified location, but entries often slide very slowly. This article is to realize the fast sliding of RecyclerView.

This paper first introduces how to implement it, then introduces the principle.

1. Implementation code

  1. Create FastScrollLinear Layout Manager, inherit Linear Layout Manager
  2. Override the smoothScrollToPosition() method, mainly the method in LinearSmoothScroller

The code is as follows. Explanations are all 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, milliseconds/pixels, how many milliseconds does it take to slide one pixel?

                     Default is (25F/densityDpi) ms/pixels

                     mdpi On the top, there are 160 pixels in an inch, 25/160.
                     xxhdpi,1 The inch has 480 pixels, 25/480.
                  */

                  //return 10F / displayMetrics.densityDpi; // Can reduce time, default 25F
                 return super.calculateSpeedPerPixel(displayMetrics);
            }

           //This method calculates the sliding time. Indirectly control the speed here.
           //Calculates the time it should take to scroll the given distance (in pixels)
           @Override 
           protected int calculateTimeForScrolling(int dx) {
               /*
                   Control the distance and then calculate the time according to the speed provided by the calculateSpeedPerPixel()).

                   By default, a scroll TARGET_SEEK_SCROLL_DISTANCE_PX = 10000 pixels.

                   This value can be reduced here to reduce the rolling time.
                */

                //Increase speed in indirect computing or directly in calculateSpeedPerPixel
                if (dx > 3000) {
                    dx = 3000;
                }

                int time = super.calculateTimeForScrolling(dx);
                LogUtil.d(time);//Look at the printing time

                return time;
            }
        };

        linearSmoothScroller.setTargetPosition(position);
        startSmoothScroll(linearSmoothScroller);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

From the two methods of duplication, we can see that both of them are to improve the sliding speed. One is to modify the speed directly, the other is to reduce the time needed by reducing the distance and indirectly improve the sliding speed.

Either way, you can see what you need. Next, I will talk about the principle of implementation. I'm just going to sort out the general process, but by now you've been able to achieve fast sliding.

2. RecyclerView sliding process combing

1. Call RecyclerView.smoothScrollToPosition(position)

public void smoothScrollToPosition(int position) {
        //... directly invoked the method of LayoutManager
        mLayout.smoothScrollToPosition(this, mState, position);
    }
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

2. Linear Layout Manager

@Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
            int position) {
        LinearSmoothScroller linearSmoothScroller = new  LinearSmoothScroller(recyclerView.getContext()) {
                  //...
                };
        //Setting the Endpoint Position
        linearSmoothScroller.setTargetPosition(position);
        //startSmoothScroll() is the method in LayoutManager.
        startSmoothScroll(linearSmoothScroller);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

3. In Layout Manager

public void startSmoothScroll(SmoothScroller smoothScroller) {
     //...
     mSmoothScroller = smoothScroller;
     //Call the SmoothScroller.start() method to start scrolling. this parameter refers to the current LayoutManager
     mSmoothScroller.start(mRecyclerView, this);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

4. In SmoothScroller

void start(RecyclerView recyclerView, LayoutManager layoutManager) {
    //...
    //Using ViewFlinger for animation, ViewFlinger implements the Runnable interface and uses Scroller internally so that it can post itself and then continuously lay out Recycler View to achieve sliding.
    mRecyclerView.mViewFlinger.postOnAnimation();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

5. In ViewFlinger, this is the focus of sliding, omitting a lot of code logic

private class ViewFlinger implements Runnable {
     @Override
     public void run() {
        if (scroller.computeScrollOffset()) {
            //Calling SmoothScroller's onAnimation method
            smoothScroller.onAnimation(dx - overscrollX, dy - overscrollY);
        }
     }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

6. In SmoothScroller

private void onAnimation(int dx, int dy) {
    //...
    if (mTargetView != null) {
        // verify target position
        if (getChildPosition(mTargetView) == mTargetPosition) {
            //The position to slide to is displayed on the screen. In onTargetFound() method, the update differentiator is changed from a linear differentiator to a decelerating differentiator.
            onTargetFound(mTargetView, recyclerView.mState, mRecyclingAction);
             mRecyclingAction.runIfNecessary(recyclerView);
         }

         //...

        if (mRunning) {
            //Next slide
            onSeekTargetStep(dx, dy, recyclerView.mState, mRecyclingAction);
            //Calling the runIfNecessary method of the inner class Action
            mRecyclingAction.runIfNecessary(recyclerView);
            }
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

7.Action medium

private void runIfNecessary(RecyclerView recyclerView) {
    //The ViewFlinger.smoothScrollBy() method is called and passed into mDuration. mDuration is passed in when upDate() is in SmoothScroller, which is determined by the two methods mentioned above.
    recyclerView.mViewFlinger.smoothScrollBy(mDx, mDy, mDuration, mInterpolator);
}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

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;
            //Call Scroller to start scrolling, where duration is
            mScroller.startScroll(0, 0, dx, dy, duration);
            postOnAnimation();
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

This section roughly describes the rolling process according to the process, involving more classes, and finally scrolls through Scroller.



Using Demo

public class FastScrollFragment extends Fragment implements View.OnClickListener {


    private RecyclerView recyclerView;
    private SimpleTextAdapter mAdapter;
    private int mVisibleCount;


    @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
            @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_fastscroll, container, false);


        recyclerView = (RecyclerView) view.findViewById(R.id.recyclerView);


        LinearLayoutManager linearLayoutManager = new FastScrollLinearLayoutManager(getContext());
        recyclerView.setLayoutManager(linearLayoutManager);


        mAdapter = new SimpleTextAdapter(getContext(), 500);
        recyclerView.setAdapter(mAdapter);


        recyclerView.getViewTreeObserver()
                .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                    @Override public void onGlobalLayout() {
                        recyclerView.getViewTreeObserver()
                                .removeGlobalOnLayoutListener(this);


                        LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();


                        mVisibleCount = linearLayoutManager.findLastVisibleItemPosition()
                                - linearLayoutManager.findFirstVisibleItemPosition() + 1;
LogUtil.d("Display so many: +mVisibleCount);
                    }
                });


        recyclerView.addItemDecoration(
                new RecyclerViewDivider(getContext(), LinearLayoutManager.VERTICAL, 20, Color.BLUE));




        view.findViewById(R.id.fast_top)
                .setOnClickListener(this);
        view.findViewById(R.id.fast_top_zhihuway)
                .setOnClickListener(this);
        view.findViewById(R.id.fast_end)
                .setOnClickListener(this);


        return view;
    }


    @Override public void onClick(View v) {
        switch (v.getId()) {
            case R.id.fast_top_zhihuway:
                /*
Similarly, first go directly to a position, then slide to the top.
                 */
                LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
                int firstVisibleItemPosition = linearLayoutManager.findFirstVisibleItemPosition();


                if (firstVisibleItemPosition > mVisibleCount) {
                    recyclerView.scrollToPosition(mVisibleCount);
                }
                recyclerView.smoothScrollToPosition(0);


                break;


            /*
Both of them shorten the unit time distance in Linear Smooth Scroller to reduce time and slide quickly.
             */
            case R.id.fast_top:
                recyclerView.smoothScrollToPosition(10000);
                break;
            case R.id.fast_end:
                recyclerView.smoothScrollToPosition(0);
                break;
        }
    }
}


Conclusion:

This article implements a fast scroll of RecyclerView, but one thing to note is that if your Item is complex, scrolling can get stuck. This is mentioned in a comment when looking at the source code, which was later found in practice. Have to say WeChat The circle of friends slides really fast. It uses ListView, which seems to open the FastEnable property.  
Similarly, you can use RecyclerView.scrollToPosition(position) to slide directly to a certain position before using smoothScrollToPosition(0) to slide to the top. This is mentioned in the comment on jumpTo() in the Action class in RecyclerView. If you are far away, you can go to a location first and then slide.

Both ways of sliding to the top implements a small Demo. test The code is on GitHub. FastScrollFragment .  
In addition, it is also used in small projects written by myself. ZhihuDaily You can see these two Demo s for more details.


Original address: http://blog.csdn.net/u014099894/article/details/51855129


Posted by ceci on Sat, 30 Mar 2019 13:36:29 -0700