Android真正意义上的无限轮播Banner和Indicator
概述
应用首页的广告轮播Banner
,一般都会使用ViewPager
来实现,但是ViewPager
没有轮播效果。
现成有这么几种实现方案,
1.使用Integer.MAX_VALUE
,理论上很难达到边界。
2.装饰adapter
方式[imbryk/LoopingViewPager@Github]。
3.扩展ViewPager
方式[yanzm/LoopViewPager]。
相关库
轮播效果其实就是一个定时任务,可以用定时器,Handler
等。
[Trinea/Android Auto Scroll ViewPager@Github]
此库通过handler
定时发送消息实现,用的比较多,使用中感觉切换并非无缝。主要代码如下:
/** * scroll only once */ public void scrollOnce() { PagerAdapter adapter = getAdapter(); int currentItem = getCurrentItem(); int totalCount; if (adapter == null || (totalCount = adapter.getCount()) <= 1) { return;
}
int nextItem = (direction == LEFT) ? --currentItem : ++currentItem;
if (nextItem < 0) {
if (isCycle) {
setCurrentItem(totalCount - 1, isBorderAnimation);
}
} else if (nextItem == totalCount) {
if (isCycle) {
setCurrentItem(0, isBorderAnimation);
}
} else {
setCurrentItem(nextItem, true);
}
}
这里是在最后或第一页直接setCurrentItem()
,因此,并非无缝轮播效果。
和本文开头的几个控件整合一下,即可方便 的实现我们想要无缝 的效果。
基本实现
1.修改[Trinea/Android Auto Scroll ViewPager@Github]
改变首页和最后一页的跳转设置。
/** * scroll only once */ public void scrollOnce() { PagerAdapter adapter = getAdapter(); int currentItem = getCurrentItem(); if (adapter == null || adapter.getCount() <= 1) { return; } int nextItem = (direction == LEFT) ? --currentItem : ++currentItem; setCurrentItem(nextItem, true); }
2.实现方式选择
第一种方式其实主要改变了adapter
中的index
,大体如下:
@Override public int getCount() { return Integer.MAX_VALUE; } @Override public Object instantiateItem(ViewGroup container, int position) {
position = position % listviews.size() ; //.... }
只是理论上的无限,需要自己处理position
。
第二种装饰了adapter
,在前后各添加一个Item
,当到了我们添加的临界item
的时候,立即设置到正确的 position
,主要方法如下:
LoopPagerAdapterWrapper
:
int toRealPosition(int position) { int realCount = getRealCount(); if (realCount == 0) return 0; int realPosition = (position-1) % realCount; if (realPosition < 0) realPosition += realCount; return realPosition; }
这里,内部封装position
映射,不需要自己处理position
,和正常ViewPager
使用方式一样。
第三种,重写扩展ViewPager
继承 ViewGroup
,使其item
无限。需要自己处理position
,而且 和 [JakeWharton/ViewPagerIndicator@Github]搭配使用起来不是很方便,setViewPager
方法需要额外处理。
综上,使用第二种方式 扩展性和 易用性都比较好。
Indicator
[JakeWharton/ViewPagerIndicator@Github]
这是一个比较全面的indicator
,如果我们只需要简单的CircleIndicator
,可以抽取来使用。
优秀相关开源库:
[THEONE10211024/CircleIndicator@Github]
[ongakuer/CircleIndicator@Github]
也可以看看本人站在巨人肩膀上完成的[DrawableIndicator@Github],
实时偏移,切换动画,drawable资源
测试
my_banner.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.bobomee.android.scrollloopviewpager.autoscrollviewpager.AutoScrollViewPager
android:id="@+id/picslooper1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<com.bobomee.android.drawableindicator.widget.DrawableIndicator
android:id="@+id/pageIndexor1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
app:indicator_height="15dp"
app:indicator_margin="15dp"
app:indicator_select_src="@drawable/select_drawable"
app:indicator_unselect_src="@drawable/unselect_drawable"
app:indicator_width="15dp" />
</RelativeLayout>
MainActivity:
AutoScrollViewPager viewPager = (AutoScrollViewPager) findViewById(R.id.picslooper1); viewPager.setFocusable(true); viewPager.setAdapter(new FragmentStateAdapter(getSupportFragmentManager())); BaseIndicator pageIndex = (BaseIndicator) findViewById(R.id.pageIndexor1); pageIndex.setViewPager(viewPager); viewPager.startAutoScroll();
最终效果图: