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); }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
下一篇: MapTask工作机制图文详解