之前總結(jié)了實(shí)現(xiàn)輪播圖的各種方式.不過總的來說,基本都是通過viewpager來實(shí)現(xiàn).并且效果也不能稱為 完美 . 在IOS中,實(shí)現(xiàn)輪播圖的方式更為豐富,什么兩圖輪播,三圖輪播... 所以偶爾我也思考是否還有其它方式可以實(shí)現(xiàn).
Google官方提供的可滑動控件無非就是ScrollView,ListView,RecyclerView等等. 其中RecyclerView就是專門處理視圖回收的,那么是否可以使用RecyclerView來實(shí)現(xiàn)呢?
使用RecyclerView來輪播,首先需要解決兩個問題
- 一次只能滑動一頁
- 不能滑一半,每次停止的時候,都要讓當(dāng)前頁居中顯示
本來要實(shí)現(xiàn)這個功能是挺麻煩的,幸好在 RecyclerView-24.2.0(大概是)開始,新增了一個類可以幫助我們完成90%,那就是 LinearSnapHelper.
LinearSnapHelper的使用很簡單:
new LinearSnapHelper().attachToRecyclerView(recyclerView);
只需要上面這行代碼,RecyclerView就能實(shí)現(xiàn)類似ViewPager的功能,在滑動停止的時候,讓某頁居中顯示.唯一的不同,就是RecyclerView一次可以滑動很多頁.
其實(shí)這樣就已經(jīng)完全足夠了,當(dāng)然如果你一定想每次都只能滑動一頁,那么就需要重寫一下 LinearSnapHelper 的 findTargetSnapPosition 方法. findTargetSnapPosition 就是 LinearSnapHelper 通過當(dāng)前滑動速度,計算最終定位position的方法.
private class PagerSnapHelper extends LinearSnapHelper {
@Override
public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX, int velocityY) {
int targetPos = super.findTargetSnapPosition(layoutManager, velocityX, velocityY);
final View currentView = findSnapView(layoutManager);
if(targetPos != RecyclerView.NO_POSITION && currentView != null){
int currentPostion = layoutManager.getPosition(currentView);
int first = ((LinearLayoutManager)layoutManager).findFirstVisibleItemPosition();
int last = ((LinearLayoutManager)layoutManager).findLastVisibleItemPosition();
currentPostion = targetPos < currentPostion ? last : (targetPos > currentPostion ? first : currentPostion);
targetPos = targetPos < currentPostion ? currentPostion - 1 : (targetPos > currentPostion ? currentPostion + 1 : currentPostion);
}
return targetPos;
}
}
在上面的代碼中,稍微耍了點(diǎn)小心思,就是通過原來計算的結(jié)果,如果是往前滑動,那么就重新賦值為當(dāng)前位置 - 1 ; 如果是往后滑動,那就則重新賦值為當(dāng)前位置 + 1. 到這里,RecyclerView的效果就完全和ViewPager一致了.
因?yàn)镽ecyclerView內(nèi)部完美的處理了視圖的回收,所以實(shí)現(xiàn)輪播圖的思路也就很明確了:
給適配器設(shè)置為無限大,
然后將初始位置通過 scrollToPosition() 定位在中間某個地方,完全不用擔(dān)心像使用ViewPager一樣的ANR問題,
-
通過定時器,每隔一定事件調(diào)用
// 向后翻動一頁 recyclerView.smoothScrollToPosition(++currentIndex); 刷新的時候,通過 notifyDataSetChanged()更新數(shù)據(jù),不用每次再重新設(shè)置適配器了.
其它的思路,就和 全面總結(jié)輪播圖 中的最終方式一樣了,這里不再重復(fù).
效果也和之前非常類似:
