Note to self: While wheels are great, use them only if: If you don’t go to encapsulate some complex functions, they will be written in the basic method, otherwise it is a good wheels it is someone else’s, when their project in different place and wheels, it can only be helpless or change the somebody else’s source code, can understand the wheel package, of course, thought oneself knowledge and the ability to easily change the source code that is the best.

1. Implementation idea

There are two options:

(1) Use the getCount() method to return integer.max_value.

(2) Insert the last data at the beginning of the list, and insert the first data at the end of the list, creating the illusion of a loop

2. Implementation

2.1 Scheme 1: getCount() returns integer.max_value

2.1.1 ViewPager Infinite Loop

The getCount method in the Adapter of the ViewPager returns a large number integer. MAX_VALUE that can theoretically slide indefinitely. When the period of a real list is displayed, the data is displayed from position 0 of the real list, creating the illusion of an infinite loop. Because ViewPager first page can’t scroll left circulation, so we should pass mViewPager setCurrentItem (Integer. MAX_VALUE / 2) set up the selected location, so in the beginning can slide to the left, but because of the need to display the first page so that value % data number = = 0. Because an ANR occurs when setCurrentItem () is set to integer.max_value, it is better to use a custom larger number, instead of 500

// The page is currently selected
private int currentPosition;
// Number of data items
private List<Integer> itemList;

public static final int mLooperCount = 500;


// Sets the currently selected item
currentPosition = getStartItem();
viewPager1.setCurrentItem(currentPosition1);

private int getStartItem(a) {
    if(getRealCount() == 0) {return 0;
        }
    // We set the current selected position to integer.max_value / 2 so that we can start sliding to the left
    // But make sure that the remainder of this value and getRealPosition is 0, since it is displayed from the first page
    int currentItem =BannerAdapter.mLooperCount / 2;
    if(currentItem % getRealCount()  ==0) {return currentItem;
    }
    // until we find the position starting from 0
    while(currentItem % getRealCount() ! =0){
        currentItem++;
    }
    return currentItem;
}

// Get the number of data items
private int getRealCount(a) {
    return itemList == null ? 0 : itemList.size();
}
Copy the code

The Adapter simply returns getCount() to integer.max_value (we’ll change it to a specific value here), and the rest is normal.

@Override
public int getCount(a) {
    return getRealCount() * mLooperCount;
}
Copy the code

2.1.2 Adding the multicast function

Use Handler’s postDelayed method

private Handler mHandler = new Handler();

@Override
protected void onResume(a) {
    super.onResume();
    // Start the rotation
    mHandler.postDelayed(mLoopRunnable, mDelayedTime);
}

@Override
protected void onPause(a) {
    super.onPause();
    // Stop the rotation
    mHandler.removeCallbacks(mLoopRunnable);    
}
Copy the code
private final Runnable mLoopRunnable = new Runnable() {
    @Override
    public void run(a) {
        if (mIsAutoPlay) {

            / / a
            currentPosition1 = viewPager1.getCurrentItem();
            currentPosition1++;
            if (currentPosition1 == bannerAdapter.getCount() - 1) {		// Slide to the last one
                currentPosition1 = 0;								  // Switch to 0
                viewPager1.setCurrentItem(currentPosition1, false);
                mHandler.postDelayed(this, mDelayedTime);
            } else {
                viewPager1.setCurrentItem(currentPosition1);
                mHandler.postDelayed(this, mDelayedTime); }}else {
            mHandler.postDelayed(this, mDelayedTime); }}};Copy the code

2.1.3 Controversy over using INTEger. MAX_VALUE

Android ViewPager unicast integer. MAX_VALUE Android ViewPager Unicast Integer.MAX_VALUE Android ViewPager Unicast Integer.MAX_VALUE

2.1.4 note

Using integer.max_value will generate ANR on setCurrentItem (), so it is better to set it to a large number. In the code, I have changed to return getRealCount()*500. If there is a return integer.max_value in the article, I have not corrected it, please change it yourself.

2.2 Solution 2: Add two data items first and last

2.2.1 ViewPager Infinite Loop

Create a new list with the length of the real list +2. Insert the last item (3) at the front and the first item (1) at the back. The new list becomes 3, 1, 2, 3, 1. SetCurrentItem (int Item, Boolean smoothScroll) switches the page to position 3 when the viewPager slides to position 0, and to position 1 when the viewPager slides to position 4. This gives the feeling of an infinite loop.

private int currentPosition2;
private void initData2(a) {

    itemList2 = new ArrayList<>();
    itemList2.add(R.drawable.ic_pic4);
    itemList2.add(R.drawable.ic_pic1);
    itemList2.add(R.drawable.ic_pic2);
    itemList2.add(R.drawable.ic_pic3);
    itemList2.add(R.drawable.ic_pic4);
    itemList2.add(R.drawable.ic_pic1);

    bannerAdapter2 = new BannerAdapter2(itemList2);
    viewPager2.setAdapter(bannerAdapter2);
    currentPosition2 = 1;

    viewPager2.setCurrentItem(currentPosition2);

    viewPager2.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int i, float v, int i1) {}@Override
        public void onPageSelected(int i) {
            currentPosition2 = i;
        }

        @Override
        public void onPageScrollStateChanged(int state) {
            // Verify that the current slide is over
            if (state == ViewPager.SCROLL_STATE_IDLE) {
                if (currentPosition2 == 0) {
                    viewPager2.setCurrentItem(itemList2.size() - 2.false);// Toggle without animation
                } else if (currentPosition2 == itemList2.size() - 1) {
                    viewPager2.setCurrentItem(1.false);// Toggle without animation}}}}); }Copy the code

2.2.2 Adding the multicast function

private Handler mHandler2 = new Handler();
@Override
protected void onResume(a) {
    super.onResume();
    // Start the rotation
    mHandler2.postDelayed(mLoopRunnable2, mDelayedTime);
}

@Override
protected void onPause(a) {
    super.onPause();
    // Stop the rotation
    mHandler2.removeCallbacks(mLoopRunnable2);
}
Copy the code
private final Runnable mLoopRunnable2 = new Runnable() {
    @Override
    public void run(a) {
        if (mIsAutoPlay) {
                // Add two more data points
                currentPosition2 = viewPager2.getCurrentItem();
                currentPosition2++;
                // There is no need to determine whether the last page has been reached in order to loop round. This is already done for us in the listener
                viewPager2.setCurrentItem(currentPosition2);
                mHandler2.postDelayed(this, mDelayedTime);

            } else {
                mHandler2.postDelayed(this, mDelayedTime); }}};Copy the code

The difference with plan 1 is that when you slide to the last one, switch to the page with subscript 1, and when you slide to the page with subscript 0, switch to the last one

2.3 compare

Still, Fantessy wrote in the article: In the second solution, when the animation is switched to position 4, we use setCurrentItem(int item, Boolean smoothScroll) to switch to position 1 to create an infinite loop effect, but in order not to be detected, The second parameter, smoothScroll, is set to false, so there is no toggle animation, resulting in stiffness, so don’t use this.

I didn’t want to implement plan 2 (I thought I could do it one way), but my curiosity made me want to see how blunt it was, but I didn’t find the blunt effect. Because we are listening for the end of animation in onPageScrollStateChanged(), so when we slide to fourth, we are actually sliding to fifth, which is the image we added to the tail, which is animated. It is not the 0 subscript of the itemList, and in this listener, when the last item is actually checked, we have already secretly switched it to 1 by setCurrentItem () without drawing it. So when the handler slides again through currentem ++, it slides to an image with subscript 2, and it also has an effect, so there’s no hard effect. Here are four images that you can take a closer look at:

3. Summary

That’s all there is to the basic method of implementing ViewPager’s infinite rotation. See Github for the code.

In fact, we commonly use banners and indicators (the small dot at the bottom). I actually use the self-defined Indicator by Iran Fantesi, because it is really good and easy to encapsulate. Although it is the same, I still want to record the encapsulation process again in the next article. Impress yourself. See you in the next article.

4. Refer to the article

Android ViewPager Unicast integer. MAX_VALUE

ViewPager series of BannerView ads modeled on Meizu apps

Android uses ViewPager to implement infinite round casting bug Cause and solution