思路
在 循環(huán) ViewPager 的兩種實現(xiàn)方法這篇文章中介紹了廣告欄的兩種實現(xiàn)思路,但是直接用到項目中還是會有不少問題。
- 方法1:將 count 設(shè)為無限大,制造一種假的循環(huán)
這種方法在實際的項目中容易導(dǎo)致anr勃救,在調(diào)用setCurrentItem或者在數(shù)據(jù)集發(fā)生改變時調(diào)用notifyDataSetChanged時有發(fā)生宏榕。 - 方法2:在 ViewPager 的首尾添加一個重復(fù)的 View
這種做法的問題是每循環(huán)一次會額外的多調(diào)用一次setCurrentItem,性能不佳碱鳞,尤其是用戶快速滾動時表現(xiàn)不夠流暢桑李。
能否將兩種方法結(jié)合起來呢,比如我將count設(shè)為200個窿给,每次滑動到最后一頁或者第一頁的時候再執(zhí)行setCurrentItem(middleItem)
贵白。當然,我還需要對滑出去的View做好回收崩泡,這點仿照ListView去做即可禁荒。
說干就干。
實現(xiàn)我們的PagerAdapter
看碼說話
public abstract class CyclePagerAdapter extends PagerAdapter {
private final int MAX_PAGES = 200;
// 對View做緩存角撞,防止每次都去inflate
protected LinkedList<View> mScrapViews = new LinkedList<View>();
// 最多緩存兩個View
protected int mMaxScrapViewSize = 2;
@Override
public Object instantiateItem(ViewGroup container, int position) {
View scrap = retrieveFromScrap();
View view = getView(position, scrap, container);
container.addView(view);
return view;
}
private View retrieveFromScrap() {
if (mScrapViews.size() > 0) {
return mScrapViews.removeLast();
}
return null;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
View view = (View) object;
container.removeView(view);
if (mScrapViews.size() < mMaxScrapViewSize) {
mScrapViews.add(view);
}
}
// 返回 getRealCount 的整數(shù)倍呛伴,該數(shù)最大值為 MAX_PAGES,這里將MAX_PAGES設(shè)為200谒所。
@Override
public int getCount() {
if (getRealCount() < 2) {
return getRealCount();
}
return getRealCount() * (MAX_PAGES / getRealCount());
}
public View getView(int position, View convertView, ViewGroup container) {
int realPosition = getRealPosition(position);
return getViewAtRealPosition(realPosition, convertView, container);
}
@Override
public final boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
public int getRealPosition(int position) {
if (getRealCount() == 0) {
return 0;
}
return position % getRealCount();
}
public abstract int getRealCount();
public abstract View getViewAtRealPosition(int position, View convertView, ViewGroup container);
}
在CyclePagerAdapter中热康,getCount返回值最大為200,并且該數(shù)是getRealCount的整數(shù)倍劣领。這里我們還添加了一個回收機制姐军,防止多次創(chuàng)建View導(dǎo)致性能損耗。
使用時只需要繼承CyclePagerAdapter即可。
public class SimpleBannerAdapter extends CyclePagerAdapter {
private static final int[] drawableIds = new int[]{R.drawable.desert, R.drawable.koala,
R.drawable.jellyfish, R.drawable.hydrangeas};
private Context mContext;
public SimpleBannerAdapter(Context context) {
this.mContext = context;
}
@Override
public int getRealCount() {
return drawableIds.length;
}
@Override
public View getViewAtRealPosition(final int position, View convertView, ViewGroup container) {
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.banner_item, container, false);
}
ImageView imageView = (ImageView) convertView.findViewById(R.id.imageView);
imageView.setImageResource(drawableIds[position]);
return convertView;
}
}
監(jiān)聽ViewPager滾動
public class CycleViewPager extends ViewPager {
private CyclePagerAdapter mCyclePagerAdapter;
@Override
public void setAdapter(PagerAdapter adapter) {
super.setAdapter(adapter);
if (adapter instanceof CyclePagerAdapter) {
mCyclePagerAdapter = (CyclePagerAdapter) adapter;
addOnPageChangeListener(mOnPageChangeListener);
setMiddleItemInner(false, true);
}
}
private OnPageChangeListener mOnPageChangeListener = new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageScrolled(int position, float offset, int offsetPixels) {
if (offset != 0) {
return;
}
if (mCyclePagerAdapter == null || mCyclePagerAdapter.getRealCount() <= 1) {
return;
}
// 第一頁
if (position == 0) {
setMiddleItemInner(false, false);
//最后一頁
} else if (position == mCyclePagerAdapter.getCount() - 1) {
setMiddleItemInner(false, false);
}
}
@Override
public void onPageSelected(int position) {
}
};
// 設(shè)置到中間的item奕锌。當ViewPager滾動到第一頁或者最后一頁的時候調(diào)用著觉。
public void setMiddleItem() {
setMiddleItemInner(true, true);
}
private void setMiddleItemInner(boolean setToFirstItem, boolean immediately) {
if (mCyclePagerAdapter != null && mCyclePagerAdapter.getRealCount() > 1) {
int currentItem = setToFirstItem ? 0 : getCurrentItem();
final int middleItem = mCyclePagerAdapter.getCount() / 2 + mCyclePagerAdapter.getRealPosition(currentItem);
if (immediately) {
setCurrentItem(middleItem, false);
} else {
post(new Runnable() {
@Override
public void run() {
setCurrentItem(middleItem, false);
}
});
}
}
}
}
至此,我們已經(jīng)實現(xiàn)了一個可以循環(huán)滾動的ViewPager了歇攻,當然固惯,自動滾動以及ViewPager指示器我們都還沒有實現(xiàn),如果想了解這部分可以參考我的github缴守。我已經(jīng)將這個項目上傳到 https://github.com/leandom/CycleViewPager 這里了葬毫。