Android无限循环与自动播放ViewPager的简单实现广告栏

xhyt8248 8年前
   <p>之前写过一个简单的ViewPager指示器,但是只能够展示指定数量的内容,没有实现无限循环和自动播放功能,今天来完整的把这几个功能写一下吧.当然还是用到之前写的简单的ViewPager指示器,并做一些小修改,来配合无限循环和自动播放.</p>    <p>效果图如下:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/7b497c12e8efac69bb3c2eceb0868bbb.gif"></p>    <h2>一、简单分析</h2>    <p>首先考虑一下无限循环怎么实现,按照之前写的,只是指定了数据源的内容,ViewPager创建了数据源长度的子项个数,可滑动范围比较小.这样就导致拉到最后一页时,无法继续滑动,或者处于第一页的时候,无法向前滑动等情况,所以在设置ViewPager子项数量时,不能设置为数据源的个数.要设置一个比较大的值,才能使ViewPager的可滑动范围比较大,然后通过动态的修改ViewPager的位置,来实现无限循环的效果.</p>    <p>接着考虑一下自动播放如何实现,自动播放,需要一个播放频率,每隔一段时间,去播放一次(也就是向后边滑动一页),这个用定时器Timer配合TimerTask就可以完成,每隔一定时间去执行一次.播放用Handler来做,在TimerTask中,发送一个消息,然后Handler接收到这个消息后,去做播放操作.当然方法有很多.</p>    <h2>二、具体实现</h2>    <p>通过分析,先来写一下适配器,定义一个比较大的数来规定ViewPager的项数:</p>    <pre>  <code class="language-java">/**       * 默认轮播个数       */      public static final int FAKE_BANNER_SIZE = 10000;        @Override      public int getCount() {          return FAKE_BANNER_SIZE;      }</code></pre>    <p>这样,ViewPager的滑动范围就不会受到限制,不过位置发生变化后,绑定数据时要对位置进行处理,不然会出现数据获取不到或者获取错误的问题,处理位置</p>    <pre>  <code class="language-java">@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;      }</code></pre>    <p>对position进行取余操作,这样就可以获取到符合数据源取值范围的位置,从而得到想要获取展示的数据.</p>    <p>搞定了适配器,再来看一下之前写的简单指示器,如何做一下修改呢.</p>    <p>首先新加入一个变量,来控制是否用于无限循环的ViewPager</p>    <pre>  <code class="language-java">/**       * 是否是循环       */      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();        }</code></pre>    <p>接着对外提供一个新的设置ViewPager的方法,因为之前获取创建指示器个数的方法是获取ViewPager适配器中子项的个数,适配器已经做了修改,显然这个方法已经不再适用.想一下,指示器个数其实和数据源的个数是一致的,所以提供一个新的方法</p>    <pre>  <code class="language-java">/**       * 设置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();      }</code></pre>    <p>然后在修改指示器显示时,对位置也需要进行处理,方式同适配器中的处理相同</p>    <pre>  <code class="language-java">@Override      public void onPageSelected(int position) {          // 处理位置变化,防止指示器显示错误          if (isCirculate && 0 != childViewCount) {              position %= childViewCount;          }          setIndicatorState(position);          if (null != mListener) {              mListener.onPageSelected(position);          }      }</code></pre>    <p>这样ViewPager无限循环的问题基本就搞定了.下面来做下自动播放的</p>    <p>部分.打算通过定时器配合Handle来完成.很简单.直接贴下代码</p>    <p>在Activity中</p>    <pre>  <code class="language-java">/**       * 轮播图自动轮播消息       */      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);      }  }</code></pre>    <p>创建一个定时器,每隔5秒钟发送一个消息,Handle接收到消息后,改变ViewPager的位置.不难理解.</p>    <p>这样效果就达到了,一个简单的广告栏效果也就实现了.当然还可以再进一步去做一些封装和优化,这里就不做了,最重要的其实就是把位置搞清楚,其他的问题并不难.</p>    <h2>三、完整代码</h2>    <p>IndicatorView:</p>    <pre>  <code class="language-java">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);          }      }    }</code></pre>    <p>适配器BannerPagerAdapter</p>    <pre>  <code class="language-java">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);      }  }</code></pre>    <p>MainActivity</p>    <pre>  <code class="language-java">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;          }      }  }</code></pre>    <p>BannerTimerTask</p>    <pre>  <code class="language-java">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);      }  }</code></pre>    <p>attr</p>    <pre>  <code class="language-java"><?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></code></pre>    <p>activity_main</p>    <pre>  <code class="language-java"><?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></code></pre>    <p>banner_item</p>    <pre>  <code class="language-java"><?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></code></pre>    <p> </p>    <p> </p>    <p>来自:http://blog.csdn.net/u013904672/article/details/53743308</p>    <p> </p>