自定义完美的ViewPager 真正无限循环的轮播图
Posted 短暂的火光
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义完美的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 真正无限循环的轮播图的主要内容,如果未能解决你的问题,请参考以下文章
ViewPager,ViewPager2 无限轮播功能。自定义 Indicator,支持一屏三页,支持仿魅族 banner 效果。极其简单的使用方式