前言
前些天需要使用到安卓的banner,也就是现在主流app主页的无限轮播的横幅,现在已经有很多好的开源项目可以直接使用,不过我还是想自己去实现一遍。因为是访问的网络数据,实际过程中还是有些坑的,所以还是记录一下。
具体实现
首先还是来看看最终的效果,gif是有些卡顿,跑起来还是很流畅的
了解到,现在实现这种横幅,基本上是2种方式,一种是使用RecyclerView的横向滚动去实现,因为横幅是从一个页面直接跳转到下一页,用RecyclerView需要监听滑动的过程,计算滑动的距离,然后进行跳转,后面官方考虑到这一点提供了PagerSnapHelper这个工具类来解决这个问题,这里就不多说了。
这篇博客主要就是写的就是第二种方式,使用ViewPager去实现。
首先,要用ViewPager实现无限轮播,可以使PagerAdapter的getCount方法返回Integer.MAX_VALUE。也就是让页面数量返回一个Integer的最大值,这样在滑动过程中产生一种无限循环的假象,首先写一个抽象基类,继承自PagerAdapter
定义适配器
|
|
这里主要是重写instantiateItem方法,进行添加页面的逻辑,首先要判断getSize不为0,因为实际网络加载时可能是一个异步的耗时操作,如果执行到下面的计算时,除数为0肯定会报错的,其它类似地方也都是这样,然后注意到下面的position%getSize,这里是用当前位置对实际item的数量进行模运算取余,得到的值就是当前item的实际位置(前面说过无限轮播是不停的增加页面,造成轮播的假象,实际位置就是指在进行轮播的几个item中,当前处的位置),接下来就判断,如果item不在集合中,就把view添加到一个List集合里面,最后要防止同一个view的重复添加,所以每次添加前需要移除这个view。
接着重写destroyItem方法,每次循环跳转时都要销毁掉之前的view。下面两个抽象方法就是用来获取到具体的值了。
然后就是我们的具体视图的适配器
这里基础抽象基类,逻辑比较简单,没什么好讲的。
视图绘制
然后重点就是自定义的BannerView类了
BannerView继承FrameLayout,实现滑动监听的接口,这里先看看initView方法,这里给viewpager设置了适配器,然后接着就是加载圆点指示器的方法,逻辑也比较简单,加载前注意先移除掉之前的圆点,防止刷新后点的数量重复添加。然后就是第一次加载时,时ViewPager跳转到mPoinCoun*100+x的位置,也是为了防止首次加载无法向左滑动,就不是无限循环的假象了。然后就让我们的ViewPager执行定时滑动任务,定时任务有很多的实现方式,可以使用Timer+handler的方式,可以使用CountDownTimer类来实现,这里的话,我使用的是线程池来进行的定时任务,后面分别传入预加载与跳转周期,然后里面需要进行判断当前是否触摸屏幕,所以要重写dispatchEvent方法,监听当前的动作,然后向handler发送消息,再进行页面滑动,这里可能会有疑惑,线程池已经设置了定时任务,为什么还要向handler去发送消息,进行延时处理,handler里面才是真正的滚动延时,除了是非Ui线程不进行Ui更新的操作 也是因为在我们刷新后,线程池会造成阻塞,无法正常执行。我使用了其它几种方式,还是出现了各种问题,这里就不多说了。
整个BannerView的流程就走完了
再看看布局文件 banner_item_layout
banner_view_layout
使用的话就直接给adapter添加数据,然后实例化给BannerView设置adapter就可以了。这里很要注意一点,就是每次刷新之前记得手动关闭线程池,也就是调用BannView中的cancelScroll()方法,不然会造成进行网络加载,第二次刷新加载数据时,banner直接出现空白页面,这肯定不是我们想要的。 另外,配合RecyclerView使用时,可以把BannerView动态添加到RecyclerView的头部,直接放到布局中,如果要实现RecyclerView上滑时,banner跟着一起滚动的话,可以使用NestedScrollView,但是会造成嵌套滑动冲突,这一点也没看到好的解决办法,设置了NestedScrollingEnable属性,但是会导致RecyclerView无法上拉加载,如果一次性加载完数据的话,RecyclerView的复用和回收机制就没起到作用了,很容易出现OOM,这也不是我们想看到的,所以最好还是配合RecyclerView使用。
总结
总的来说,实现一个并不困难,难在实际过程中会出现各种各样的问题,也正是常说的,debug时间远远多于写代码的是时间(说到底还是经验不足的,理解不够深的缘故)。废话不多说,最后该Demo地址github