Android Realizes Infinite Loop Scroll

  • 2021-12-11 09:04:28
  • OfStack

There are two ideas for traditional ViewPager to do circular scrolling.

One is to set count to Integer. MAX, and then take the module according to index
One is to add end at the beginning and start at the end. Simply put, there are two more. When sliding to these two, setCurrentItem will be directly to the real position.

When observing the circular scrolling of pdd, I thought of several ways to realize it.

1. Through Recyclerview, the idea of cycling is similar to that of ViewPager, and one more point should intercept all touch events. But in this way, you can't set the animation of entry and exit like pdd.
2. It should be possible to transform the form of VerticalViewpager, but it feels troublesome.
3. It is realized by custom. (Originally thought quite simple, under the implementation, the code is not much but some small details need to pay attention to.)

I chose to customize here just an demo, providing a way of thinking.

The core is that when the above item slides off the screen, drop it remove and then add it to the end of the custom ViewGroup.


public class LoopView extends ViewGroup {
  private static final String TAG = "LoopView";
  private float dis;
  private ObjectAnimator animator;
  private int currentIndex = 0;
  private Handler handler = new Handler(Looper.getMainLooper());
 
  public LoopView(Context context) {
    super(context);
    init();
  }
 
  public LoopView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
  }
 
  public LoopView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init();
  }
 
  void init() {
    View view1 = new View(getContext());
    view1.setTag("gray");
    view1.setBackgroundColor(Color.GRAY);
    LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, 200);
    addView(view1, layoutParams);
 
    View view2 = new View(getContext());
    view2.setTag("red");
    view2.setBackgroundColor(Color.RED);
    LayoutParams layoutParams1 = new LayoutParams(LayoutParams.MATCH_PARENT, 200);
    addView(view2, layoutParams1);
 
    View view3 = new View(getContext());
    view3.setTag("green");
    view3.setBackgroundColor(Color.GREEN);
    LayoutParams layoutParams2 = new LayoutParams(LayoutParams.MATCH_PARENT, 200);
    addView(view3, layoutParams2);
 
    animator = ObjectAnimator.ofFloat(this, "dis", 0, 1);
    animator.setDuration(2000);
    animator.addListener(new AnimatorListenerAdapter() {
      @Override
      public void onAnimationEnd(Animator animation) {
        currentIndex++;
        View first = getChildAt(0);
        removeView(first);
        addView(first);
        handler.postDelayed(new Runnable() {
          @Override
          public void run() {
            animator.clone().start();
          }
        }, 3000);
      }
    });
 
  }
 
  public void start() {
    animator.start();
  }
 
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    measureChildren(widthMeasureSpec, heightMeasureSpec);
    super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(200, MeasureSpec.EXACTLY));
  }
 
  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    int childCount = getChildCount();
    int top = currentIndex * getMeasuredHeight();
    for (int i = 0; i < childCount; i++) {
      View childAt = getChildAt(i);
      childAt.layout(l, top, r, top + childAt.getMeasuredHeight());
      top += childAt.getMeasuredHeight();
    }
  }
 
  public float getDis() {
    return dis;
  }
 
  public void setDis(float dis) {
    this.dis = dis;
    float disY = dis * getHeight();
    scrollTo(0, (int) (currentIndex * getHeight() + disY));
  }
}

Attention should be paid to the value of top when onLayout.


Related articles: