自定义完美的ViewPager 真正无限循环的轮播图
网上80%的思路关于Android轮播图无限循环都是不正确的,不是真正意义上的无限循环,
其思路大多是将ViewPager的getCount方法返回值设置为Integer.MAX_VALUE,
然后呢将ViewPager的当前展示页设置为第1000页或者是10000页,这样用户一般情况下是滑不到边界的
例如有5张图片的轮播图,item的编号为(0,1,2,3,4)当前页的页号如果是5, 这时候就将编号设置为0,即
actPosition %= datas.size();这个公式就是这么来的
这种思路实现的无限轮播虽然可以实现需求 但是却不是真正意义的无限轮播,结合了iOS 、HTML5的无限轮播图实现思路,总结如下:
例如:有五张轮播图 item的编号为(0,1,2,3,4) 要想实现 无限循环 我们在这五张的头部和尾部各加一张即(5+2)张,item编号为(0,1,2,3,4,5,6)其中编号为0,6的两张不做展示只是为了做循环轮播的铺垫,
1、当我们从编号为5 右滑的时候到了编号6 这时候就将当前页面设置为1
2、当我们从编号为1左滑的时候到了编号0 这时候就将当前页面设置为5
第一种情况:

第二种情况:

这么做之后就可以实现无限轮播 怎么保证从编号6跳转编号1的时候不出现页面停顿 突然跳到下一页的现象呢?
public Object instantiateItem (ViewGroup container, int position)
在指定的位置创建页面;适配器负责添加view到这个容器中,然而它只保证在finishUpdate(ViewGroup)返回时才完成。
public void destroyItem (ViewGroup container, int position, Object object)
删除指定位置的页面;适配器负责从view容器中删除view,然而它只保证在finishUpdate(ViewGroup)返回时才完成。
所以说 重点就在于finishUpdate(ViewGroup)这个方法 其实无论是创建view添加到容器中 还是 销毁view 都是在此方法结束之后执行的
换句话说 就是 我在这个方法里让页面完成从 编号5跳转到编号1 或者从编号1跳转到编号5,此方法完成时 视图还未完成创建或者 销毁 这样也就不会出现 页面停顿 突然跳到下一页的现象
@Override
public void finishUpdate(ViewGroup container) {
int position = mBanner.getCurrentItem();
if (position == 0) {
position = datas.size();
viewPager.setCurrentItem(position,false);
} else if (position == (datas.size()+2) - 1) {
position = 1;
viewPager.setCurrentItem(position,false);
}
}
如此 完美解决 无限轮播
一般情况下 轮播图要求自动轮播 没关系 我们可以利用Handler去开启线程 让其每隔一定时间去轮播
这里的重点是 如果我们定义每隔3秒轮播一张,当我们用手滑动了之后这时候要重新计时,防止用户刚滑动完 程序立马又滑动一次,
handler.removeCallbacksAndMessages(null);
此方法意思是移除所有的定消息 handler.sendEmptyMessageDelayed(0,3000);
此方法是三秒之后发送一个 message.what=0的消息
完整的代码
我定义了一个View名字叫TopView 实现每隔3秒轮播,并且下方有小圆点(当前显示的编号小圆点会变成白色,其他小圆点显示为灰色)的轮播图
基本思路是 在FrameLayout里边底层是一个Viewpager 上层是一个LinearLayout 里边有对应张数的小圆点 小圆点是根据数据的个数动态生成的
类里边还定义了一个 点击监听接口 ,点击对应的图片可以回调给调用者
public class TopView extends FrameLayout implements ViewPager.OnPageChangeListener {
private ViewPager vPager;
private List<ImageView> imgViews;
private List<String> datas;// 数据源
private LinearLayout navLayout;
private LinearLayout.LayoutParams layoutParams;//线性布局中子控件使用的布局参数,作用设置子控件大小,外边距
private Context mContext;
private DownLoadImage imageLoader;
private int currentPosition=0;
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
currentPosition = (++currentPosition) % imgViews.size();
vPager.setCurrentItem(currentPosition,false);
//处理完之前的消息之后再次发送一个3秒之后的消息 如此 可实现每隔三秒轮播
if(handler!=null){
handler.sendEmptyMessageDelayed(0,3000);
}
}
};
public TopView(Context context) {
this(context,null);
}
public TopView(Context context, AttributeSet attrs) {
this(context,attrs,0);
}
public TopView(Context context,AttributeSet attrs,int defStyle) {
super(context,attrs,defStyle);
mContext=context;
// 第二个参数this: 布局资源中根标签内声明的布局参数参考的父控件对象
// 第三个参数true: 代表是将第一个参数中声明的子控件归属到第二个参数对象中,false不归属
LayoutInflater.from(context).inflate(R.layout.topview, this, true);
initView();
}
private void initView() {
// 查找相关的UI控件
vPager = (ViewPager) findViewById(R.id.viewPager);
navLayout = (LinearLayout) findViewById(R.id.navLayout);
}
public void setData(List<String> datas) {
this.datas = datas;
createViews();
}
private void createViews() {
// 根据数据源创建ViewPager中显示的UI
imgViews = new ArrayList<ImageView>();//加入数据源
layoutParams =new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);
// layoutParams.leftMargin=20;
layoutParams.rightMargin=10;//px
layoutParams.width=20;
layoutParams.height=20;
ImageView imgView = null;
ImageView navImg = null;
for (int i=0;i<datas.size();i++)
{
imgView = new ImageView(getContext());
imgView.setScaleType(ImageView.ScaleType.FIT_XY);
imgView.setTag(datas.get(i));
imgViews.add(imgView);
navImg = new ImageView(getContext());
navImg.setScaleType(ImageView.ScaleType.CENTER_CROP);
if(i==0)
navImg.setImageResource(R.drawable.page_now);
else
navImg.setImageResource(R.drawable.page);
//设置导航图片的标签: 当前导航图片的位置
navImg.setTag(i);
navImg.setLayoutParams(layoutParams);
navLayout.addView(navImg);
}
vPager.setAdapter(new ImageAdapter());
vPager.addOnPageChangeListener(this);
loadImgs();
//开启handler的线程 3秒之后发出此消息
if(handler!=null){
handler.sendEmptyMessageDelayed(0,3000);
}
}
private void loadImgs() {
// 加载网络图片加载后放入imageView
for (int i=0;i<datas.size();i++) {
String data=datas.get(i);
for (ImageView imageView : imgViews) {
if(imageView.getTag().equals(data)){
getImageView(imageView,data,i);
}
}
}
}
class ImageAdapter extends PagerAdapter {
@Override
public int getCount() {
return datas.size()+2;
}
@Override
public boolean isViewFromObject(View arg0, Object arg1) {
// TODO Auto-generated method stub
return arg0 == arg1;
}
@Override
public Object instantiateItem(ViewGroup container, int actPosition) {
//对Viewpager页号求模去除View列表中要显示的项
actPosition %= datas.size();
ImageView view = imgViews.get(actPosition);
//如果View已经在之前添加到了一个父组件,则必须先remove,否则会抛出IllegalStateException。
ViewParent viewParent = view.getParent();
if (viewParent!=null){
ViewGroup parent = (ViewGroup)viewParent;
parent.removeView(view);
}
container.addView(view);
return view;
}
@Override
public void destroyItem(ViewGroup container, int actPosition, Object object) {
//注意不在此方法进行removeView
}
@Override
public void finishUpdate(ViewGroup container) {
super.finishUpdate(container);
int position = vPager.getCurrentItem();
if (position == 0) {
position = datas.size();
currentPosition=position;
if(handler!=null){
handler.removeCallbacksAndMessages(null);
handler.sendEmptyMessageDelayed(0,3000);
}
vPager.setCurrentItem(position,false);
} else if (position == (datas.size()+2) - 1) {
position = 1;
currentPosition=position;
if(handler!=null){
handler.removeCallbacksAndMessages(null);
handler.sendEmptyMessageDelayed(0,3000);
}
vPager.setCurrentItem(position,false);
}
}
}
@Override
public void onPageScrollStateChanged(int arg0) {
// TODO Auto-generated method stub
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub
}
@Override
public void onPageSelected(int position) {
position=position % datas.size();
if(handler!=null){
handler.removeCallbacksAndMessages(null);
}
currentPosition=position;
ImageView navImg = null;
//遍历 导航布局中所有的子控件,判断子控件的位置是否为选择位置,若是,则改变图片的内容
for(int i=0;i<navLayout.getChildCount();i++){
navImg = (ImageView) navLayout.getChildAt(i );//获取布局中指定位置的子控件
if(i==position)
navImg.setImageResource(R.drawable.page_now);
else
navImg.setImageResource(R.drawable.page);
}
if(handler!=null){
handler.sendEmptyMessageDelayed(0,3000);
}
}
public interface TopViewClickListener{
public void onTopViewClick(int position);
}
public TopViewClickListener mTopViewClickListener;
public void setTopViewClickListener(TopViewClickListener topViewClickListener){
mTopViewClickListener=topViewClickListener;
}
private ImageView getImageView(ImageView imageView,String url, final int position) {
imageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mTopViewClickListener != null) {
mTopViewClickListener.onTopViewClick(position);
}
}
});
if (imageLoader == null) {
imageLoader = new DownLoadImage(mContext, R.drawable.bannerdefault, R.drawable.bannerdefault, null);
}
imageLoader.displayImage(url, imageView, null);
return imageView;
}
}
TopView类中用到的布局文件
<?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="wrap_content"> <android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" /> <LinearLayout
android:id="@+id/navLayout"
android:layout_height="30dp"
android:layout_width="match_parent"
android:layout_gravity="bottom"
android:gravity="center"
android:orientation="horizontal" /> </FrameLayout>
如何调用:
在activity_main.xml中
<com.myview.TopView
android:id="@+id/topview"
android:layout_width="match_parent"
android:layout_height="@dimen/size_600px"/>
在MainActivity 中的伪代码:
private TopView topView;
public class ActivityMain extends Activity implements TopView.TopViewClickListener{
public void onCreate(。。。。。){
//urls是图片的地址
topView.setData(urls);
topView= (TopView) findViewById(R.id.topview);
//设置点击图片事件的监听器
topView.setTopViewClickListener(this);
}
@Override
public void onTopViewClick(int position) {
if (datas!= null && datas.size() > 0) {
//用position去加载对应的数据即可
}
} }
如此 完美解决真正意义上的无限轮播图
严禁盗版
转载请注明出处:https://www.cnblogs.com/bimingcong/p/9028515.html

自定义完美的ViewPager 真正无限循环的轮播图的更多相关文章
- 用原生的javascript 实现一个无限滚动的轮播图
说一下思路:和我上一篇博客中用JQ去写的轮播图有相同点和不同点 相同点: 首先页面布局是一样的 同样是改变.inner盒子的位置去显示不同的图片 不同点: 为了实现无限滚动需要多添加两张重复的图片 左 ...
- Android开发之ViewPager实现轮播图(轮播广告)效果的自定义View
最近开发中需要做一个类似京东首页那样的广告轮播效果,于是采用ViewPager自己自定义了一个轮播图效果的View. 主要原理就是利用定时任务器定时切换ViewPager的页面. 效果图如下: 主页面 ...
- swiper轮播图(逆向自动切换类似于无限循环)
swiper插件轮播图,默认的轮播循序是会从右向左,第一张,第二张,第三张,然后肉眼可见是的从第三张从左到右倒回第一张,这样就会有些视觉体验不高, ,不过还是能够用swiper本身的特性更改成无限循环 ...
- 仿百度壁纸客户端(二)——主页自定义ViewPager广告定时轮播图
仿百度壁纸客户端(二)--主页自定义ViewPager广告定时轮播图 百度壁纸系列 仿百度壁纸客户端(一)--主框架搭建,自定义Tab + ViewPager + Fragment 仿百度壁纸客户端( ...
- android ViewPager实现的轮播图广告自定义视图,网络获取图片和数据
public class SlideShowAdView extends FrameLayout { //轮播图图片数量 private static int IMAGE_COUNT = 3; ...
- swift-自定义无限轮播图
一 前言 1.之前一直在用OC编程,最近才开始接触使用swift就发现使用OC越来越不习惯,感觉已经爱上了swift. 2.这个自定义轮播图只是对之前OC版本进行了翻译,欢迎指正. 3.我决定一步步 ...
- Android之无限轮播图源代码
Android轮播广告图是大家经常用到的一个控件今天便撸了一把代码 实现步骤 使用Viewpager进行实现图片滑动 设置ViewPager的数据,让其无限切换 Activity代码 public c ...
- ViewPager轮播图
LoopViewPagerLayout无限轮播 项目地址:https://github.com/why168/LoopViewPagerLayout 支持三种动画: 支持修改轮播的速度: 支持修改滑动 ...
- Android ViewPager轮播图
Android客户端开发中很多时候需要用到轮播图的方式进行重点新闻的推送或者欢迎页面的制作,下面这个轮播图效果的Deamo来自互联网再经过修改而成. 1.布局文件activity_main.xml中添 ...
随机推荐
- 【翻译】Sencha Ext JS 5发布
原文:Announcing Sencha Ext JS 5 简介 我代表Sencha和整个Ext JS团队,很自豪的宣布,在今天,Sencha Ext JS 5发布了.Ext JS 5已经迈出了一大步 ...
- AngularJS进阶(二十三)ANGULAR三宗罪之版本陷阱
ANGULAR三宗罪之版本陷阱 坑!碰到个大坑,前面由于绑定日期时将angular版本换为angular-1.3.0-beta.1时,后来午睡后,登录系统,发现无论如何都登陆不进去了,经过调试,发现数 ...
- Android群英传笔记——第八章:Activity与Activity调用栈分析
Android群英传笔记--第八章:Activity与Activity调用栈分析 开篇,我们陈述一下Activity,Activity是整个应用用户交互的核心组件,了解Activity的工作模式,生命 ...
- 集群通信组件tribes之应用程序处理入口
Tribes为了更清晰更好地划分职责,它被设计成用IO层和应用层,IO层专心负责网络传输方面的逻辑处理,把接收到的数据往应用层传送,当然应用层发送的数据也是通过此IO层发送,数据传往应用层后必须要留一 ...
- 04_Nginx命令行参数,控制信号,Nginx启动、停止、重启命令
Nginx简单型,先关闭进程,修改你的配置后,重启进程. kill -QUIT `cat/usr/local/nginx/nginx.pid` ./nginx 2 重新加载配置文件,不重启进程, ...
- C语言笔试经典-查找多位数重复数字以及次数
从键盘输入一个多位的整数 用程序判断 这个数里面有没有 重复的数字 有重复的数字就打印 哪个数字重复了 重复了几次 例如:输入:1122431 打印结果: 1重复 出现3次 2重复 出现2次, ...
- Treemap 有序的hashmap。用于排序
TreeMap:有固定顺序的hashmap.在需要排序的Map时候才用TreeMap. Map.在数组中我们是通过数组下标来对其内容索引的,键值对. HashMap HashMap 用哈希码快速定位一 ...
- Http的会话跟踪和跨站攻击(xss)
会话跟踪 什么是会话? 客户端打开与服务器的连接发出请求到服务器响应客户端请求的全过程称之为会话. 什么是会话跟踪? 会话跟踪指的是对同一个用户对服务器的连续的请求和接受响应的监视. 为什么需要会话跟 ...
- 解决IE7兼容H5新标签的方法
外部引入JS <script src="http://cdn.bootcss.com/html5shiv/r29/html5.min.js"></script&g ...
- phantomjs 爬去动态页面
最近有一个小需求,需要根据用户输入的某宝的店铺 url,检查地址是否存在,并抓取店铺名称.某宝店铺 url 的 title 通常是 xx-xx-xx 的形式,中间的 xx 就是对应的店铺名称. 这个需 ...