Android实现一个丝滑的自动轮播控件实例代码
前言
现在很多的App都有自动轮播的banner界面,用于展示广告图片或者显示当前比较热门的一些活动,除了具备比较酷炫的效果之外,通过轮播的方式来减少对界面的占用,也是很赞的一个设计点。本文主要是总结自动轮播控件的实现过程,以及对这类控件的一些优化的技巧。
一、如何实现
在开始进行我们的代码编程之前,我们先要思考一下,在Google提供的官方Api里面,有没有类似的控件实现了相似的功能,毕竟官方的控件大都经过了时间的考验,无论是稳定性还是性能方面都是非常不错的,如果我们能够基于官方的控件进行相应的改造,控件的稳定性也会有相对的保障。
在比较常见的主流控件里面,其实ViewPager和RecyclerView已经实现了类似的功能,尤其是ViewPager,可以说是已经实现了我们这个控件的大部分功能,所以如果我们基于ViewPager来进行改造的话,也能让我们的轮播控件更加稳定。
那ViewPager跟我们需要的自动轮播控件有多少差距呢,主要有两个:
- 不支持自动播放
- 无法从最后一张滑动到第一张
所以我们主要是针对这两部分进行相应的改造,从而实现我们自己的自动轮播控件。
1.1实现自动轮播功能
要想实现自动轮播功能,我们最先想到的应该是通过Timer或者ScheduledExecutorService来实现计时器的功能,然后让ViewPager通过serCurrentItem(intposition)方法,将当前的Item设置为下一个position的数据,但是如果通过定时器来实现的话,会有一个问题,那就是我们在需要让banner进行停止播放的时候就比较麻烦,所以通过Handler用sendMessage的形式,进行事件的发送实现ViewPager的自动轮播,以及某些场景的停止是比较合理的。
我们来看下代码:
privatestaticclassAutoScrollHandlerextendsHandler{ privateWeakReferencemBannerRef; privatestaticfinalintMSG_CHANGE_SELECTION=1; AutoScrollHandler(AutoScrollViewPagerautoScrollViewPager){ mBannerRef=newWeakReference<>(autoScrollViewPager); } privatevoidstart(){ removeMessages(MSG_CHANGE_SELECTION); sendEmptyMessageDelayed(MSG_CHANGE_SELECTION,AUTO_SCROLL_TIME); } privatevoidstop(){ removeMessages(MSG_CHANGE_SELECTION); } @Override publicvoidhandleMessage(Messagemsg){ if(msg.what==MSG_CHANGE_SELECTION){ if(mBannerRef==null||mBannerRef.get()==null){ return; } AutoScrollViewPagerbanner=mBannerRef.get(); if(banner.mSelectedIndex==Integer.MAX_VALUE){ intrightPos=banner.mSelectedIndex%banner.mBannerList.size(); banner.setCurrentItem(banner.getInitPosition()+rightPos+1,true); }else{ if(!hasMessages(MSG_CHANGE_SELECTION)){ banner.setCurrentItem(banner.mSelectedIndex+1,true); sendEmptyMessageDelayed(MSG_CHANGE_SELECTION,AUTO_SCROLL_TIME); } } } } }
可以看到,我们先传入外部的ViewPager,然后通过弱引用的形式防止内存泄露,通过在handlerMessage()方法里面,调用setCurrentItem()方法,将当前ViewPager的Item设置为对应的position+1的数据,所以我们只要在外部调用Handler的sendMessage()方法,就能使ViewPager进行自动的无限轮播。
1.2让ViewPager从最后一张滑动到第一张
我们知道,ViewPager是无法从最后一页滑动到第一页的,但我们可以换一个思路,如果我们在ViewPager的Adapter里面,通过getCount()方法将ViewPager的大小设置为无限大,然后通过取余的方式来保证滑动的页面一直对应数据源的那几个数据,这样便能让ViewPager实现从最后一张滑动到第一张的效果。
publicintgetCount(){ if(mBannerList==null){ return0; } if(mBannerList.size()==1){ return1; }else{ returnInteger.MAX_VALUE; } }
publicObjectinstantiateItem(ViewGroupcontainer,finalintposition){ if(mBannerList!=null&&mBannerList.size()>0){ ViewimageView=null; Uriuri=Uri.parse(mBannerList.get(position%mBannerList.size()).cover_url);//通过取余的方式 imageView=newSimpleDraweeView(mContext); ((SimpleDraweeView)imageView).setImageURI(uri); container.addView(imageView); returnimageView; } returnnull; }
二、如何进行优化
在上面我们只是简单的实现了ViewPager的自动轮播功能,但其实还有很多的细节需要我们进行优化,例如:我们是通过将ViewPager的大小设置为无限大的方式,来实现从最后一张滑动到第一张的,但这时候如果不进行缓存的话,我们在Adapter的instantiateItem(ViewGroupcontainer,finalintposition)方法里面,便需要返回很多新new出来的View,这样会造成不必要的内存浪费,只有对这些细节进行优化,才能让我们的控件更加的好用,稳定性和性能方面也会更加优异。
2.1通过缓存减少内存浪费
为了让ViewPager能实现无线轮播的功能,我们是使用了通过将getCount()的大小设置为无限大的方式来实现的,但这会产生一个问题,这样会使我们在Adapter的instantiateItem()方法中返回很多新new出来的View,而造成不必要的内存浪费。
所以我们可以通过一个List作为缓存池,在Adapter中的destroyItem()方法中将废弃的object存到缓存池中,重复利用,这样便能避免内存浪费。
privatefinalArrayListmViewCaches=newArrayList<>(); @Override publicvoiddestroyItem(ViewGroupcontainer,intposition,Objectobject){ ImageViewimageView=(ImageView)object; container.removeView(imageView); mViewCaches.add(imageView); }
然后在Adapter的instantiateItem()方法中,从List中取出已经被缓存的View,进行重复利用
publicObjectinstantiateItem(ViewGroupcontainer,finalintposition){ if(mBannerList!=null&&mBannerList.size()>0){ ViewimageView=null; Uriuri=Uri.parse(mBannerList.get(position%mBannerList.size()).cover_url); if(mViewCaches.isEmpty()){ imageView=newSimpleDraweeView(GlobalContext.getContext()); }else{ //当缓存集合有数据时,进行复用 imageView=(ImageView)mViewCaches.remove(0); } }
2.2适当的停止自动轮播
当我们触摸Banner或者离开当前展示Banner的页面时,如果banner还在不停的进行无线轮播的话,会造成没必要的性能损失,所以我们需要在触摸Banner以及当前的Activity为不可见状态的时候,停止Banner的轮播,从而提升性能。
publicbooleandispatchTouchEvent(MotionEventev){ intaction=ev.getAction(); if(action==MotionEvent.ACTION_UP||action==MotionEvent.ACTION_CANCEL ||action==MotionEvent.ACTION_OUTSIDE){ startAutoPlay(); }elseif(action==MotionEvent.ACTION_DOWN){ stopAutoPlay(); } returnsuper.dispatchTouchEvent(ev); }
2.3改变ViewPager切换速度
原生的ViewPager在进行自动轮播的时候,切换速度是特别快的,会给人一种很突兀的感觉,而且ViewPager也没有提供接口给我们对ViewPager进行切换速度的设置,所以我们需要通过反射的方式,使用Scroller来进行切换速度的设置,从而让我们的Banner更加的丝滑。
publicAutoScrollViewPager(Contextcontext){ this(context,null); initViewPagerScroll(); } privatevoidinitViewPagerScroll(){ try{ FieldmField=ViewPager.class.getDeclaredField("mScroller"); mField.setAccessible(true); BannerScrollerscroller=newBannerScroller(getContext()); mField.set(this,scroller); }catch(Exceptione){ Log.d(TAG,e.getMessage()); } }
publicclassBannerScrollerextendsScroller{ privatestaticfinalintBANNER_DURATION=1000; privateintmDuration=BANNER_DURATION; publicBannerScroller(Contextcontext){ super(context); } @Override publicvoidstartScroll(intstartX,intstartY,intdx,intdy,intduration){ super.startScroll(startX,startY,dx,dy,mDuration); } }
至此,我们的自动轮播控件,无论是性能上还是稳定性上都已经很不错了。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。