SoFunction
Updated on 2025-04-10

Recycleview realizes infinite automatic carousel

Overview

RecycleView implements infinite repetition of specific data, in my opinion, there are only two ways.

1. Modify the adpter multiplexing mechanism to reuse data infinitely
2. Return the data length in adpter and return the maximum value of Integer

Since the first type can achieve infinite repetition of data, the data bits still have no change, so when it automatically jumps to the end, it cannot be carouseled to the next bit, so here I use the second way to achieve automatic carousel

Briefly describe the multiplexing mechanism of modifying adpter

Let's take LinearLayoutManager as an example. We just need to re-play LinearLayoutManager to do some tricks and feet when drawing.

package ;

import ;
import ;
import ;
import ;
import ;
import ;

import ;
import ;
import ;

/**
  * @author Version: 1.0
  * Created date: 2020/4/14 14
  * describe:
  */
public class ScrollSpeedLinearLayoutManger extends LinearLayoutManager {
 public ScrollSpeedLinearLayoutManger(Context context) {
  super(context);
 }

 public ScrollSpeedLinearLayoutManger(Context context, int orientation, boolean reverseLayout) {
  super(context, orientation, reverseLayout);
 }

 public ScrollSpeedLinearLayoutManger(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
  super(context, attrs, defStyleAttr, defStyleRes);
 }

 @Override
 public  generateDefaultLayoutParams() {
  return new (.WRAP_CONTENT,.WRAP_CONTENT);
 }

 // 1 When the RecyclerView is initialized, it will be called twice.// 2 When calling(), it will be called.// 3 When calling setAdapter to replace Adapter, it will be called.// 4 It will also be called when the RecyclerView executes an animation. @Override
 public void onLayoutChildren( recycler,  state) {
  ("TAG","onLayoutChildren ");
  if (getItemCount() == 0){
   detachAndScrapAttachedViews(recycler);
   return;
  }
  //() supports animation  if (getItemCount() == 0 && ()){
   return;
  }
  //Remove all the views in the current Recycler and put them in the scrap cache, and then reuse the views in the cache first.  detachAndScrapAttachedViews(recycler);

  int actualHeight = 0;
  for (int i = 0 ;i < getItemCount() ; i++){
   View scrap = (i);
   addView(scrap);
   measureChildWithMargins(scrap,0,0);
   int width = getDecoratedMeasuredWidth(scrap);
   int height = getDecoratedMeasuredHeight(scrap);
   layoutDecorated(scrap,0,actualHeight,width,actualHeight+height);
   actualHeight+=height;
   //If you go beyond the interface, you won't draw it, and you won't add it.   if (actualHeight > getHeight()){
    break;
   }
  }
 }

 @Override
 public boolean canScrollVertically() {
  return true;
 }


 @Override
 public int scrollVerticallyBy(int dy,  recycler,  state) {
  ("feifeifei","getChildCount() " + getChildCount() + " ().size() " + ().size());

  //When the interface scrolls down, dy is positive, dy is negative when scrolling upward, dy is negative when scrolling upward
  //filling  fill(dy,recycler,state);
  //scroll  offsetChildrenVertical(dy*-1);

  //Recycling has left the interface  recycleOut(dy,recycler,state);

  return dy;
 }

 private void fill(int dy,  recycler,  state){
  //Scroll down  if (dy > 0){
   //Fill in the bottom first   View lastView = getChildAt(getChildCount() -1);
   int lastPos = getPosition(lastView);
   if (() - dy < getHeight()){
    View scrap;
    if (lastPos == getItemCount() -1){
     scrap = (0);
    }else {
     scrap = (lastPos+1);
    }
    addView(scrap);
    measureChildWithMargins(scrap,0,0);
    int width = getDecoratedMeasuredWidth(scrap);
    int height = getDecoratedMeasuredHeight(scrap);
    layoutDecorated(scrap,0,(),width,()+height);
   }
  }else {
   //Scroll up   //The top fill now   View firstView = getChildAt(0);
   int layoutPostion = getPosition(firstView);

   if (() >= 0 ){
    View scrap ;
    if (layoutPostion == 0){
     scrap = (getItemCount()-1);
    }else {
     scrap = (layoutPostion -1);
    }
    addView(scrap,0);
    measureChildWithMargins(scrap,0,0);
    int width = getDecoratedMeasuredWidth(scrap);
    int height = getDecoratedMeasuredHeight(scrap);
    layoutDecorated(scrap,0,() - height,width,());
   }
  }
 }

 private void recycleOut(int dy,  recycler,  state){
  for (int i = 0 ; i <getChildCount() ;i++){
   View view = getChildAt(i);
   if (dy >0){
    if (()-dy <0){
     ("feifeifei","recycleOut " + i);
     removeAndRecycleView(view,recycler);
    }
   }else {
    if (()-dy > getHeight()){
     ("feifeifei","recycleOut " + i);
     removeAndRecycleView(view,recycler);
    }
   }
  }
 }

 @Override
 public void smoothScrollToPosition(RecyclerView recyclerView,  state, int position) {
   smoothScroller = new CenterSmoothScroller(());
  (position);
  startSmoothScroll(smoothScroller);
 }

 private class CenterSmoothScroller extends LinearSmoothScroller {
  public CenterSmoothScroller(Context context) {
   super(context);
  }
  protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
   return 0.2f;
  }
 }
}

This is probably the grid that is written in this way, you need to rewrite it yourself because there will be differences in calculations. Here I will briefly explain it.

Main topic

Implementation of Adapter Adapter

There is no difference in writing. The only getItemCount and onBindViewHolder need to be processed as follows

public class AdAuditorAdapter extends <> {
 private Context mContext;
 private List<String> mData;

 public AdAuditorAdapter(Context mContext, List<String> mData) {
   = mContext;
   = mData;
 }

 @NonNull
 @Override
 public MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
  MyViewHolder holder = new MyViewHolder((mContext).inflate(.item_adauditor, viewGroup, false));
  return holder;
 }

 @Override
 public void onBindViewHolder(@NonNull MyViewHolder myViewHolder, @SuppressLint("RecyclerView") int i) {
  ("HHHHHHHHH", "onBindViewHolder: -->"+i );
  //Fetch the remaining, otherwise the index will be crossed.  myViewHolder.tv_1.setText((i%()));
  (new () {
   @Override
   public void onClick(View v) {
    (v, i%(), 3);
   }
  });

 }


 @Override
 public int getItemCount() {
 //Return the maximum adpter value  return Integer.MAX_VALUE;
 }

 public void update(List<String> list) {
   = list;
  notifyDataSetChanged();
 }

 class MyViewHolder extends  {
  TextView tv_1;


  public MyViewHolder(@NonNull View itemView) {
   super(itemView);

   tv_1 = (.tv_1); //ID

  }
 }

 public MyClickListener getListener() {
  return listener;
 }

 public void setMyClickListener(MyClickListener listener) {
   = listener;
 }

 MyClickListener listener;

 public interface MyClickListener {
  void onClick(View view, int position, int type);
 }

 public List<String> getData() {
  return mData;
 }
}

Implementation of activity

1 Basic implementation

1.1 Add fake data to write a click event
1.2 Use handler to delay sending a message (position); move to the specified location
1.3 Click to stop moving

2 Effect optimization

2.1 Add uniform damping effect
2.2 Implementing infinite carousel to consider the case that the value exceeds the Integer maximum value
2.3 When clicking on the recycleview when it is being rotated, the carousel will stop, and clicking again will execute the click event (optimized to click stop and execute the click event)
The damping effect is to reduce the sliding rate

We do

package ;

import ;
import ;
import ;
import ;
import ;
import ;

import ;
import ;
import ;

/**
  * @author Version: 1.0
  * Created date: 2020/4/14 14
  * describe:
  */
public class ScrollSpeedGridLayoutManager1 extends GridLayoutManager {


 public ScrollSpeedGridLayoutManager1(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
  super(context, attrs, defStyleAttr, defStyleRes);
 }

 public ScrollSpeedGridLayoutManager1(Context context, int spanCount) {
  super(context, spanCount);
 }

 public ScrollSpeedGridLayoutManager1(Context context, int spanCount, int orientation, boolean reverseLayout) {
  super(context, spanCount, orientation, reverseLayout);
 }


 @Override
 public void smoothScrollToPosition(RecyclerView recyclerView,  state, int position) {
   smoothScroller = new CenterSmoothScroller(());
  (position);
  startSmoothScroll(smoothScroller);
 }

 private class CenterSmoothScroller extends LinearSmoothScroller {
  public CenterSmoothScroller(Context context) {
   super(context);
  }
  protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
   return 10f;//Sliding rate problem  }
 }
}

Activity all codes

package ;

import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;

import ;
import ;
import ;
import ;
import ;
import ;

import ;
import ;

import ;
import ;
import ;

/**
  * @author Version: 1.0
  * Created date: 2020/4/14 14
  * describe:
  */
public class RecycleViewActivity extends AppCompatActivity {
 static RecyclerView rv_1;
 private static int HANDLER_MSG = 0x0011;
 private static int HANDLER_LONG_MSG = 0x0021;
 static int position = 0;
 static int addNum = 3;
 @SuppressLint("HandlerLeak")
 private static Handler handler = new Handler() {
  @Override
  public void handleMessage(@NonNull Message msg) {
   if ( == HANDLER_MSG) {
   // The 9 grid effect is the same rate    if (addNum==3){
     position = position + addNum;
     addNum = 6;
    }else {
     position = position + addNum;
     addNum = 3;
    }
    ("TAG", "handleMessage: -->" + position);
    smoothMoveToPosition(rv_1, position >= 0 ? position : 0);
    if (position==Integer.MAX_VALUE/2){
     //Click or reset adapter by more than 2% of Integer.MAX_VALUE     LongAutoMove();
    }else {
     AutoMove();
    }

   }else if (==HANDLER_LONG_MSG){
    position = 0;
    addNum = 3;
    ("TAG", "handleMessage: -->" + position);
    smoothMoveToPosition(rv_1, 0);
    AutoMove();

   }
  }


 };

 private static AdAuditorAdapter adAuditorAdapter;
 static List<String> strings;
 @Override
 protected void onCreate(@Nullable Bundle savedInstanceState) {
  (savedInstanceState);
  setContentView(.activity_recycle);
  rv_1 = findViewById(.rv_1);
  strings = new ArrayList<>();
  for (int i = 0; i < 50; i++) {
   (i + "");
  }
  adAuditorAdapter = new AdAuditorAdapter(this, strings);
  (new () {
   @Override
   public void onClick(View view, int position, int type) {
    (, ().get(position), Toast.LENGTH_SHORT).show();
    StopMove();
   }
  });
  GridLayoutManager layoutManager = new ScrollSpeedGridLayoutManager1(this,3,, false);
  rv_1.setLayoutManager(layoutManager);
  rv_1.setAdapter(adAuditorAdapter);
  rv_1.addOnScrollListener(new () {
   @Override
   public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
    (recyclerView, newState);
//    if (mShouldScroll && RecyclerView.SCROLL_STATE_IDLE == newState) {
//     mShouldScroll = false;
//     smoothMoveToPosition(recyclerView, mToPosition);
//    }
    ("TAG", "onScrollStateChanged11111111: -->" + newState);
    if (newState == 1) {
//      holder = (());
     (new () {
      @Override
      public void onClick(View v) {
       (, ().get(position)+"........", Toast.LENGTH_SHORT).show();
      }
     });
     StopMove();
     LongAutoMove();
    }
   }

//   @Override
//   public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
//    (recyclerView, dx, dy);
//    ("TAG", "onScrolled: dx=" +dx +" dy="+dy );
//   }
  });
  rv_1.setOnTouchListener(new () {
   @Override
   public boolean onTouch(View v, MotionEvent event) {
    if (()==MotionEvent.ACTION_DOWN){
     // Monitor the click position and find the view to realize the click event     View childView = rv_1.findChildViewUnder((), ());
     ("GGGGGGGGGGGGGGGGG", "onTouch: -->"+rv_1.getChildLayoutPosition(childView));
     ().onClick(v, rv_1.getChildLayoutPosition(childView),1);
    }
    return false;
   }
  });
  AutoMove();
 }


 private static void AutoMove() {
  (HANDLER_MSG);
  (HANDLER_MSG, 2000);
 }

 private static void LongAutoMove() {
  if ((HANDLER_MSG)) {
   (HANDLER_LONG_MSG);
  }
  (HANDLER_LONG_MSG, 5000);
 }

 public static void StopMove() {
  if ((HANDLER_MSG)) {
   (HANDLER_MSG);
  }
 }


 //Is the target item after the last visible item private static boolean mShouldScroll;
 //Record the target item position private static int mToPosition;

 /**
   * Slide to the specified position
   */
 private static void smoothMoveToPosition(RecyclerView mRecyclerView, final int position) {
  if (position==0){
   (adAuditorAdapter);
  }
   (position);
   mToPosition = position;
   mShouldScroll = true;
 }

 @Override
 protected void onDestroy() {
  ();
  if (handler!=null){
   (null);
   handler= null;
  }
  if (adAuditorAdapter!=null) {
   adAuditorAdapter= null;
  }
 }
}

Basic implementation of automatic carousel effect

The demo here only writes about the approximate effect and there are many things that need to be optimized before it can be used in the project.

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.