无限循环banner实现过程中还是遇到很多坑的:主要有向后滑动白屏,只有两个page时白屏.最近一一解决了上述问题,并对其原因进行了探究,在此记录,希望能对初学者有所帮助.
/**
- 关于无限循环banner,实现:
- 1.继承ViewPager,实现滑动翻页
- 2.通过重写PagerAdapter的getCount return Integer.MAX_VALUE,实现无限滑动
- 3.Observable.interval结合setCurrentItem()实现自动翻页
- 4.处理点击事件,自动滑动的暂停(MotionEvent.ACTION_MOVE)与启动(MotionEvent.ACTION_UP)
*注意:1.instantiateItem中view复用
- 2.destroyItem中不做任何处理
- 3.只有一个或者两个item时的特殊处理
- @author fengjingchao */
代码:
public class AutoBanner extends ViewPager {
private int mCurrentPage;
private Disposable mViewPagerSubscribe;
private OnItemClickListener listener;
private ArrayList<ScanEntity.BannersBean> urls;
private Context mContext;
private boolean isAutoPlay = true;
private int delay;
private int period;
public AutoBanner(@NonNull Context context) {
this(context, null);
}
public AutoBanner(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
TypedArray b = context.obtainStyledAttributes(attrs, R.styleable.AutoBanner);
delay = b.getInt(R.styleable.AutoBanner_delay, 3000);
period = b.getInt(R.styleable.AutoBanner_delay, 2000);
mContext = context;
}
public interface OnItemClickListener {
void onItemClick(int position);
}
/**
* 为banner设置数据
*
* @param datas
*/
public void setUrls(List<ScanEntity.BannersBean> datas) {
if (datas == null || datas.size() == 0) {
return;
}
if (datas.size() <= 1) {
this.isAutoPlay = false;
}
urls = new ArrayList<>();
//size==2时,由于前后两个相同,导致第三个instantiateItem时第一个被remove掉,会出现白屏
if (datas.size() == 2) {
urls.addAll(datas);
urls.addAll(datas);
} else {
urls.addAll(datas);
}
ArrayList<ImageView> imageViews = new ArrayList<>();
for (int i = 0; i < urls.size(); i++) {
ImageView imageView = new ImageView(mContext);
RequestOptions requestOptions = new RequestOptions().centerCrop();
Glide.with(mContext).load(urls.get(i).getUrl()).apply(requestOptions).into(imageView);
imageViews.add(imageView);
}
setAdapter(new ImageAdapter(imageViews));
addOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageScrolled(int i, float v, int i1) {
}
@Override
public void onPageSelected(int position) {
mCurrentPage = position;
}
@Override
public void onPageScrollStateChanged(int i) {
}
});
setCurrentItem(urls.size() * 50);
resume();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
resume();
break;
case MotionEvent.ACTION_MOVE:
stop();
break;
}
return super.onTouchEvent(event);
}
public void stop() {
if (mViewPagerSubscribe == null) {
return;
}
if (!mViewPagerSubscribe.isDisposed()) {
mViewPagerSubscribe.dispose();
}
}
public void resume() {
boolean isRunning = mViewPagerSubscribe != null && !mViewPagerSubscribe.isDisposed();
if (!isAutoPlay || isRunning) {
return;
}
mViewPagerSubscribe = Observable.interval(delay, period, TimeUnit.MILLISECONDS)
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Long>() {
@Override
public void accept(Long aLong) throws Exception {
// 进行轮播操作
if (urls != null && urls.size() > 0) {
mCurrentPage++;
setCurrentItem(mCurrentPage);
}
}
});
}
/**
* 为banner item设置点击监听
*
* @param listener
*/
public void setOnItemClickListener(OnItemClickListener listener) {
this.listener = listener;
}
private class ImageAdapter extends PagerAdapter {
private final int childCount;
private ArrayList<ImageView> imageViews;
public ImageAdapter(ArrayList<ImageView> imageViews) {
this.imageViews = imageViews;
childCount = imageViews.size();
}
@Override
public int getCount() {
//设置成最大,使用户看不到边界
if (imageViews == null || imageViews.size() <= 1) {
return imageViews.size();
}
return Integer.MAX_VALUE;
}
@Override
public Object instantiateItem(ViewGroup container, final int position) {
//重复利用view
ImageView imageView = imageViews.get(position % childCount);
imageView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) {
listener.onItemClick(position % childCount);
}
}
});
ViewParent parent = imageView.getParent();//parent == container
if (parent != null) {
ViewGroup viewParent = (ViewGroup) parent;
viewParent.removeView(imageView);
}
container.addView(imageView);
return imageView;
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object o) {
return view == o;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
//对于无限循环的viewpager ,view是需要被循环利用的,滑动一个循环后,container中的childcount将固定为最大值(即view的个数),再滑动的话也不会增加,
// 所以不需要通过removeView来释放,removeView的话反而可能会将刚instantiateItem的view移除,导致出现白屏
// container.removeView((View)object);
}
}
}
attr.xml
<resources>
<declare-styleable name="AutoBanner">
<attr name="delay" format="integer"></attr>
<attr name="period" format="integer"></attr>
</declare-styleable>
</resources>
layout.xml
<com.citybank.test.main.weight.AutoBanner
android:id="@+id/banner"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_200"
app:delay="3000"
app:period="2000" />