您的位置:首页 > 移动开发 > Android开发

Android轮播图原理思路分析+实现方案

2017-10-20 21:23 423 查看
来自:http://blog.csdn.net/wubihang/article/details/52512597

ListView的headerView设置为轮播图之后结合上/下拉刷新/加载的模式成为现在大多数APP的一个必须具备的功能,对于许多初学者来说想要实现轮播图这样一个集线程睡眠、自动处理、替换过程中刷新UI界面的组合功能非常困难,没有思路,感觉无从下手,去搜索各种实现方案,发现目前充斥着大量的类似于使用弃用组件Gallery这样的例子。 

  本篇博客将通过分析轮播图的各个注意事项及实现思路来实现一个简易的轮播图。 

  首先看下一下效果图; 

需求: 1.轮播,如轮播四张图:到第四张之后不会回到第一张而是继续向下一张切换; 

2.初始状态即可向前滑动 

3.轮播过程中会有状态切换以白点和黑点的方式呈现;


 



ps:这里的魂斗罗见到有没有热血沸腾,情怀所在;

实现步骤: 

一、轮播图部分采用的是ViewPager显示Layout布局,替换图片的方式 

1.为了让图片滑到最后一张不是以翻页的形式回到首页,getCount方法返回的是int最大值,这样就会有很多页; 

2.由于getCount方法返回整型最大值,所以使用的position采用取余的计算方法,虽然position会一直增加,但是显示的数据始终为固定数量;
package com.wu.rotateimgdemo;

import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.List;

/**
* Created by wubihang on 16/9/12.
*/
public class RotateVpAdapter extends PagerAdapter{

private List<RotateBean> datas;
private Context context;
private LayoutInflater inflater;

public RotateVpAdapter(Context context) {
this.context = context;
inflater = LayoutInflater.from(context);
}

public void setDatas(List<RotateBean> datas) {
this.datas = datas;
notifyDataSetChanged();
}

@Override
public int getCount() {
// 为了让ViewPager到最后一页不会像翻书一样回到第一页
// 设置页数为int最大值,这样向下滑动永远都是下一页
return datas == null ? 0 : Integer.MAX_VALUE;
}

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

@Override
public Object instantiateItem(ViewGroup container, int position) {
// position是int最大值所以这里可能是几百甚至上千,因此取余避免数组越界
int newPosition = position % datas.size();
View convertView = inflater.inflate(R.layout.item_vp, container, false);
ImageView imageView = (ImageView) convertView.findViewById(R.id.item_iv);
TextView textView = (TextView) convertView.findViewById(R.id.item_tv);
textView.setText("文字内容" + newPosition);
imageView.setImageResource(datas.get(newPosition).getImgId());

container.addView(convertView);
return convertView;
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {

}
}
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

二、设置轮播的线程 

1.轮播采用Handler对象的postDelayed方法,发送一个延时的任务; 

任务的内容是每隔固定时间让ViewPager当前页自增1,如果处于轮播状态再次延时执行该任务,这样只要轮播状态isRotate为true,就像死循环一样一直执行此轮播任务;
/**
* 开始轮播
*/
private void startRotate() {
rotateRunnable = new Runnable() {
@Override
public void run() {
int nowIndex = viewPager.getCurrentItem();
viewPager.setCurrentItem(++nowIndex);
if (isRotate) {
handler.postDelayed(rotateRunnable, TIME);
}
}
};
handler.postDelayed(rotateRunnable, TIME);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

在onPause和onResume里对于轮播状态进行了处理
@Override
protected void onResume() {
super.onResume();
isRotate = true;
}

@Override
protected void onPause() {
super.onPause();
isRotate = false;
}
1
2
3
4
5
6
7
8
9
10
11
12

三、设置轮播状态切换的小圆点 

1.设置的方式是动态的,有多少张轮播图就循环加入多少个小圆点; 

2.将position==0时的小圆点设置为黑色,默认选中第一张,剩下的设置为白色; 

这里的mipmap是我自己做的两张黑色的和白色的小圆点图片;
/**
* 添加轮播切换小点
*/
private void addPoints() {
// 有多少张图加载多少个小点
for (int i = 0; i < datas.size(); i++) {
ImageView pointIv = new ImageView(this);
pointIv.setPadding(5,5,5,5);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(20,20);
pointIv.setLayoutParams(params);

// 设置第0页小点的为灰色
if (i == 0) {
pointIv.setImageResource(R.mipmap.point_grey);
} else {
pointIv.setImageResource(R.mipmap.point_white);
}
pointLl.addView(pointIv);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

四、设置ViewPager页切换时小圆点的颜色跟着改变 

1.当页切换时,获取所有的小圆点,将他们都设置为白色; 

2.获取当前页的小圆点,将它设置为黑色;
private void changePoints() {
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

}

@Override
public void onPageSelected(int position) {
if (isRotate) {
// 把所有小点设置为白色
for (int i = 0; i < datas.size(); i++) {
ImageView pointIv = (ImageView) pointLl.getChildAt(i);
pointIv.setImageResource(R.mipmap.point_white);
}
// 设置当前位置小点为灰色
ImageView iv = (ImageView) pointLl.getChildAt(position % datas.size());
iv.setImageResource(R.mipmap.point_grey);
}
}

@Override
public void onPageScrollStateChanged(int state) {

}
});
}
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

这样简单的完成了一个ViewPager实现的轮播线程;

分析及实现过程完毕,下面贴上全部代码!

1.布局文件

activity_main.xml 

分别是装载轮播图的ViewPager和装载小圆点的容器;
<?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="match_parent"
android:orientation="vertical">

<FrameLayout
android:layout_width="match_parent"
android:layout_height="300dp">

<android.support.v4.view.ViewPager
android:id="@+id/rotate_vp"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

<LinearLayout
android:id="@+id/rotate_point_container"
android:layout_width="match_parent"
android:layout_height="20dp"
android:layout_gravity="bottom|center"
android:layout_marginBottom="10dp"
android:orientation="horizontal"/>
</FrameLayout>

</LinearLayout>
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

ViewPager的页布局 

item_vp.xml 

分别是显示轮播图的ImageView和显示上面文字的TextView;
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<ImageView
android:id="@+id/item_iv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="fitXY" />

<TextView
android:id="@+id/item_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_marginBottom="25dp"
android:textSize="16sp"
android:textColor="#f03"
android:layout_marginRight="10dp"
android:textStyle="bold" />

</FrameLayout>
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

2.数据实体类 

这里运用的是本地图片,因此使用的是imgId; 

如果网络图片也可以加入,将图片源更改即可;
package com.wu.rotateimgdemo;

import java.io.Serializable;

/**
* Created by wubihang on 16/9/12.
*/
public class RotateBean implements Serializable{

private int imgId;
private String imgUrl;

public RotateBean() {
}

public RotateBean(String imgUrl) {
this.imgUrl = imgUrl;
}

public RotateBean(int imgId) {
this.imgId = imgId;
}

public RotateBean(int imgId, String imgUrl) {
this.imgId = imgId;
this.imgUrl = imgUrl;
}

public int getImgId() {
return imgId;
}

public void setImgId(int imgId) {
this.imgId = imgId;
}

public String getImgUrl() {
return imgUrl;
}

public void setImgUrl(String imgUrl) {
this.imgUrl = imgUrl;
}
}
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

3.适配器 

适配器部分需要注意: 

1)getCount的返回值 

2)使用时position取余的运用(防止数组越界)
package com.wu.rotateimgdemo;

import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.List;

/**
* Created by wubihang on 16/9/12.
*/
public class RotateVpAdapter extends PagerAdapter{

private List<RotateBean> datas;
private Context context;
private LayoutInflater inflater;

public RotateVpAdapter(List<RotateBean> datas, Context context) {
this.datas = datas;
this.context = context;
inflater = LayoutInflater.from(context);
}

public RotateVpAdapter(Context context) {
this.context = context;
inflater = LayoutInflater.from(context);
}

public void setDatas(List<RotateBean> datas) {
this.datas = datas;
notifyDataSetChanged();
}

@Override
public int getCount() {
// 为了让ViewPager到最后一页不会像翻书一样回到第一页
// 设置页数为int最大值,这样向下滑动永远都是下一页
return datas == null ? 0 : Integer.MAX_VALUE;
}

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

@Override
public Object instantiateItem(ViewGroup container, int position) {
// position是int最大值所以这里可能是几百甚至上千,因此取余避免数组越界
int newPosition = position % datas.size();
View convertView = inflater.inflate(R.layout.item_vp, container, false);
ImageView imageView = (ImageView) convertView.findViewById(R.id.item_iv);
TextView textView = (TextView) convertView.findViewById(R.id.item_tv);
textView.setText("文字内容" + newPosition);
imageView.setImageResource(datas.get(newPosition).getImgId());

container.addView(convertView);
return convertView;
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {

}
}
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

4.Activity部分,此部分代码比较多,请阅读时仔细梳理;
package com.wu.rotateimgdemo;

import android.os.Handler;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.LinearLayout;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

private static final int TIME = 3000;

private ViewPager viewPager;
private LinearLayout pointLl;// 轮播状态改变的小圆点容器
private List<RotateBean> datas;
private RotateVpAdapter vpAdapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager = (ViewPager) findViewById(R.id.rotate_vp);
pointLl = (LinearLayout) findViewById(R.id.rotate_point_container);

buildDatas();// 构造数据
vpAdapter = new RotateVpAdapter(datas, this);
viewPager.setAdapter(vpAdapter);
// ViewPager的页数为int最大值,设置当前页多一些,可以上来就向前滑动
// 为了保证第一页始终为数据的第0条 取余要为0,因此设置数据集合大小的倍数
viewPager.setCurrentItem(datas.size() * 100);

// 开始轮播
handler = new Handler();
startRotate();
// 添加轮播小点
addPoints();
// 随着轮播改变小点
changePoints();
}

private void changePoints() { viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { if (isRotate) { // 把所有小点设置为白色 for (int i = 0; i < datas.size(); i++) { ImageView pointIv = (ImageView) pointLl.getChildAt(i); pointIv.setImageResource(R.mipmap.point_white); } // 设置当前位置小点为灰色 ImageView iv = (ImageView) pointLl.getChildAt(position % datas.size()); iv.setImageResource(R.mipmap.point_grey); } } @Override public void onPageScrollStateChanged(int state) { } }); }

/**
* 添加轮播切换小点
*/
private void addPoints() {
// 有多少张图加载多少个小点
for (int i = 0; i < datas.size(); i++) {
ImageView pointIv = new ImageView(this);
pointIv.setPadding(5,5,5,5);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(20,20);
pointIv.setLayoutParams(params);

// 设置第0页小点的为灰色
if (i == 0) {
pointIv.setImageResource(R.mipmap.point_grey);
} else {
pointIv.setImageResource(R.mipmap.point_white);
}
pointLl.addView(pointIv);
}
}

private Handler handler;
private boolean isRotate = false;
private Runnable rotateRunnable;

/** * 开始轮播 */ private void startRotate() { rotateRunnable = new Runnable() { @Override public void run() { int nowIndex = viewPager.getCurrentItem(); viewPager.setCurrentItem(++nowIndex); if (isRotate) { handler.postDelayed(rotateRunnable, TIME); } } }; handler.postDelayed(rotateRunnable, TIME); }
@Override protected void onResume() { super.onResume(); isRotate = true; } @Override protected void onPause() { super.onPause(); isRotate = false; }
private void buildDatas() {
datas = new ArrayList<>();
datas.add(new RotateBean(R.mipmap.big_image));
datas.add(new RotateBean(R.mipmap.btn_normal));
datas.add(new RotateBean(R.mipmap.icon_baoman));
datas.add(new RotateBean(R.mipmap.btn_pressed));
}
}
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136

本篇博客内容纯属原创,各位随便使用随便点评; 

祝大家写代码无bug,实现功能思路如飞;

源码下载请见下方链接

http://download.csdn.net/detail/wubihang/9628348
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: