一. 默认不可以无限循环的状态

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
public class MainActivity extends AppCompatActivity {

//图片资源
private int[] resources = {R.drawable.pic_1,R.drawable.pic_2,R.drawable.pic_3};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//找到控件
ViewPager viewPager = findViewById(R.id.pager);

//预加载页面数量
viewPager.setOffscreenPageLimit(resources.length);

//设置适配器
viewPager.setAdapter(new PagerAdapter() {
@Override
public int getCount() {
return resources.length;
}

@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return object == view;
}

@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
//设置布局
View inflate = LayoutInflater.from(getApplicationContext()).inflate(R.layout.pager_layout, null);
ImageView imageView = inflate.findViewById(R.id.image_view);
imageView.setBackgroundResource(resources[position]);

//添加视图
container.addView(inflate);

return inflate;
}

@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
});

}
}

<!-- activity_main.xml --->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">

<androidx.viewpager.widget.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="100dp"
android:clipChildren="false"
/>

</LinearLayout>

<!-- pager_layout.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<ImageView
android:id="@+id/image_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>

</LinearLayout>

二. 伪无限循环

如果我们设置 ViewPager 的 页面数量很大,然后在 初始化页面 的函数中对 postion 取余。这就相当于我们创建了很多的一模一样的页面连续的排列在一起,这样向右滑动就会形成无限滑动的效果。虽然,理论上一直向右滑动还是会滑不动,但是一般情况下是不会这么无聊的。

这样还有一个问题,向左滑动还是不能滑动的。但是,如果我们将初始页面设置在中间位置,那么就可以达到无限滑动的效果了。

① 设置步骤:

  • 在 PagerAdapter的getCount 方法中设置 返回值 是一个很大的值。
  • 在 PagerAdapter的instantiateItem 方法中对 position 进行取余操作,一方面是防止数组越界,一方面是为了显示正确的内容。
  • 使用 setCurrentItem 方法,设置 显示的页面 大概处于中间的位置。注意该方法一定要在设置 适配器 之后再调用,否则会没有效果。

② 返回很大的页面会不会影响内存?

我个人觉得,ViewPager默认只会加载两个页面,滑动了之后,还是只加载两个页面,不会对内存造成太大的影响。

参考文章:Android ViewPager 无限轮播Integer.MAX_VALUE 争议(看源码)

③ 在 getCount 方法中 返回 Integer.MAX_VALUE 的问题?

使用Integer.MAX_VALUE会在setCurrentItem()的时候发生ANR,所以还是设置为一个比较大的数比较好。

④ 代码

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
60
61
62
63
/**
* 只改变了MainActivity
*/
public class MainActivity extends AppCompatActivity {

//图片资源
private int[] resources = {R.drawable.pic_1,R.drawable.pic_2,R.drawable.pic_3};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//找到控件
ViewPager viewPager = findViewById(R.id.pager);

//预加载页面数量
//viewPager.setOffscreenPageLimit(resources.length);

//设置适配器
viewPager.setAdapter(new PagerAdapter() {
@Override
public int getCount() {
if (resources.length == 1){
return 1;
}

return 500;
}

@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return object == view;
}

@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
//获得真正的position
int realPosition = position % resources.length;

//设置布局
View inflate = LayoutInflater.from(getApplicationContext()).inflate(R.layout.pager_layout, null);
ImageView imageView = inflate.findViewById(R.id.image_view);
imageView.setBackgroundResource(resources[realPosition]);

//添加视图
container.addView(inflate);

return inflate;
}

@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
});

//设置当前显示的页面
viewPager.setCurrentItem(240);

}
}

⑤ 运行效果

三. 真正的无限循环

假设有三条页面数据,分别是0、1、2。我们再创建一个新的页面数据,长度为真实数据的长度+2,在最前面插入最后一条页面数据2,在最后面插入第一条页面数据0,新的页面数据就变为2、0、1、2、0。设置新的页面数据为ViewPager的数据时,需要注意,设置默认开始的页面是第一个页面0。

当ViewPager滑动到最后一个页面0时就通过setCurrentItem(int item,boolean smoothScroll)方法将页面切换到第一个页面0处。同理当滑动到第一个页面2时,通过该方法将页面切换到第二个页面2,这样给我们的感觉就是无限循环。

① 操作步骤

  • 在 PagerAdapter的getCount 方法中设置 返回值 是想要显示的页面数量 + 2。
  • 在 PagerAdapter的instantiateItem 方法中对 position 进行相关判断更改,以显示正确的内容。
  • 使用 setCurrentItem 方法,设置 显示的页面 是 第二个 页面。
  • 通过在监听滚动方法的 onPageSelected 判断有没有滑动到左右添加的两个页面,如果滑到到了,需要使用 setCurrentItem 移动到该页面对应的真正页面处。
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/**
* 只改变了MainActivity
*/
public class MainActivity extends AppCompatActivity {

//图片资源
private int[] resources = {R.drawable.pic_1,R.drawable.pic_2,R.drawable.pic_3};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//找到控件
final ViewPager viewPager = findViewById(R.id.pager);

//预加载页面数量
//viewPager.setOffscreenPageLimit(resources.length);

//设置适配器
viewPager.setAdapter(new PagerAdapter() {
@Override
public int getCount() {
return resources.length + 2;
}

@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return object == view;
}

@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
//处理position
int realPosition;

//resources.length():3
//对应三个页面:0,1,2
//索引:0 1 2 3 4
//页面:2 0 1 2 0
if (position == 0) {
//索引为0的页面应该显示最后一个页面的内容
//资源数组中的最后一个资源
realPosition = resources.length - 1;
} else if (position == resources.length + 2 - 1) {
//索引为4的页面应该显示第一个页面的内容
//资源数组中的第一个资源
realPosition = 0;
} else {
realPosition = position - 1;
}

//设置布局
View inflate = LayoutInflater.from(getApplicationContext()).inflate(R.layout.pager_layout, null);
ImageView imageView = inflate.findViewById(R.id.image_view);
imageView.setBackgroundResource(resources[realPosition]);

//添加视图
container.addView(inflate);

return inflate;
}

@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
});

//设置显示的页面
viewPager.setCurrentItem(1);

//设置页面监听
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

}

//resources.length():3
//对应三个页面:0,1,2
//索引:0 1 2 3 4
//页面:2 0 1 2 0
@Override
public void onPageSelected(int position) {
if (position == 0){
//当滑动到第一个页面(索引为0)的时候,定位到真正的最后一个页面(索引为3,页面为2)
viewPager.setCurrentItem(resources.length,false);
}else if (position == resources.length+1){
//当定位到最后一个页面(索引为4)的时候,定位到真正的第一个页面(索引为1,页面为0)
viewPager.setCurrentItem(1,false);
}
}

@Override
public void onPageScrollStateChanged(int state) {

}
});
}
}

② 运行效果

③ 上面移动位置的操作是在 onPageSelected 方法中执行,该方法是已经滑动到该页面后才开始调用,我们可以看到有一点点的生硬。如果我们放在 onPageScrollStateChanged 方法中 滑动结束的时候移动位置就看不到生硬的效果了。

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/**
* 只改变了MainActivity
*/
public class MainActivity extends AppCompatActivity {

//图片资源
private int[] resources = {R.drawable.pic_1,R.drawable.pic_2,R.drawable.pic_3};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//找到控件
final ViewPager viewPager = findViewById(R.id.pager);

//预加载页面数量
//viewPager.setOffscreenPageLimit(resources.length);

//设置适配器
viewPager.setAdapter(new PagerAdapter() {
@Override
public int getCount() {
return resources.length + 2;
}

@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return object == view;
}

@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
//处理position
int realPosition;

//resources.length():3
//对应三个页面:0,1,2
//索引:0 1 2 3 4
//页面:2 0 1 2 0
if (position == 0) {
//索引为0的页面应该显示最后一个页面的内容
//资源数组中的最后一个资源
realPosition = resources.length - 1;
} else if (position == resources.length + 2 - 1) {
//索引为4的页面应该显示第一个页面的内容
//资源数组中的第一个资源
realPosition = 0;
} else {
realPosition = position - 1;
}

//设置布局
View inflate = LayoutInflater.from(getApplicationContext()).inflate(R.layout.pager_layout, null);
ImageView imageView = inflate.findViewById(R.id.image_view);
imageView.setBackgroundResource(resources[realPosition]);

//添加视图
container.addView(inflate);

return inflate;
}

@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
container.removeView((View) object);
}
});

//设置显示的页面
viewPager.setCurrentItem(1);

//设置页面监听
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
//记录当前的位置
int currentPosition = 0;

@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

}

@Override
public void onPageSelected(int position) {
currentPosition = position;
}

@Override
public void onPageScrollStateChanged(int state) {
//判断滑动是否结束
if (state == ViewPager.SCROLL_STATE_IDLE){
//resources.length():3
//对应三个页面:0,1,2
//索引:0 1 2 3 4
//页面:2 0 1 2 0
if (currentPosition == 0){
//当滑动到第一个页面(索引为0)的时候,定位到真正的最后一个页面(索引为3,页面为2)
viewPager.setCurrentItem(resources.length,false);
}else if (currentPosition == resources.length+1){
//当定位到最后一个页面(索引为4)的时候,定位到真正的第一个页面(索引为1,页面为0)
viewPager.setCurrentItem(1,false);
}
}
}
});
}
}

参考文章

ViewPager两种方式实现无限轮播

ViewPager实现无限循环的2种方法