欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android实现带指示点的自动轮播无限循环效果

程序员文章站 2024-03-02 10:42:04
想要实现无限轮播,一直向左滑动,当到最后一个view时,会滑动到第一个,无限… 可以自己写viewpager然后加handler先实现自动滚动,当然这里我为了项目的进度直...

想要实现无限轮播,一直向左滑动,当到最后一个view时,会滑动到第一个,无限…

可以自己写viewpager然后加handler先实现自动滚动,当然这里我为了项目的进度直接使用了trinea的android-auto-scroll-view-pager库,网址:点击进入github 引用库compile('cn.trinea.android.view.autoscrollviewpager:android-auto-scroll-view-pager:1.1.2') {
exclude module: 'support-v4'
之后

1布局为

<relativelayout 
android:layout_width="match_parent" 
android:layout_height="@dimen/y150"> 
<cn.trinea.android.view.autoscrollviewpager.autoscrollviewpager 
android:id="@+id/viewpager1" 
android:layout_width="match_parent" 
android:layout_height="wrap_content" /> 
<!--点点的布局--> 
<linearlayout 
android:id="@+id/ll_dot1" 
android:layout_width="match_parent" 
android:layout_height="wrap_content" 
android:layout_alignparentbottom="true" 
android:layout_marginbottom="8dp" 
android:gravity="center" 
android:orientation="horizontal" /> 
</relativelayout> 

2 构建pageradapter
继承自recyclingpageradapter (后面会贴出来源码)

 `public class indicator1adapter extends recyclingpageradapter {
 private list<integer> imageidlist;
 context context;
 //是否循环(创造构造方法,在activity里设置是否)
 //集合大小
 private int size;

 public indicator1adapter(list<integer> mdata, context context) {
  this.imageidlist = mdata;
  this.context = context;
  this.size = mdata.size();
  isinfiniteloop = false;
 }
 @override
 public int getcount() {
 //是:最大(让集合的长度无限,从而模拟无限循环) 否,集合长度
  return isinfiniteloop ? integer.max_value : imageidlist.size();
 }

 /**
  * @return the isinfiniteloop
  */
 public boolean isinfiniteloop() {
  return isinfiniteloop;
 }

 /**
  * @param是否无限循环
  */
 public indicator1adapter setinfiniteloop(boolean isinfiniteloop) {
  this.isinfiniteloop = isinfiniteloop;
  return this;
 }

 /**
  * 真实的position
  *
  * @param position
  * @return
  */
 private int getposition(int position) {
  return isinfiniteloop ? position % size : position;
 }
 @override
 public view getview(int position, view view, viewgroup container) {
  viewholder holder;
  if (view == null) {
   holder = new viewholder();
   view = holder.imageview = new imageview(context);
   view.settag(holder);
  } else {
   holder = (viewholder)view.gettag();
  }
  holder.imageview.setimageresource(imageidlist.get(getposition(position)));
  holder.imageview.setscaletype(imageview.scaletype.fit_xy);
  return view;
 }

 private static class viewholder {

  imageview imageview;
 }
}

3 在activity里或者fragment里就可以设置viewpager

定义的成员变量:

//viewpager1
 @bindview(r.id.viewpager1)
 autoscrollviewpager mpager1;
 //承载小点点的控件容器(布局里有)
 @bindview(r.id.ll_dot1)
 linearlayout mlldot1;
indicator1adapter adapter1 = new indicator1adapter( mdata,act).setinfiniteloop(true);//开启无限循环
  mpager1.setadapter(adapter1);
  mpager1.setinterval(play_time);//轮播时间间隔
  mpager1.startautoscroll();//开启自动轮播
  mpager1.setcurrentitem(integer.max_value / 2 - integer.max_value / 2 % mdata.size());

然后你嫌弃官方的换图间隔时间太短,一闪而过,可以通过反射 设置

//通过反射让滚动速度为自己的喜好的(这里设为1.2s)
  try {
   field field = viewpager.class.getdeclaredfield("mscroller");
   field.setaccessible(true);
   fixedspeedscroller scroller = new fixedspeedscroller(mpager1.getcontext(),
     new accelerateinterpolator());
   field.set(mpager1, scroller);
   scroller.setmduration(1200);
  } catch (exception e) {
   log.e(tag, "exception", e);
  }

4 然后我们的小点点还没有使用呢
这里我写了方法:

/**
 * 设置状态点1
 */
 private void setovallayout1() {
  for (int i = 0; i < mdata.size(); i++) {
  /**
   * 生成对应数量的点点(布局,结果提供)
   */
   mlldot1.addview(inflater.inflate(r.layout.dot, null));
  }
  // 默认显示第一页
  mlldot1.getchildat(0).findviewbyid(r.id.v_dot)
    .setbackgroundresource(r.drawable.dot_selected);
  mpager1.addonpagechangelistener(new viewpager.onpagechangelistener() {
   public void onpageselected(int position) {
    //遍历图片数组
//    toast.maketext(act, "position"+position, toast.length_short).show();
    for (int i = 0; i < mdata.size(); i++) {
     if(i==position%mdata.size()){
      // 圆点选中
      /**
      * 这里需要注意如果直接写position,由于我们是无限循环,他的position是无限往上
      *增加的,那么就会报空指针,因为我们总共才生成了mdata.size()个点点,这里可以让当前的
      *position取余,得到的即是当前位置的点点
      */
      mlldot1.getchildat(position%mdata.size())
        .findviewbyid(r.id.v_dot)
        .setbackgroundresource(r.drawable.dot_selected);
     }else{
      // 取消圆点选中
      mlldot1.getchildat(curindex1%mdata.size())
        .findviewbyid(r.id.v_dot)
        .setbackgroundresource(r.drawable.dot_normal);
     }
    }
    curindex1 = position;
   }

   public void onpagescrolled(int arg0, float arg1, int arg2) {
   }

   public void onpagescrollstatechanged(int arg0) {
   }
  });


 }

别忘了重写

 @override
 public void onpause() {
  super.onpause();
  // stop auto scroll when onpause
  mpager1.stopautoscroll();
 }

 @override
 public void onresume() {
  super.onresume();
  // start auto scroll when onresume
  mpager1.startautoscroll();
 }

好了,无限循环自动轮播,完成了.

5点点布局:

<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content">
 <!-- 小圆点view -->
 <view
  android:id="@+id/v_dot"
  android:layout_width="8dp"
  android:layout_height="8dp"
  android:layout_marginleft="2dp"
  android:layout_marginright="2dp"
  android:background="@drawable/dot_normal"/>
</relativelayout>

6 点点的background
dot_normal.xml

<?xml version="1.0" encoding="utf-8"?><!-- 圆点未选中 -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
 android:shape="oval">
 <solid android:color="@color/background_color" />

 <corners android:radius="5dp" />
</shape>

dot_selected.xml

<?xml version="1.0" encoding="utf-8"?><!-- 圆点选中 -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
 android:shape="oval">
 <solid android:color="@color/red" />
 <corners android:radius="5dp" />
</shape>

recyclingpageradapter的源码依赖recyclebin类,一并贴出来

public class recyclebin {
 /**
 * views that were on screen at the start of layout. this array is populated at the start of
 * layout, and at the end of layout all view in activeviews are moved to scrapviews.
 * views in activeviews represent a contiguous range of views, with position of the first
 * view store in mfirstactiveposition.
 */
 private view[] activeviews = new view[0];
 private int[] activeviewtypes = new int[0];

 /** unsorted views that can be used by the adapter as a convert view. */
 private sparsearray<view>[] scrapviews;

 private int viewtypecount;

 private sparsearray<view> currentscrapviews;

 public void setviewtypecount(int viewtypecount) {
 if (viewtypecount < 1) {
  throw new illegalargumentexception("can't have a viewtypecount < 1");
 }
 //noinspection unchecked
 sparsearray<view>[] scrapviews = new sparsearray[viewtypecount];
 for (int i = 0; i < viewtypecount; i++) {
  scrapviews[i] = new sparsearray<view>();
 }
 this.viewtypecount = viewtypecount;
 currentscrapviews = scrapviews[0];
 this.scrapviews = scrapviews;
 }

 protected boolean shouldrecycleviewtype(int viewtype) {
 return viewtype >= 0;
 }

 /** @return a view from the scrapviews collection. these are unordered. */
 view getscrapview(int position, int viewtype) {
 if (viewtypecount == 1) {
  return retrievefromscrap(currentscrapviews, position);
 } else if (viewtype >= 0 && viewtype < scrapviews.length) {
  return retrievefromscrap(scrapviews[viewtype], position);
 }
 return null;
 }

 /**
 * put a view into the scrapviews list. these views are unordered.
 *
 * @param scrap the view to add
 */
 void addscrapview(view scrap, int position, int viewtype) {
 if (viewtypecount == 1) {
  currentscrapviews.put(position, scrap);
 } else {
  scrapviews[viewtype].put(position, scrap);
 }

 if (build.version.sdk_int >= build.version_codes.ice_cream_sandwich) {
  scrap.setaccessibilitydelegate(null);
 }
 }

 /** move all views remaining in activeviews to scrapviews. */
 void scrapactiveviews() {
 final view[] activeviews = this.activeviews;
 final int[] activeviewtypes = this.activeviewtypes;
 final boolean multiplescraps = viewtypecount > 1;

 sparsearray<view> scrapviews = currentscrapviews;
 final int count = activeviews.length;
 for (int i = count - 1; i >= 0; i--) {
  final view victim = activeviews[i];
  if (victim != null) {
  int whichscrap = activeviewtypes[i];

  activeviews[i] = null;
  activeviewtypes[i] = -1;

  if (!shouldrecycleviewtype(whichscrap)) {
   continue;
  }

  if (multiplescraps) {
   scrapviews = this.scrapviews[whichscrap];
  }
  scrapviews.put(i, victim);

  if (build.version.sdk_int >= build.version_codes.ice_cream_sandwich) {
   victim.setaccessibilitydelegate(null);
  }
  }
 }

 prunescrapviews();
 }

 /**
 * makes sure that the size of scrapviews does not exceed the size of activeviews.
 * (this can happen if an adapter does not recycle its views).
 */
 private void prunescrapviews() {
 final int maxviews = activeviews.length;
 final int viewtypecount = this.viewtypecount;
 final sparsearray<view>[] scrapviews = this.scrapviews;
 for (int i = 0; i < viewtypecount; ++i) {
  final sparsearray<view> scrappile = scrapviews[i];
  int size = scrappile.size();
  final int extras = size - maxviews;
  size--;
  for (int j = 0; j < extras; j++) {
  scrappile.remove(scrappile.keyat(size--));
  }
 }
 }

 static view retrievefromscrap(sparsearray<view> scrapviews, int position) {
 int size = scrapviews.size();
 if (size > 0) {
  // see if we still have a view for this position.
  for (int i = 0; i < size; i++) {
  int fromposition = scrapviews.keyat(i);
  view view = scrapviews.get(fromposition);
  if (fromposition == position) {
   scrapviews.remove(fromposition);
   return view;
  }
  }
  int index = size - 1;
  view r = scrapviews.valueat(index);
  scrapviews.remove(scrapviews.keyat(index));
  return r;
 } else {
  return null;
 }
 }
}

recyclingpageradapter

public abstract class recyclingpageradapter extends pageradapter {
 static final int ignore_item_view_type = adapterview.item_view_type_ignore;

 private final recyclebin recyclebin;

 public recyclingpageradapter() {
 this(new recyclebin());
 }

 recyclingpageradapter(recyclebin recyclebin) {
 this.recyclebin = recyclebin;
 recyclebin.setviewtypecount(getviewtypecount());
 }

 @override public void notifydatasetchanged() {
 recyclebin.scrapactiveviews();
 super.notifydatasetchanged();
 }

 @override public final object instantiateitem(viewgroup container, int position) {
 int viewtype = getitemviewtype(position);
 view view = null;
 if (viewtype != ignore_item_view_type) {
  view = recyclebin.getscrapview(position, viewtype);
 }
 view = getview(position, view, container);
 container.addview(view);
 return view;
 }

 @override public final void destroyitem(viewgroup container, int position, object object) {
 view view = (view) object;
 container.removeview(view);
 int viewtype = getitemviewtype(position);
 if (viewtype != ignore_item_view_type) {
  recyclebin.addscrapview(view, position, viewtype);
 }
 }

 @override public final boolean isviewfromobject(view view, object object) {
 return view == object;
 }

 /**
 * <p>
 * returns the number of types of views that will be created by
 * {@link #getview}. each type represents a set of views that can be
 * converted in {@link #getview}. if the adapter always returns the same
 * type of view for all items, this method should return 1.
 * </p>
 * <p>
 * this method will only be called when when the adapter is set on the
 * the {@link adapterview}.
 * </p>
 *
 * @return the number of types of views that will be created by this adapter
 */
 public int getviewtypecount() {
 return 1;
 }

 /**
 * get the type of view that will be created by {@link #getview} for the specified item.
 *
 * @param position the position of the item within the adapter's data set whose view type we
 *  want.
 * @return an integer representing the type of view. two views should share the same type if one
 *   can be converted to the other in {@link #getview}. note: integers must be in the
 *   range 0 to {@link #getviewtypecount} - 1. {@link #ignore_item_view_type} can
 *   also be returned.
 * @see #ignore_item_view_type
 */
 @suppresswarnings("unusedparameters") // argument potentially used by subclasses.
 public int getitemviewtype(int position) {
 return 0;
 }

 /**
 * get a view that displays the data at the specified position in the data set. you can either
 * create a view manually or inflate it from an xml layout file. when the view is inflated, the
 * parent view (gridview, listview...) will apply default layout parameters unless you use
 * {@link android.view.layoutinflater#inflate(int, viewgroup, boolean)}
 * to specify a root view and to prevent attachment to the root.
 *
 * @param position the position of the item within the adapter's data set of the item whose view
 *  we want.
 * @param convertview the old view to reuse, if possible. note: you should check that this view
 *  is non-null and of an appropriate type before using. if it is not possible to convert
 *  this view to display the correct data, this method can create a new view.
 *  heterogeneous lists can specify their number of view types, so that this view is
 *  always of the right type (see {@link #getviewtypecount()} and
 *  {@link #getitemviewtype(int)}).
 * @return a view corresponding to the data at the specified position.
 */
 public abstract view getview(int position, view convertview, viewgroup container);
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。