給本身的忠告:雖然輪子很好用,可是使用輪子的前提是:若是不去封裝一些複雜的功能,本身會用最基本的方法寫一個,否則再好的輪子那也是別人的,當本身項目遇到和輪子不同的地方,那就只能一籌莫展或者改人家的源碼,固然能看懂輪子的封裝思想本身學以至用而且可以很輕鬆的更改源碼那是最好不過的了。java
兩種方案:git
(1)採用Adapter內的getCount()方法返回Integer.MAX_VALUE。github
(2)在列表的最前面插入最後一條數據,在列表末尾插入第一個數據,形成循環的假象ide
在ViewPager的Adapter內的getCount方法中,返回一個很大的數Integer.MAX_VALUE,理論上能夠無限滑動。當顯示完一個真實列表的週期後,又從真實列表的0位置顯示數據,形成無限循環輪播的假象。由於ViewPager第一頁不能向左滑動循環,因此咱們要經過mViewPager.setCurrentItem(Integer.MAX_VALUE/2)設置選中的位置,這樣最開始就能夠向左滑動,可是由於要顯示第一頁因此該值%數據個數==0。由於設置爲Integer.MAX_VALUE後會在setCurrentItem()的時候發生ANR,因此這裏使用一個自定義的較大的數比較好,這裏我是用500oop
//當前選中頁
private int currentPosition;
//數據項個數
private List<Integer> itemList;
public static final int mLooperCount = 500;
//設置當前選中的item
currentPosition = getStartItem();
viewPager1.setCurrentItem(currentPosition1);
private int getStartItem() {
if(getRealCount() == 0){
return 0;
}
// 咱們設置當前選中的位置爲Integer.MAX_VALUE / 2,這樣開始就能往左滑動
// 可是要保證這個值與getRealPosition 的 餘數爲0,由於要從第一頁開始顯示
int currentItem = getRealCount() * BannerAdapter.mLooperCount / 2;
if(currentItem % getRealCount() ==0 ){
return currentItem;
}
// 直到找到從0開始的位置
while (currentItem % getRealCount() != 0){
currentItem++;
}
return currentItem;
}
//獲取數據項個數
private int getRealCount() {
return itemList == null ? 0 : itemList.size();
}
複製代碼
Adapter只需將getCount()返回Integer.MAX_VALUE便可(這裏咱們改成具體的數值),其餘的操做是正常的操做。post
@Override
public int getCount() {
return getRealCount() * mLooperCount;
}
複製代碼
採用Handler的postDelayed方法動畫
private Handler mHandler = new Handler();
@Override
protected void onResume() {
super.onResume();
//開始輪播
mHandler.postDelayed(mLoopRunnable, mDelayedTime);
}
@Override
protected void onPause() {
super.onPause();
//中止輪播
mHandler.removeCallbacks(mLoopRunnable);
}
複製代碼
private final Runnable mLoopRunnable = new Runnable() {
@Override
public void run() {
if (mIsAutoPlay) {
//方案一
currentPosition1 = viewPager1.getCurrentItem();
currentPosition1++;
if (currentPosition1 == bannerAdapter.getCount() - 1) { //滑到最後一個時
currentPosition1 = 0; //切換到第0個
viewPager1.setCurrentItem(currentPosition1, false);
mHandler.postDelayed(this, mDelayedTime);
} else {
viewPager1.setCurrentItem(currentPosition1);
mHandler.postDelayed(this, mDelayedTime);
}
} else {
mHandler.postDelayed(this, mDelayedTime);
}
}
};
複製代碼
有人會以爲會影響內存,你們能夠參考這篇文章Android ViewPager 無限輪播Integer.MAX_VALUE 爭議(看源碼)就可以解決疑惑。this
使用Integer.MAX_VALUE會在setCurrentItem()的時候發生ANR,因此仍是設置爲一個比較大的數比較好。在代碼中我已經更改成返回getRealCount()*500這一數值,若是文章中有返回Integer.MAX_VALUE的,那就是我還沒更正,你們請自行更改。spa
假設有三條數據,分別編號一、二、3,咱們再建立一個新的列表,長度爲真實列表的長度+2,在最前面插入最後一條數據3,在最後面插入第一條數據1,新列表就變爲三、一、二、三、1,當viewpager滑動到位置0時就經過setCurrentItem(int item,boolean smoothScroll)
方法將頁面切換到位置3,同理當滑動到位置4時,經過該方法將頁面切換到位置1,這樣給咱們的感受就是無限循環。.net
private int currentPosition2;
private void initData2() {
itemList2 = new ArrayList<>();
itemList2.add(R.drawable.ic_pic4);
itemList2.add(R.drawable.ic_pic1);
itemList2.add(R.drawable.ic_pic2);
itemList2.add(R.drawable.ic_pic3);
itemList2.add(R.drawable.ic_pic4);
itemList2.add(R.drawable.ic_pic1);
bannerAdapter2 = new BannerAdapter2(itemList2);
viewPager2.setAdapter(bannerAdapter2);
currentPosition2 = 1;
viewPager2.setCurrentItem(currentPosition2);
viewPager2.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int i, float v, int i1) {
}
@Override
public void onPageSelected(int i) {
currentPosition2 = i;
}
@Override
public void onPageScrollStateChanged(int state) {
//驗證當前的滑動是否結束
if (state == ViewPager.SCROLL_STATE_IDLE) {
if (currentPosition2 == 0) {
viewPager2.setCurrentItem(itemList2.size() - 2, false);//切換,不要動畫效果
} else if (currentPosition2 == itemList2.size() - 1) {
viewPager2.setCurrentItem(1, false);//切換,不要動畫效果
}
}
}
});
}
複製代碼
private Handler mHandler2 = new Handler();
@Override
protected void onResume() {
super.onResume();
//開始輪播
mHandler2.postDelayed(mLoopRunnable2, mDelayedTime);
}
@Override
protected void onPause() {
super.onPause();
//中止輪播
mHandler2.removeCallbacks(mLoopRunnable2);
}
複製代碼
private final Runnable mLoopRunnable2 = new Runnable() {
@Override
public void run() {
if (mIsAutoPlay) {
//方案二:多添兩條數據
currentPosition2 = viewPager2.getCurrentItem();
currentPosition2++;
//不須要爲了循環輪播來判斷是否到達最後一頁,在監聽器中已經爲咱們作了此操做
viewPager2.setCurrentItem(currentPosition2);
mHandler2.postDelayed(this, mDelayedTime);
} else {
mHandler2.postDelayed(this, mDelayedTime);
}
}
};
複製代碼
與方案一不一樣的地方就是當滑動到最後一個時,切換到下標爲1的頁面,當滑動下標爲0的頁面時,切換到最後一個
依然範特稀西 在文章中這樣說到:第二種方案在切換動畫的時候,由於當滑到位置4時,咱們經過setCurrentItem(int item,boolean smoothScroll)方法,來將其切換到位置1纔有了無限循環的效果,但爲了避免被發現,第二個參數smoothScroll設置爲false,這樣就沒有了切換動畫,致使生硬,因此不用這個。
原本沒想實現方案二(想着會一種方法就行),但好奇心使我想看下到底有多生硬,但沒有發現生硬的效果。由於咱們在onPageScrollStateChanged()方法裏監聽了動畫結束的狀態,因此當滑動到第四張,再次開啓一個週期的時候,咱們實際上是滑動到了第五張,就是咱們往尾部添加的那張圖片,此時是有動畫的,並非itemlist下標爲0的位置,並且在此監聽器中,當判斷其實最後一張的時候,咱們已經經過setCurrentItem()不帶動畫效果的方式偷偷的把它切換到下標爲1的位置了,因此在handler經過currentItem++方式再次滑動時,它滑動到的是下標爲2的圖片,也是帶效果的,因此不存在什麼生硬的效果。 如下我一共放了四張圖,你們能夠仔細看下效果:
以上就是最基本的方法來實現ViewPager的無限輪播的所有內容。具體代碼見Github。
其實咱們常見的Banner圖還有Indicator指示器(就是底部的小點),這個我用的其實仍是依然範特稀西自定義的Indicator,由於確實很好用,並且封裝的話也很簡單,雖然同樣的,可是我仍是想下一篇再記錄一下封裝的過程,讓本身加深下印象,下篇文章見。
Android ViewPager 無限輪播Integer.MAX_VALUE 爭議(看源碼)
ViewPager系列之 仿魅族應用的廣告BannerView
Android 使用ViewPager實現無限輪播出現空白bug緣由及解決方案(Integer.MAX_VALUE實現方式)