RecyclerView多類型狀態(tài)下寫個優(yōu)雅的通用Adapter

相關(guān)源碼和示例

源碼參考:
https://github.com/aesean/ApiDemo/blob/master/app/src/main/java/com/aesean/apidemo/widget/recyclerview/AbsMultiTypeAdapter.java

https://github.com/aesean/ApiDemo/blob/master/app/src/main/java/com/aesean/apidemo/widget/recyclerview/MultiTypeAdapterImpl.java

使用示例:
https://github.com/aesean/ApiDemo/blob/master/app/src/main/java/com/aesean/apidemo/activity/recyclerview/MultiTypeRecyclerViewActivity.java

RecyclerView面對多類型Adapter時候面臨的問題

RecyclerView出來這么久了相信應(yīng)該大家都在用了。使用基本三步憔鬼。
1龟劲、創(chuàng)建一個ViewHolder類胃夏。
2、創(chuàng)建一個Adapter昌跌。
3仰禀、向Adapter添加或者刪除數(shù)據(jù)。
當(dāng)然如果大家都是在用單ViewHolder的Adapter上面的代碼寫起來還是很簡單的蚕愤,沒什么問題答恶。但是如果遇到多類型ViewType怎么辦?
常規(guī)寫法大家會在Adapter中復(fù)寫

public int getItemViewType(int position) {
      // 通過postion拿到對應(yīng)的數(shù)據(jù)审胸,然后處理返回不同的Type
}

然后在onCreateViewHolder方法中根據(jù)不同的ViewType返回不同的ViewHolder亥宿。在onBindViewHolder進行數(shù)據(jù)綁定的時候,再判斷不同的ViewHolder進行不同的綁定處理砂沛。
寫的多了烫扼,會發(fā)現(xiàn)這里有個問題,Adapter本身要求一個ViewHolder的范型碍庵,單ViewHolder這里直接寫對應(yīng)的ViewHolder就行了捌袜,那多ViewType怎么辦薄辅?寫B(tài)aseViewHolder嗎此蜈?onBindViewHolder時候還是需要繼續(xù)判斷ViewHolder類型适荣。其實這時候Adapter本身要傳的ViewHolder范型其實已經(jīng)沒什么意義了。
這還不是最主要的苹享,如果項目復(fù)雜了双絮,大家用RecyclerView多了,會發(fā)現(xiàn)得问,不同界面經(jīng)常會遇到復(fù)用ViewHolder的情況囤攀。經(jīng)常會遇到界面0是ViewHolder0,ViewHolder2宫纬,ViewHolder4的組合焚挠。界面1是ViewHolder2,ViewHolder3的組合漓骚,界面2是ViewHolder0蝌衔,ViewHolder4的組合。這時候我們是每個界面都寫一個Adapter嗎蝌蹂?通常大家都會這么處理噩斟,寫的多了會發(fā)現(xiàn),Adapter在寫大量重復(fù)代碼叉信。那怎么辦亩冬?
主要是基于這種需求。所以需要用一種優(yōu)雅的方式解決Adapter需要處理多個ViewHolder硼身,而且還需要ViewHolder在不同界面能非常簡單方便的被復(fù)用硅急。

思路

我們要做什么?我們要做的就是把盡量多的重復(fù)邏輯封裝起來佳遂,避免重復(fù)勞動营袜。另外就是盡可能讓復(fù)用簡單,屬于誰的代碼就讓誰處理丑罪。

既然Adapter存在綁定多個不同ViewHodler的情況荚板,那我們最好有個通用的Adapter,這個Adapter能處理任意多個不同ViewHolder的create和bind吩屹,然后對應(yīng)的界面自己去注冊自己需要的ViewHolder跪另,你需要什么ViewHolder你就注冊什么ViewHolder。
我們現(xiàn)在重點關(guān)注Adapter的onCreateViewHolder和onBindViewHolder方法煤搜。
onCreateViewHolder是用來創(chuàng)建ViewHolder的免绿,參數(shù)只有ViewGroup和ViewType,ViewGroup是讓我們用來填充ViewHolder進去的擦盾。實際能用來區(qū)分不同ViewHolder的就只有ViewType了嘲驾。
那ViewType是從哪里來的?是從getItemViewType來的迹卢。getItemViewType可以通過position拿到我們的數(shù)據(jù)源辽故,然后通過判斷數(shù)據(jù)源的類類型來判斷它是什么ViewType。

好腐碱,現(xiàn)在我們需要一個Adapter必定有的保存數(shù)據(jù)的數(shù)據(jù)源誊垢。這里我們選用List(注意必須是個有序List,而且這個List會被經(jīng)常讀取數(shù)據(jù)症见,ArrayList讀取效率非常高喂走,所以這里用ArrayList是最好的選擇。)筒饰。因為我們需要綁定多個ViewHolder缴啡,所以這里L(fēng)ist的范型,需要是Object瓷们。就是支持任意類型业栅。

回到getItemViewType,從getItemViewType的postion谬晕,我們拿到了List中保存的Data碘裕,那我們怎么知道這個Data要被綁定到什么ViewHolder上呢?所以需要有個register方法來注冊ViewHolder攒钳。那這個方法參數(shù)是什么帮孔?是ViewHolder和ViewHolder對應(yīng)的Data。又因為我們ViewHolder是被onCreateViewHolder時候創(chuàng)建的,所以這個肯定是不能傳實例的文兢。Data也不能是實例晤斩,Data應(yīng)該是被調(diào)用處(通常是Http/Https Api返回)創(chuàng)建實例。所以這里兩個參數(shù)應(yīng)該都是類類型姆坚。

    public void register(Class<?> dataType, Class<? extends ViewHolder> viewType)

好了澳泵,我們把注冊的類類型保存在一個Map中。用來記錄每個數(shù)據(jù)類型對應(yīng)的ViewHolder兼呵。又因為getItemViewType返回的是個int來區(qū)分ViewType兔辅,所以我們需要把Data的類類型映射為int,怎么映射击喂?非常簡單用hashCode维苔。所以這個紀(jì)錄data與View綁定關(guān)系的Map的key是個Int保存Data類類型的hashCode,value是ViewHolder的類類型懂昂。

再回到onCreateViewHolder方法介时,通過傳過來的int viewType,我們?nèi)ノ覀儽4嬗成潢P(guān)系的mBindMap中拿到這個ViewType對應(yīng)的ViewHolder的類類型忍法。然后我們就可以通過反射來創(chuàng)建ViewHolder對象了潮尝。

這里注意還有個問題。通常我們寫ViewHolder可能都會寫成下面這樣:

    public static class SimpleViewHolder extends RecyclerView.ViewHolder{

        public SimpleViewHolder(View itemView) {
            super(itemView);
        }
    }

然后在onCreateViewHolder中通過類似下面的代碼創(chuàng)建SimpleViewHolder饿序。

View view =  LayoutInflater.from(context).inflate(R.layout.view_holder_simple,parent,false);
ViewHolder viewHodler = new SimpleViewHolder(view);

其實個人認(rèn)為這是非常不好的寫法勉失。為什么要讓調(diào)用者來創(chuàng)建屬于SimpleViewHolder的itemView?誰的事情交給誰做原探。SimpleViewHolder應(yīng)該對應(yīng)什么View乱凿,SimpleViewHolder自己最清楚。所以我們可以對SimpleViewHolder進行改造咽弦。

    public static class SimpleViewHolder extends RecyclerView.ViewHolder{

        public SimpleViewHolder(ViewGroup parent) {
            super(LayoutInflater.from(parentView.getContext()).inflate(R.layout.simple_view_holder, parentView, false));
        }
    }

這樣onCreateViewHolder就完全不關(guān)心SimpleViewHolder應(yīng)該對應(yīng)什么View徒蟆,這個應(yīng)該讓SimpelViewHolder自己決定。

回到前面onCreateViewHolder創(chuàng)建ViewHolder的地方型型。這時候我們要約定好段审,被注冊的ViewHolder必須有個參數(shù)為ViewGroup的構(gòu)造方法。
然后繼續(xù)下一步onBindViewHolder闹蒜,onBindViewHolder兩個參數(shù)寺枉,ViewHolder實例和positon(Data實例),我們要做的就是把Data設(shè)置到ViewHolder上绷落,一個ViewHolder該如何綁定數(shù)據(jù)姥闪,肯定是ViewHolder自身最清楚,所以這里ViewHolder必須有個綁定數(shù)據(jù)的方法砌烁。這里我們需要抽象一個ViewHolder的基類筐喳,所有ViewHolder都必須繼承這個ViewHolder,然后這個ViewHodler基類,至少有個綁定數(shù)據(jù)的方法避归,我們暫時取名叫bindData荣月。
好到這里我們基本就完成了,不同ViewType綁定的處理槐脏。
梳理下思路:
第一步:register注冊需要的ViewHolder和ViewHolder對應(yīng)的數(shù)據(jù)Data喉童。
第二步:getViewType返回Data對應(yīng)的viewType
第三步:onCreateViewHolder通過ViewType拿到對應(yīng)的ViewHolder類類型撇寞,通過反射創(chuàng)建ViewHolder實例顿天。
第四步:拿到postion對應(yīng)的數(shù)據(jù),設(shè)置給ViewHolder蔑担。
思路有了剩下的就是填充代碼了牌废。

Head Content和Foot的處理

在實際寫的時候考慮到我們經(jīng)常會遇到需要區(qū)分Head Content和Foot的情況。我們前面那些關(guān)于不同ViewType的處理其實跟Head Content Foot是不沖突的啤握。為了方便擴展實現(xiàn)鸟缕。所以實際寫的時候抽象了一個AbsMultiTypeAdapter出來,AbsMultiTypeAdapter處理了不同Type的創(chuàng)建和綁定排抬。但是AbsMultiTypeAdapter不關(guān)心數(shù)據(jù)源懂从,所有需要調(diào)用數(shù)據(jù)源的方法(包含data的方法,比如addData蹲蒲,removeData)全部改為抽象方法番甩,讓子類去實現(xiàn)。這樣子類可以根據(jù)需要來實現(xiàn)帶有Head Content和Foot的數(shù)據(jù)源届搁。
在MultiTypeAdapterImpl中實際實現(xiàn)了完整的MultiTypeAdapter的功能缘薛。MultiTypeAdapterImpl實現(xiàn)了任意多個類型的Adapter。具體實現(xiàn)方式就是把之前我們習(xí)慣的數(shù)據(jù)源從List變成List套List的List<List<Object>>卡睦,外層List保存不同的Type宴胧,內(nèi)層List是實際對應(yīng)Type包含的數(shù)據(jù)。然后就是處理Postion與Type Offset的相互轉(zhuǎn)換了表锻。詳細(xì)的不再細(xì)說恕齐。

最終效果

上面說了那么多,那最終實際效果是什么瞬逊?我們實現(xiàn)了一個MultiTypeAdapterImpl显歧。它支持任意的ViewHolder以及它們的組合。
使用時候第一步必須通過register方法注冊你需要的ViewHolder和這個ViewHolder對應(yīng)的數(shù)據(jù)類型码耐。當(dāng)然ViewHolder必須繼承AbsViewHolder追迟。然后就可以通過addItem方法給Adapter添加數(shù)據(jù)了,剩下的itemView createViewHolder bindViewHolder骚腥,MultiTypeAdapterImpl會幫你處理敦间。
相當(dāng)于你的Activity里寫的代碼非常少。只有register和addItem。ViewHolder復(fù)用非常輕松簡單廓块。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末厢绝,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子带猴,更是在濱河造成了極大的恐慌昔汉,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拴清,死亡現(xiàn)場離奇詭異靶病,居然都是意外死亡,警方通過查閱死者的電腦和手機口予,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門娄周,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人沪停,你說我怎么就攤上這事煤辨。” “怎么了木张?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵众辨,是天一觀的道長。 經(jīng)常有香客問我舷礼,道長鹃彻,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任且轨,我火速辦了婚禮浮声,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘旋奢。我一直安慰自己泳挥,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布至朗。 她就那樣靜靜地躺著屉符,像睡著了一般。 火紅的嫁衣襯著肌膚如雪锹引。 梳的紋絲不亂的頭發(fā)上矗钟,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天,我揣著相機與錄音嫌变,去河邊找鬼吨艇。 笑死,一個胖子當(dāng)著我的面吹牛腾啥,可吹牛的內(nèi)容都是我干的东涡。 我是一名探鬼主播冯吓,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼疮跑!你這毒婦竟也來了组贺?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤祖娘,失蹤者是張志新(化名)和其女友劉穎失尖,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體渐苏,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡掀潮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了整以。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胧辽。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖公黑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情摄咆,我是刑警寧澤凡蚜,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站吭从,受9級特大地震影響朝蜘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜涩金,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一谱醇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧步做,春花似錦副渴、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至将鸵,卻和暖如春勉盅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背顶掉。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工草娜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人痒筒。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓宰闰,卻偏偏與公主長得像嗜暴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子议蟆,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容