Android 自动无限轮播的轮播图(附源码)(通过InfiniteShufflingViewPager解决冲突)
轮播图是绝大多数的项目都要实现的功能,因为比较常用且并不复杂,所以出现了各种各样的轮播图,今天就做一个可以自动无限轮播的轮播图。废话不多说,直接进入正题。
注:文章末尾附项目源码下载链接。
效果展示
主要功能包括:通过Handler实现自动无限轮播、小圆点背景选择器、InfiniteShufflingViewPager解决冲突、轮播图数量为1时禁止滑动、Adapter的特殊处理等。下面先看下效果:
页面布局
以下为轮播图的页面布局文件代码:该文件中使用了自定义控件InfiniteShufflingViewPager,该控件的功能包括:1.请求父控件及祖宗控件不要拦截事件;2.解决ScrollView和ViewPager的上下滑动冲突;3.当ViewPager填充的数据为1的时候,让其不能滑动;
注:文章末尾附InfiniteShufflingViewPager源码。
<?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:background="#333333" android:orientation="vertical"> <RelativeLayout android:layout_width="match_parent" android:layout_height="180dp"> <com.wangyang.infiniteshufflingviewpager.customview.InfiniteShufflingViewPager android:id="@+id/vp" android:layout_width="match_parent" android:layout_height="180dp" /> <LinearLayout android:id="@+id/ll_point_container" android:layout_width="match_parent" android:layout_height="20dp" android:layout_alignParentBottom="true" android:gravity="center" android:orientation="horizontal" android:paddingLeft="5dp" android:paddingRight="5dp" /> </RelativeLayout> </LinearLayout>
初始化PagerAdapter
为ViewPager初始化数据适配器PagerAdapter,用法基本上保持一致,特殊处理两点:1.将数量设置为Integer.MAX_VALUE(Integer的最大值);2.重新计算position(int newPosition = position % img.length);以下为PagerAdapter的代码:
/** * 无限轮播的ViewPager的adapter * 1.将数量设置为Integer.MAX_VALUE; * 2.重新计算position(int newPosition = position % img.length;) */ PagerAdapter infiniteShufflingViewPagerAdapter = new PagerAdapter() { @Override public int getCount() { return Integer.MAX_VALUE; } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } @Override public Object instantiateItem(ViewGroup container, int position) { //因为要无限轮循播放 final int newPosition = position % img.length; ImageView imageView = new ImageView(context); imageView.setImageResource(img[newPosition]); imageView.setScaleType(ImageView.ScaleType.FIT_XY); container.addView(imageView); return imageView; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); } };
初始化小圆点
根据img的数量初始化小圆点,1.创建View;2.设置背景选择器;3.设置宽高;4.设置左外间距(除了第一个);5.添加到容器中;6.设置都不可用(默认是可用的,即亮的);以下为初始化小圆点的代码:
/** * 初始化小圆点 */ private void initPoint() { if (img != null && img.length > 0) { for (int i = 0; i < img.length; i++) { //创建指示器(小白点) View pointView = new View(context); //设置背景选择器 pointView.setBackgroundResource(R.drawable.selector_bg_point); //设置宽高 LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(DensityUtil.dip2px(5, context), DensityUtil.dip2px(5, context)); //设置左外间距(除了第一个) if (i != 0) { params.leftMargin = DensityUtil.dip2px(10, context); } llPointContainer.addView(pointView, params); //设置都不可用(默认是可用的,即亮的) pointView.setEnabled(false); } } }
以下为背景选择器的文件代码:
shape_bg_point_disable.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="#ffffff" /> <stroke android:width="1dp" android:color="#f47a39" /> </shape>
shape_bg_point_enable.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="#f47a39" /> </shape>selector_bg_point.xml
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android" > <item android:state_enabled="true" android:drawable="@drawable/shape_bg_point_enable"></item> <item android:state_enabled="false" android:drawable="@drawable/shape_bg_point_disable"></item> </selector>
初始化ViewPager
最后对ViewPager进行初始化操作,实现通过Handler实现自动无限轮播、小圆点的展示、轮播图数量为1时禁止滑动等操作。以下为初始化ViewPager的代码:
/** * 初始化ViewPager */ private void initViewPager() { //异常处理 if (img == null || img.length <= 0) { return; } //设置数据适配器 vp.setAdapter(infiniteShufflingViewPagerAdapter); if (img.length > 1) { if (llPointContainer.getChildCount() > 0) { //设置到某个位置,使左右都可无限滑动 vp.setCurrentItem(50000 + llPointContainer.getChildCount() - 50000 % llPointContainer.getChildCount()); //设置第一个圆点默认是可用的 llPointContainer.getChildAt(0).setEnabled(true); previousSelectedPosition = 0; } vp.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { int newPosition = position % img.length; //指示器:将之前的禁用,把最新的启用,更新指示器 llPointContainer.getChildAt(newPosition).setEnabled(true); llPointContainer.getChildAt(previousSelectedPosition).setEnabled(false); //记录之前的位置和时间 previousSelectedPosition = newPosition; startTime = System.currentTimeMillis(); } @Override public void onPageScrollStateChanged(int state) { } }); //通过Handler使ViewPager无限轮播 if (mHandler == null) { mHandler = new Handler() { @Override public void handleMessage(Message msg) { endTime = System.currentTimeMillis(); if (endTime - startTime >= 2000) { vp.setCurrentItem(vp.getCurrentItem() + 1); //继续发送延时3秒的消息,形成内循环 mHandler.sendEmptyMessageDelayed(0, 3000); } } }; new Thread() { @Override public void run() { //启动自动轮播逻辑(保证只执行一次) mHandler.sendEmptyMessageDelayed(0, 3000); } }.start(); } //当手指按住viewpager时,停止自动轮播; //当手指松开viewpager时,开启自动轮播; vp.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //停止广告自动轮播,删除handler的所有消息 mHandler.removeCallbacksAndMessages(null); break; case MotionEvent.ACTION_CANCEL: //启动广告 mHandler.sendEmptyMessageDelayed(0, 3000); break; case MotionEvent.ACTION_UP: //启动广告 mHandler.sendEmptyMessageDelayed(0, 3000); break; } return false; } }); } else { //当轮播图数量为1时,禁止ViewPager滑动 vp.setCanScroll(false); } }
小结
以上就是轮播图的实现过程了,其它具体的实现细节,文章末尾的源码中都有详细的注解,在这里就不一一赘述了。实际开发中还是需要通过接口获取轮播图的图片,不管以哪种形式获取图片,都要注意图片的大小,防止出现OOM(Out of Memory)内存溢出异常,导致程序崩溃。