Android无限循环与自动播放ViewPager的简单实现(广告栏)
Posted 冷不冷
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android无限循环与自动播放ViewPager的简单实现(广告栏)相关的知识,希望对你有一定的参考价值。
之前写过一个简单的ViewPager指示器,但是只能够展示指定数量的内容,没有实现无限循环和自动播放功能,今天来完整的把这几个功能写一下吧.当然还是用到之前写的简单的ViewPager指示器,并做一些小修改,来配合无限循环和自动播放.
可参考之前写过的Android简单的ViewPager指示器
效果图如下:
一、简单分析
首先考虑一下无限循环怎么实现,按照之前写的,只是指定了数据源的内容,ViewPager创建了数据源长度的子项个数,可滑动范围比较小.这样就导致拉到最后一页时,无法继续滑动,或者处于第一页的时候,无法向前滑动等情况,所以在设置ViewPager子项数量时,不能设置为数据源的个数.要设置一个比较大的值,才能使ViewPager的可滑动范围比较大,然后通过动态的修改ViewPager的位置,来实现无限循环的效果.
接着考虑一下自动播放如何实现,自动播放,需要一个播放频率,每隔一段时间,去播放一次(也就是向后边滑动一页),这个用定时器Timer配合TimerTask就可以完成,每隔一定时间去执行一次.播放用Handler来做,在TimerTask中,发送一个消息,然后Handler接收到这个消息后,去做播放操作.当然方法有很多.
二、具体实现
通过分析,先来写一下适配器,定义一个比较大的数来规定ViewPager的项数:
/**
* 默认轮播个数
*/
public static final int FAKE_BANNER_SIZE = 10000;
@Override
public int getCount()
return FAKE_BANNER_SIZE;
这样,ViewPager的滑动范围就不会受到限制,不过位置发生变化后,绑定数据时要对位置进行处理,不然会出现数据获取不到或者获取错误的问题,处理位置
@Override
public Object instantiateItem(ViewGroup container, int position)
View view = LayoutInflater.from(mContext).inflate(R.layout.banner_item, container, false);
ImageView imageView = (ImageView) view.findViewById(R.id.iv_banner_item);
// 获取当前显示位置
position %= pictureList.size();
imageView.setImageResource(pictureList.get(position));
container.addView(view);
return view;
对position进行取余操作,这样就可以获取到符合数据源取值范围的位置,从而得到想要获取展示的数据.
搞定了适配器,再来看一下之前写的简单指示器,如何做一下修改呢.
首先新加入一个变量,来控制是否用于无限循环的ViewPager
/**
* 是否是循环
*/
private boolean isCirculate;
public IndicatorView(Context context, AttributeSet attrs, int defStyleAttr)
super(context, attrs, defStyleAttr);
// 获取自定义属性
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.IndicatorView);
...
isCirculate = ta.getBoolean(R.styleable.IndicatorView_isCirculate, true);
ta.recycle();
// 初始化
init();
接着对外提供一个新的设置ViewPager的方法,因为之前获取创建指示器个数的方法是获取ViewPager适配器中子项的个数,适配器已经做了修改,显然这个方法已经不再适用.想一下,指示器个数其实和数据源的个数是一致的,所以提供一个新的方法
/**
* 设置vp
*
* @param childViewCount 需要显示指示器的个数
* @param viewpager vp
*/
public void setViewPager(int childViewCount, ViewPager viewpager)
if (null == viewpager)
return;
if (null == viewpager.getAdapter())
throw new IllegalStateException("ViewPager does not have adapter.");
this.mViewPager = viewpager;
this.mViewPager.addOnPageChangeListener(this);
this.childViewCount = childViewCount;
removeAllViews();
invalidate();
然后在修改指示器显示时,对位置也需要进行处理,方式同适配器中的处理相同
@Override
public void onPageSelected(int position)
// 处理位置变化,防止指示器显示错误
if (isCirculate && 0 != childViewCount)
position %= childViewCount;
setIndicatorState(position);
if (null != mListener)
mListener.onPageSelected(position);
这样ViewPager无限循环的问题基本就搞定了.下面来做下自动播放的
部分.打算通过定时器配合Handle来完成.很简单.直接贴下代码
在Activity中
/**
* 轮播图自动轮播消息
*/
public static final int AUTOBANNER_CODE = 0X1001;
/**
* 当前轮播图位置
*/
private int mBannerPosition;
/**
* 自动轮播计时器
*/
private Timer timer = new Timer();
/**
* 自动轮播任务
*/
private BannerTimerTask mBannerTimerTask;
/**
* 用户当前是否点击轮播图
*/
private boolean mIsUserTouched = false;
/**
* 轮播图Handler
*/
Handler bannerHandler = new Handler(new Handler.Callback()
@Override
public boolean handleMessage(Message msg)
// 当用户点击时,不进行轮播
if (!mIsUserTouched)
// 获取当前的位置
mBannerPosition = mViewPager.getCurrentItem();
// 更换轮播图
mBannerPosition = (mBannerPosition + 1) % mBannerPagerAdapter.FAKE_BANNER_SIZE;
mViewPager.setCurrentItem(mBannerPosition);
return true;
);
/**
* 开始轮播
*/
private void startBannerTimer()
if (timer == null)
timer = new Timer();
if (mBannerTimerTask != null)
mBannerTimerTask.cancel();
mBannerTimerTask = new BannerTimerTask(bannerHandler);
if (timer != null && mBannerTimerTask != null)
// 循环5秒执行
timer.schedule(mBannerTimerTask, 5000, 5000);
public class BannerTimerTask extends TimerTask
/**
* handler
*/
Handler handler;
public BannerTimerTask(Handler handler)
super();
this.handler = handler;
@Override
public void run()
handler.sendEmptyMessage(MainActivity.AUTOBANNER_CODE);
创建一个定时器,每隔5秒钟发送一个消息,Handle接收到消息后,改变ViewPager的位置.不难理解.
这样效果就达到了,一个简单的广告栏效果也就实现了.当然还可以再进一步去做一些封装和优化,这里就不做了,最重要的其实就是把位置搞清楚,其他的问题并不难.
三、完整代码
IndicatorView:
package com.example.junweiliu.simpleindicatorview;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
/**
* 轮播图圆形指示器
* Created by junweiliu on 16/6/15.
*/
public class IndicatorView extends LinearLayout implements ViewPager.OnPageChangeListener
/**
* 需要创建的指示器个数
*/
private int childViewCount = 0;
/**
* 设置圆点间margin
*/
private int mInterval;
/**
* 当前选中的位置
*/
private int mCurrentPostion = 0;
/**
* 普通显示的图片
*/
private Bitmap normalBp;
/**
* 选中时显示的图片
*/
private Bitmap selectBp;
/**
* 设置的轮播图Vp
*/
private ViewPager mViewPager;
/**
* 指示器单项宽度
*/
private int mWidth;
/**
* 指示器单项高度
*/
private int mHeight;
/**
* 圆点半径
*/
private int mRadius;
/**
* 普通状态圆点颜色
*/
private int normalColor;
/**
* 选中状态圆点颜色
*/
private int selectColor;
/**
* 是否是循环
*/
private boolean isCirculate;
/**
* 对外提供ViewPager的回调接口
*/
interface OnPageChangeListener
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);
public void onPageSelected(int position);
public void onPageScrollStateChanged(int state);
/**
* 回调接口
*/
private OnPageChangeListener mListener;
/**
* 设置回调
*
* @param listener
*/
public void setOnPageChangeListener(OnPageChangeListener listener)
this.mListener = listener;
public IndicatorView(Context context)
this(context, null);
public IndicatorView(Context context, AttributeSet attrs)
this(context, attrs, 0);
public IndicatorView(Context context, AttributeSet attrs, int defStyleAttr)
super(context, attrs, defStyleAttr);
// 获取自定义属性
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.IndicatorView);
normalBp = drawableToBitamp(ta.getDrawable(R.styleable.IndicatorView_normalDrawable));
selectBp = drawableToBitamp(ta.getDrawable(R.styleable.IndicatorView_selectDrawable));
mInterval = ta.getDimensionPixelOffset(R.styleable.IndicatorView_indicatorInterval, 6);
normalColor = ta.getColor(R.styleable.IndicatorView_normalColor, Color.GRAY);
selectColor = ta.getColor(R.styleable.IndicatorView_selectColor, Color.RED);
mRadius = ta.getInteger(R.styleable.IndicatorView_indicatorRadius, 6);
isCirculate = ta.getBoolean(R.styleable.IndicatorView_isCirculate, true);
ta.recycle();
// 初始化
init();
/**
* 初始化数据
*/
private void init()
// 处理自定义属性
if (null == normalBp)
normalBp = makeIndicatorBp(normalColor);
if (null == selectBp)
selectBp = makeIndicatorBp(selectColor);
mWidth = normalBp.getWidth();
mHeight = normalBp.getWidth();
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
// 如果是wrap_content设置为图片宽高,否则设置为父容器宽高
setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? sizeWidth : (mWidth + mInterval) * childViewCount
, (heightMode == MeasureSpec.EXACTLY) ? sizeHeight
: mHeight);
/**
* 重绘
*
* @param canvas
*/
@Override
protected void dispatchDraw(Canvas canvas)
// 创建指示器圆点
if (getChildCount() < childViewCount && getChildCount() == 0)
for (int i = 0; i < childViewCount; i++)
addView(makeIndicatorItem());
// 设置默认选中指示器
setIndicatorState(mCurrentPostion);
super.dispatchDraw(canvas);
/**
* 设置Vp
*
* @param viewpager
*/
public void setViewPager(ViewPager viewpager)
if (null == viewpager)
return;
if (null == viewpager.getAdapter())
throw new IllegalStateException("ViewPager does not have adapter.");
this.mViewPager = viewpager;
this.mViewPager.addOnPageChangeListener(this);
this.childViewCount = viewpager.getAdapter().getCount();
invalidate();
/**
* 设置Vp
*
* @param viewpager
* @param currposition 当前选中的位置
*/
public void setViewPager(ViewPager viewpager, int currposition)
if (null == viewpager)
return;
if (null == viewpager.getAdapter())
throw new IllegalStateException("ViewPager does not have adapter.");
this.mViewPager = viewpager;
this.mViewPager.addOnPageChangeListener(this);
this.childViewCount = viewpager.getAdapter().getCount();
this.mCurrentPostion = currposition;
invalidate();
/**
* 设置vp
*
* @param childViewCount 需要显示指示器的个数
* @param viewpager vp
*/
public void setViewPager(int childViewCount, ViewPager viewpager)
if (null == viewpager)
return;
if (null == viewpager.getAdapter())
throw new IllegalStateException("ViewPager does not have adapter.");
this.mViewPager = viewpager;
this.mViewPager.addOnPageChangeListener(this);
this.childViewCount = childViewCount;
removeAllViews();
invalidate();
/**
* 创建指示器
*
* @return
*/
private View makeIndicatorItem()
ImageView iv = new ImageView(getContext());
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
lp.width = normalBp.getWidth();
lp.height = normalBp.getHeight();
lp.rightMargin = mInterval;
iv.setImageBitmap(normalBp);
iv.setLayoutParams(lp);
return iv;
/**
* 创建圆点指示器图片
*
* @param color 创建不同颜色的指示器项
* @return
*/
private Bitmap makeIndicatorBp(int color)
Bitmap normalBp = Bitmap.createBitmap(mRadius * 2, mRadius * 2,
Bitmap.Config.ARGB_8888);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(color);
Canvas canvas = new Canvas(normalBp);
canvas.drawCircle(mRadius, mRadius, mRadius, paint);
return normalBp;
/**
* drawable转bitmap
*
* @param drawable
* @return
*/
private Bitmap drawableToBitamp(Drawable drawable)
if (null == drawable)
return null;
if (drawable instanceof BitmapDrawable)
BitmapDrawable bd = (BitmapDrawable) drawable;
return bd.getBitmap();
int w = drawable.getIntrinsicWidth();
int h = drawable.getIntrinsicHeight();
Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, w, h);
drawable.draw(canvas);
return bitmap;
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
if (null != mListener)
mListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
@Override
public void onPageSelected(int position)
if (isCirculate && 0 != childViewCount)
position %= childViewCount;
setIndicatorState(position);
if (null != mListener)
mListener.onPageSelected(position);
@Override
public void onPageScrollStateChanged(int state)
if (null != mListener)
mListener.onPageScrollStateChanged(state);
/**
* 设置指示器的状态
*
* @param position
*/
public void setIndicatorState(int position)
for (int i = 0; i < getChildCount(); i++)
if (i == position)
((ImageView) getChildAt(i)).setImageBitmap(selectBp);
else
((ImageView) getChildAt(i)).setImageBitmap(normalBp);
适配器BannerPagerAdapter
package com.example.junweiliu.simpleindicatorview;
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 java.util.ArrayList;
import java.util.List;
/**
* Created by junweiliu on 16/6/14.
* VP适配器
*/
public class BannerPagerAdapter extends PagerAdapter
/**
* 上下文
*/
private Context mContext;
/**
* 图像列表
*/
private List<Integer> pictureList = new ArrayList<>();
/**
* 默认轮播个数
*/
public static final int FAKE_BANNER_SIZE = 10000;
public BannerPagerAdapter(Context context, List<Integer> pictureList)
this.mContext = context;
this.pictureList = pictureList;
@Override
public int getCount()
return FAKE_BANNER_SIZE;
@Override
public boolean isViewFromObject(View view, Object object)
return view == object;
@Override
public Object instantiateItem(ViewGroup container, int position)
View view = LayoutInflater.from(mContext).inflate(R.layout.banner_item, container, false);
ImageView imageView = (ImageView) view.findViewById(R.id.iv_banner_item);
// 获取当前显示位置
position %= pictureList.size();
imageView.setImageResource(pictureList.get(position));
container.addView(view);
return view;
@Override
public void destroyItem(ViewGroup container, int position, Object object)
container.removeView((View) object);
MainActivity
package com.example.junweiliu.simpleindicatorview;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
public class MainActivity extends AppCompatActivity
/**
* 轮播图
*/
private ViewPager mViewPager;
/**
* 指示器
*/
private IndicatorView mIndicatorView;
/**
* 适配器
*/
private BannerPagerAdapter mBannerPagerAdapter;
/**
* 图片资源
*/
private List<Integer> pictureList = new ArrayList<>();
/**
* 轮播图自动轮播消息
*/
public static final int AUTOBANNER_CODE = 0X1001;
/**
* 当前轮播图位置
*/
private int mBannerPosition;
/**
* 自动轮播计时器
*/
private Timer timer = new Timer();
/**
* 自动轮播任务
*/
private BannerTimerTask mBannerTimerTask;
/**
* 用户当前是否点击轮播图
*/
private boolean mIsUserTouched = false;
/**
* 轮播图Handler
*/
Handler bannerHandler = new Handler(new Handler.Callback()
@Override
public boolean handleMessage(Message msg)
// 当用户点击时,不进行轮播
if (!mIsUserTouched)
// 获取当前的位置
mBannerPosition = mViewPager.getCurrentItem();
// 更换轮播图
mBannerPosition = (mBannerPosition + 1) % mBannerPagerAdapter.FAKE_BANNER_SIZE;
mViewPager.setCurrentItem(mBannerPosition);
return true;
);
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initDatas();
initView();
/**
* 初始化数据
*/
private void initDatas()
pictureList.add(R.drawable.pic_one);
pictureList.add(R.drawable.pic_two);
pictureList.add(R.drawable.pic_three);
pictureList.add(R.drawable.pic_one);
pictureList.add(R.drawable.pic_two);
pictureList.add(R.drawable.pic_three);
/**
* 初始化控件
*/
private void initView()
mViewPager = (ViewPager) findViewById(R.id.vp_banner);
mIndicatorView = (IndicatorView) findViewById(R.id.idv_banner);
mBannerPagerAdapter = new BannerPagerAdapter(this, pictureList);
mViewPager.setAdapter(mBannerPagerAdapter);
mIndicatorView.setViewPager(pictureList.size(), mViewPager);
// 设置默认起始位置,使开始可以向左边滑动
mViewPager.setCurrentItem(pictureList.size() * 100);
mIndicatorView.setOnPageChangeListener(new IndicatorView.OnPageChangeListener()
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
@Override
public void onPageSelected(int position)
@Override
public void onPageScrollStateChanged(int state)
);
mViewPager.setOnTouchListener(new View.OnTouchListener()
@Override
public boolean onTouch(View v, MotionEvent event)
int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN
|| action == MotionEvent.ACTION_MOVE)
mIsUserTouched = true;
else if (action == MotionEvent.ACTION_UP)
mIsUserTouched = false;
return false;
);
startBannerTimer();
/**
* 开始轮播
*/
private void startBannerTimer()
if (timer == null)
timer = new Timer();
if (mBannerTimerTask != null)
mBannerTimerTask.cancel();
mBannerTimerTask = new BannerTimerTask(bannerHandler);
if (timer != null && mBannerTimerTask != null)
// 循环5秒执行
timer.schedule(mBannerTimerTask, 5000, 5000);
/**
* 销毁时,关闭任务,防止异常
*/
@Override
public void onDestroy()
super.onDestroy();
if (null != mBannerTimerTask)
mBannerTimerTask.cancel();
mBannerTimerTask = null;
BannerTimerTask
package com.example.junweiliu.simpleindicatorview;
import android.os.Handler;
import java.util.TimerTask;
/**
* Created by junweiliu on 16/6/15.
*/
public class BannerTimerTask extends TimerTask
/**
* handler
*/
Handler handler;
public BannerTimerTask(Handler handler)
super();
this.handler = handler;
@Override
public void run()
handler.sendEmptyMessage(MainActivity.AUTOBANNER_CODE);
attr
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--IndicatorView相关-->
<!--普通指示器图片-->
<attr name="normalDrawable" format="reference"/>
<!--选中指示器图片-->
<attr name="selectDrawable" format="reference"/>
<!--指示器间隔-->
<attr name="indicatorInterval" format="dimension"/>
<!--普通指示器颜色-->
<attr name="normalColor" format="color"/>
<!--选中指示器颜色-->
<attr name="selectColor" format="color"/>
<!--圆点弧度-->
<attr name="indicatorRadius" format="integer"/>
<!--是否是循环-->
<attr name="isCirculate" format="boolean"/>
<declare-styleable name="IndicatorView">
<attr name="normalDrawable"/>
<attr name="selectDrawable"/>
<attr name="indicatorInterval"/>
<attr name="normalColor"/>
<attr name="selectColor"/>
<attr name="indicatorRadius"/>
<attr name="isCirculate"/>
</declare-styleable>
</resources>
activity_main
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:idv="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="225dp"
tools:context="com.example.junweiliu.simpleindicatorview.MainActivity">
<!--轮播图-->
<android.support.v4.view.ViewPager
android:id="@+id/vp_banner"
android:layout_width="match_parent"
android:layout_height="225dp"
>
</android.support.v4.view.ViewPager>
<!--指示器-->
<com.example.junweiliu.simpleindicatorview.IndicatorView
android:id="@+id/idv_banner"
android:layout_width="match_parent"
android:layout_height="10dp"
android:layout_alignParentBottom="true"
android:gravity="center_horizontal"
android:layout_marginTop="20dp"
idv:indicatorInterval="10dp"
idv:isCirculate="true"
idv:normalDrawable="@mipmap/oval_indicator_grey"
idv:selectDrawable="@mipmap/oval_indicator_green">
</com.example.junweiliu.simpleindicatorview.IndicatorView>
</RelativeLayout>
banner_item
<?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">
<ImageView
android:id="@+id/iv_banner_item"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
/>
</LinearLayout>
以上是关于Android无限循环与自动播放ViewPager的简单实现(广告栏)的主要内容,如果未能解决你的问题,请参考以下文章
android ViewPager 进阶(仿画廊/图书翻页) 与 palette 使用 (含完整Demo)