今天要跟大家安利一種封裝方式,保證只用100行代碼就能擼一個(gè)列表頁(yè)面掩浙。
來(lái)上圖:
相關(guān)說(shuō)明:
- 該列表頁(yè)可以是Activity谣妻,也可以是Fragment
- 該列表頁(yè)使用RecyclerView鳞滨,所以支持列表累盗,網(wǎng)格寒矿,瀑布流
- 該列表頁(yè)支持下拉刷新,自動(dòng)加載更多
- 該列表需extends BaseListActivity或BaseListFragment
之前Stay寫(xiě)過(guò)一篇RecyclerView再封裝若债,本篇是對(duì)該篇的詳細(xì)解釋符相。
本文難度適中,沒(méi)有過(guò)多的算法拆座,純粹是利用Android提供的API與一些設(shè)計(jì)模式相結(jié)合做的封裝主巍。
沒(méi)什么好解釋的了,老司機(jī)要開(kāi)車?yán)才泊眨蔚卧兴鳌!?/p>
首先來(lái)看看我們這個(gè)列表SampleListActivity, 它是繼承BaseListActivity
public class SampleListActivity extends BaseListActivity<String>
注意躏碳,此處有泛型搞旭。這里的T就是你用在列表數(shù)據(jù)List<T>。為什么要這樣寫(xiě)菇绵,先埋個(gè)坑肄渗,一會(huì)再填。
在SampleListActivity中
@Override
protected BaseViewHolder getViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.activity_sample_list_item, parent, false);
return new SampleViewHolder(view);
}
@Override
protected ILayoutManager getLayoutManager() {
return new MyGridLayoutManager(getApplicationContext(), 3);
}
class SampleViewHolder extends BaseViewHolder {
}
子類SampleListActivity只需要做以上實(shí)現(xiàn)
- 指定列表類型:linear, grid, stagger
- 指定item對(duì)應(yīng)的ViewHolder
- ViewHolder的數(shù)據(jù)綁定
ok咬最,只要完成這些事情翎嫡,一個(gè)妹子列表就出來(lái)了。開(kāi)擼吧永乌。
好奇的你不會(huì)就此滿足惑申。Adapter哪去了?RecyclerView呢翅雏?我怎么沒(méi)看到它們圈驼。嗯哼~
BaseListActivity有個(gè)兒子叫SampleListActivity, 兒子非常努力,憑借自己的天賦找到了組織望几,終于绩脆,3秒后,兒子拿著請(qǐng)求來(lái)的數(shù)據(jù)加上老爹給的框架擼上了妹紙橄抹。注:妹紙圖來(lái)自gank.io
就是這樣一個(gè)情況靴迫,除了請(qǐng)求數(shù)據(jù)以及制定每個(gè)Item的UI樣式,其他的都由父類完成楼誓。
是不是好奇BaseListActivity中封裝了些什么玉锌?我們進(jìn)去看看吧。
其實(shí)SampleListActivity還有個(gè)爺爺慌随,不過(guò)這不重要芬沉。我們看躺同,在父類中定義了ArrayList<T>, Adapter, Recycler. 并且對(duì)Recycler, Adapter做了初始化,為什么要這樣寫(xiě)丸逸?老司機(jī)帶你看一下系統(tǒng)源碼就知道了蹋艺。
android.app.ListActivity
這里實(shí)際上參考了系統(tǒng)ListActivity的初衷,將ListView(這里是RecyclerView)封裝起來(lái)黄刚,并且定義一個(gè)默認(rèn)layout捎谨,當(dāng)列表頁(yè)非常簡(jiǎn)單時(shí),子類只需要綁定data就可以完成UI顯示憔维。
Stay這里做的BaseListActivity要更內(nèi)聚一些涛救。因?yàn)槲覀儗ist<T>定義在父類,所以在Adapter的getItemCount中可以直接做返回业扒。不需要子類明確指定size检吆。至于其它父類定義不了的,比如onCreateViewHolder, onBindViewHolder程储,可以讓子類實(shí)現(xiàn)蹭沛。
還有父類預(yù)定義了列表樣式,默認(rèn)為L(zhǎng)inearLayoutManager章鲤,如果子類不想要摊灭,直接重寫(xiě)方法就可以了。
其實(shí)這個(gè)父類代碼也不多败徊,90行帚呼。父類有父類的想法,為了兒子能自己獨(dú)立成長(zhǎng)皱蹦,只能提供一些最基礎(chǔ)的框架煤杀,至于兒子以后能干嘛,那是兒子的事情根欧。
雖然父類做的事情不多怜珍,但是能給的都給了端蛆,它做的最正確的事凤粗,就是早年還做了一套封裝,它叫PullRecycler今豆。這個(gè)PullRecycler還挺給力的嫌拣,可以下拉刷新,自動(dòng)加載更多呆躲,支持三種LayoutManager异逐。
其實(shí)這個(gè)PullRecycler沒(méi)有多難做,也就是一個(gè)SwipeRefreshLayout+RecyclerView插掂。當(dāng)然難點(diǎn)還是有的灰瞻。下拉刷新是SwipeRefreshLayout實(shí)現(xiàn)的腥例,但是自動(dòng)加載更多有三個(gè)坑。
-
判斷是否需要加載更多酝润,是通過(guò)onScrollListener來(lái)做的燎竖,你需要拿屏幕中最后一個(gè)顯示的item posistion去跟totalCount比對(duì)。但是在StaggerLayoutManager中要销,拿到的是一個(gè)數(shù)組构回。其它LayoutManager拿到的是int。這就坑了疏咐,不統(tǒng)一纤掸,很多github上的RecyclerView封裝都是通過(guò)instanceOf來(lái)強(qiáng)轉(zhuǎn)的,我不太喜歡浑塞。所以我就定義了一個(gè)接口ILayoutManager借跪,讓每個(gè)LayoutManager去實(shí)現(xiàn)一個(gè)統(tǒng)一的findLastVisiblePosition()
-
加載更多footer需要自成一行,但在grid和stagger模式下酌壕,這個(gè)就比較麻煩了垦梆。GridLayoutManager還簡(jiǎn)單一點(diǎn),直接看源碼的類注釋你就能知道如何做仅孩。SpanSizeLookup托猩,如果為footer,那就返回1辽慕,代表占滿整個(gè)寬度京腥。
-
以前ListView可以添加footer船侧,但是RecyclerView沒(méi)有欠气,你得自己在adapter中做判斷,如果有footer镜撩,那itemCount要+1预柒。所以我又將Adapter抽出來(lái),做成BaseListAdapter袁梗,是否顯示footer宜鸯,判斷是否是stagger模式下footer,給一個(gè)默認(rèn)footerViewHolder遮怜,如果子類不滿意還能再重寫(xiě)淋袖。
啊啊啊,開(kāi)車好累锯梁,老司機(jī)得歇會(huì)即碗。
附上源碼github焰情,自己拿去開(kāi)吧。直接可以運(yùn)行剥懒,另外還封裝了BaseSectionListActivity, 帶section header的sample烙样。