ViewPager实现循环轮播图
- 引言:以前利用自定义控件写过一次轮播图,十分的繁琐,既要监听用户手势的滑动操作,又需要根据点坐标处理图片的平移事件,还无法实现循环轮播的效果。今天通过ViewPager发现实现轮播效果十分的简单,接下来我们看看如何利用ViewPager来实现轮播图的效果。
1.利用ViewPager实现滑动切换效果
主布局中添加控件ViewPager:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<RelativeLayout
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_centerInParent="true"
>
//ViewPager控件
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_centerInParent="true"
>
</android.support.v4.view.ViewPager>
</RelativeLayout>
主Activity中为ViewPager设置数据填充,此次为了方便起见,准备了四个Fragment作为图片的数据视图源:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19//数据自己添加就行
private List<Fragment> fragmentList = new ArrayList<>();
class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
}
public Fragment getItem(int position) {
return fragmentList.get(position);
}
public int getCount() {
return fragmentList.size();
}
}
接下来,在Fragment的视图中添加需要轮播的图片,此处为了区分直接用背景色替代了。
1 | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
接着在Activity中,将Adapter赋值给ViewPager,实现滑动切换效果。
myAdapter = new MyAdapter(getSupportFragmentManager());
viewPager.setAdapter(myAdapter);
效果如下:

2.ViewPager实现无限循环滑动效果
从上述ViewPager的切换中我们可以看到,向下到达第四张图时,将不可往下继续滑动,同理,向上滑动到第一张时,也无法继续上滑。而我们要实现的效果便是,当滑动到第四张时,接着可以向下滑动回到第一张,向上滑动到第一张时,接着上滑回到末尾的第四张。怎么样实现呢?
1.思路分析
我们这么来想,此时有四张图片处于我们的总体里,且依次从左到右排列。当我们滑到尾部的时候,如果接着还有一张图续在尾部后面,与第一张图相同,这样当我们接着向右滑动时,就会切回到首页的界面(但此时其实是我们在末尾新加的那张)。与此同时,我们将ViewPager的position替换为1,将ViewPager的Item替换为第一张图的Item项,这样当我们继续右滑,就可以实现轮播效果。同理,放一张图在最左边作为ViewPager的第一个Item项,与第四张图相同,切到时替换为第四张图,即可完成整体轮播效果。
2.画图分析

3.代码实现
首先,我们需要定义出六个页面,同样在此处用Fragment实现:
/**
* 数据填充
*/
private void initData() {
if(fragmentList==null){
fragmentList = new ArrayList<>();
}
//添加第四个
fragmentList.add(new Fragment4());
//顺序添加
fragmentList.add(new Fragment1());
fragmentList.add(new Fragment2());
fragmentList.add(new Fragment3());
fragmentList.add(new Fragment4());
//添加第一个
fragmentList.add(new Fragment1());
//设置起始的变更位置,由于采用先等待后变更模式,而起始就是1位置,所以索引从2开始
position = 2;
}
接着,我们来看看在ViewPager的监听逻辑实现:
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
//此方法用于监听滑动过程逻辑,不用处理
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
//此方法中变更当前页面的位置
public void onPageSelected(int position1) {
position = position1;
if (position == fragmentList.size()-1) {
// 如果滑到了最后一个,即和第一张相同的图时,设置当前值为1
position = FIRST_PAGE;
} else if (position == 0) {
// 如果索引值为0,即滑到了和第四张图相同的时候,就设置索引值为倒数第二个
position = fragmentList.size()-2;
} else {
//否则正常显示
position = position1;
}
}
//此方法用来变更ViewPager显示的Item
public void onPageScrollStateChanged(int state) {
//滑动状态改变的方法
//state :dragging 拖拽 idle 静止 settling 惯性过程
//如果是静止状态,将当前页进行替换
if(state==ViewPager.SCROLL_STATE_IDLE){
//每次滑动时会走这个方法
viewPager.setCurrentItem(position, false);
}
}
});
分析一波:我们在起始时就设置viewPager.setCurrentItem(1,false)
,也就是显示我们的第一张图。然后,当我们向右滑动时,先走onPageSelected
,位置未变更,再走onPageScrollStateChanged
,此时position整体一致,因此视图保持正常切换。当滑到最后一个时,我们将位置设为1回到第一个图片的位置,再将ViewPager变更到图片1,不过由于末尾图与1图完全相同,因此感官上是看不出来的,就这样我们完成了后台的变更,实现了无限循环的轮播,向前也是一样的逻辑。
4.效果演示:
此时,我们的效果就已经实现了:

3.ViewPager轮播图中添加小圆点并完成联动
1.实现思路
我们在布局文件中,用一个横向的LinearLayout存储四个自定义Shape小圆点,然后采用相对布局,将ViewPager同LinearLayout放在一个RelativeLayout下,从而实现小圆点的添加动作。(注:由于小圆点的点击范围过小,影响用户操作体验,因此用定宽高的LinearLayout将它包裹起来,完成联动)
2.代码实现
主视图的布局代码如下:
//定高容器RelativeLayout
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_centerInParent="true"
>
//轮播图ViewPager
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_centerInParent="true"
>
</android.support.v4.view.ViewPager>
//横向小圆点容器
<LinearLayout
android:id="@+id/circle_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_alignParentBottom="true"
android:layout_marginBottom="10dp"
android:gravity="center"
>
//依次用定宽高的LinearLayout包裹
<LinearLayout
android:id="@+id/pic0_lay"
android:layout_width="30dp"
android:layout_height="30dp"
android:gravity="center"
>
<ImageView
android:id="@+id/pic_0"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/circle_check_style"
/>
</LinearLayout>
<LinearLayout
android:id="@+id/pic1_lay"
android:layout_width="30dp"
android:layout_height="30dp"
android:gravity="center"
>
<ImageView
android:id="@+id/pic_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/circle_uncheck_style"
/>
</LinearLayout>
<LinearLayout
android:id="@+id/pic2_lay"
android:layout_width="30dp"
android:layout_height="30dp"
android:gravity="center"
>
<ImageView
android:id="@+id/pic_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/circle_uncheck_style"
/>
</LinearLayout>
<LinearLayout
android:id="@+id/pic3_lay"
android:layout_width="30dp"
android:layout_height="30dp"
android:gravity="center"
>
<ImageView
android:id="@+id/pic_3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/circle_uncheck_style"
/>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
自定义小圆点的布局:
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval"
android:useLevel="false">
<solid android:color="@color/green"/>
//未选中设置为黑色的布局即可
<solid android:color="@color/gray"/>
<size android:width="15dp"
android:height="15dp"/>
</shape>
主Activity的逻辑处理:
//翻页时小圆点的联动
public void onPageSelected(int position1) {
position = position1;
if (position == fragmentList.size()-1) {
// 设置当前值为1
position = FIRST_PAGE;
} else if (position == 0) {
// 如果索引值为0了,就设置索引值为倒数第二个
position = fragmentList.size()-2;
} else {
position = position1;
}
//翻页后需要去改变对应位置的图片
switch (position){
case 1:
pic_0.setBackgroundResource(R.drawable.circle_check_style);
pic_1.setBackgroundResource(R.drawable.circle_uncheck_style);
pic_2.setBackgroundResource(R.drawable.circle_uncheck_style);
pic_3.setBackgroundResource(R.drawable.circle_uncheck_style);
break;
case 2:
pic_1.setBackgroundResource(R.drawable.circle_check_style);
pic_0.setBackgroundResource(R.drawable.circle_uncheck_style);
pic_2.setBackgroundResource(R.drawable.circle_uncheck_style);
pic_3.setBackgroundResource(R.drawable.circle_uncheck_style);
break;
case 3:
pic_2.setBackgroundResource(R.drawable.circle_check_style);
pic_0.setBackgroundResource(R.drawable.circle_uncheck_style);
pic_1.setBackgroundResource(R.drawable.circle_uncheck_style);
pic_3.setBackgroundResource(R.drawable.circle_uncheck_style);
break;
case 4:
pic_3.setBackgroundResource(R.drawable.circle_check_style);
pic_0.setBackgroundResource(R.drawable.circle_uncheck_style);
pic_1.setBackgroundResource(R.drawable.circle_uncheck_style);
pic_2.setBackgroundResource(R.drawable.circle_uncheck_style);
break;
default:
break;
}
}
//点击小圆点时切换ViewPager
pic_0 = findViewById(R.id.pic_0);
pic0_lay = findViewById(R.id.pic0_lay);
pic0_lay.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
//此处对第二个参数SmoothScroll设置为false,表示禁止平滑移动效果,否则在4切1时会显示闪屏。
viewPager.setCurrentItem(1,false);
}
});
pic_1 = findViewById(R.id.pic_1);
pic1_lay = findViewById(R.id.pic1_lay);
pic1_lay.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
viewPager.setCurrentItem(2,false);
}
});
pic_2 = findViewById(R.id.pic_2);
pic2_lay = findViewById(R.id.pic2_lay);
pic2_lay.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
viewPager.setCurrentItem(3,false);
}
});
pic_3 = findViewById(R.id.pic_3);
pic3_lay = findViewById(R.id.pic3_lay);
pic3_lay.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
viewPager.setCurrentItem(4,false);
}
});
上述的代码逻辑比较简单,我们定义好主布局,将四个小圆点图片放在ViewPager中,然后在ViewPager的滑动监听中去同步改变小圆点的选中状态,保持与之匹配。而对于小圆点的点击事件来说,每当我们点击时,就切换到对应的ViewPager的Item项即可。
3.效果演示

4.ViewPager无限自动轮播的最终实现
到了这一步,成功已经在向我们招手了,接下来我们就写一个定时器来实现一个自动轮播的效果即可。代码如下:
//添加一个定时器任务
timerTask = new TimerTask() {
public void run() {
while (flag) {
try {
Thread.sleep(2000);
//向主线程每隔2s发送一次消息
Message msg = new Message();
msg.what = MSG_ONE;
handler.sendMessage(msg);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
};
//实现主线程的轮播
private Handler handler = new Handler(){
public void handleMessage(Message msg) {
switch (msg.what){
case MSG_ONE:
//定时变更子项显示
if(position<5){
//变换
viewPager.setCurrentItem(position,false);
//+1操作
position++;
}else{
position = 1;
//变换
viewPager.setCurrentItem(position,false);
position++;
}
break;
default:
break;
}
}
};
//设置为1位置先显示(初始position为2)
viewPager.setCurrentItem(1);
//启动轮播
timer.schedule(timerTask,0);
在这里,我们设置了定时器先等待2s在变更的模式,而首次已经加载了页面1,因此position应该从2开始,到显示4结束。大于4时就重置为1循环去显示。这样,我们就实现了自动轮播的功能。
好了,大功告成,让我们看看最终的实现效果吧:
