RecyclerView全面解析之二:做一個(gè)RecyclerView的萬(wàn)能適配器

寫(xiě)在前面

如果每次都直接寫(xiě)一個(gè)Adapter,實(shí)在是特別的繁瑣翼抠,代碼太多咙轩,所以我在仔細(xì)學(xué)習(xí)之后,也在網(wǎng)上大神的博客之中取了不少經(jīng)阴颖,現(xiàn)在準(zhǔn)備好好打造一個(gè)萬(wàn)能的適配器活喊。

之前我有一篇博客是寫(xiě)RecyclerView的基本使用的,我們現(xiàn)在就接著上一篇博客繼續(xù)量愧,一個(gè)暫時(shí)沒(méi)有優(yōu)化過(guò)的Adapter是這個(gè)樣子的:

package com.lay.laykypro.adapter.ClidFragment;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.lay.laykypro.R;

import java.util.HashMap;
import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;


public class MainClidRecyclerViewAdapter extends RecyclerView.Adapter<MainClidRecyclerViewAdapter.MainClidHolder> {

    private List<HashMap<String, Object>> mdataList;
    private Context mContext;
    private LayoutInflater mLayoutInflater;

    //設(shè)置接口對(duì)象
    private OnItemClickListener onItemClickListener;

    public MainClidRecyclerViewAdapter(List<HashMap<String, Object>> mdataList, Context mContext) {
        this.mdataList = mdataList;
        this.mContext = mContext;
        this.mLayoutInflater = mLayoutInflater.from(mContext);
    }

    @Override
    public MainClidHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = mLayoutInflater.inflate(R.layout.item_recyclerview_clidfragment, parent, false);
        MainClidHolder mainClidHolder = new MainClidHolder(view);

        return mainClidHolder;
    }

    @Override
    public void onBindViewHolder(MainClidHolder holder, final int position) {
            //設(shè)置數(shù)據(jù)綁定
        HashMap<String, Object> itemHashMap = mdataList.get(position);
        //拿到控件
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                onItemClickListener.onItemClick(position);
            }
        });
        holder.tvRecyclerview.setText((String)itemHashMap.get("type"));

    }

    public void setOnItemClickListener(OnItemClickListener onItemClickListener){
        this.onItemClickListener=onItemClickListener;

    }

    @Override
    public int getItemCount() {
        return mdataList.size();
    }

    class MainClidHolder extends RecyclerView.ViewHolder {
        @BindView(R.id.tv_recyclerview)
        TextView tvRecyclerview;


        public MainClidHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
        }
    }

    /**
     * 點(diǎn)擊事件接口
     */
    public interface OnItemClickListener{
        void onItemClick(int position);
    }


}

現(xiàn)在我們來(lái)進(jìn)行一步一步的優(yōu)化钾菊。寫(xiě)一個(gè)BaseAdapter 使得以后所有的Recycler都用得上帅矗,用的時(shí)候直接繼承就行了。

一煞烫、基本改造浑此。

目標(biāo):數(shù)據(jù)改為泛型,布局id直接在構(gòu)造其中傳遞滞详,將數(shù)據(jù)綁定回傳給子類(lèi)凛俱,讓子類(lèi)專(zhuān)心處理這個(gè)東西。
代碼如下:

package com.lay.laykypro.adapter.ClidFragment;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.List;

/**
 * 基本改造
 * @param <T>
 */
public abstract class BaseRecyclerViewAdapter<T> extends RecyclerView.Adapter<BaseRecyclerViewAdapter.ViewHolder> {
    protected List<T> tList;
    protected Context mContext;
    protected LayoutInflater mLayoutInflater;
    protected int layId;

    private OnItemClickListener mOnItemClickListener;

    public BaseRecyclerViewAdapter(List<T> tList, Context mContext, int layId) {
        this.tList = tList;
        this.mContext = mContext;
        this.layId=layId;
        mLayoutInflater=LayoutInflater.from(mContext);
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = mLayoutInflater.inflate(layId, parent, false);
        ViewHolder viewHolder = new ViewHolder(view);
        return viewHolder;
    }
    @Override
    public void onBindViewHolder(final BaseRecyclerViewAdapter.ViewHolder holder, final int position) {
        convert(holder,tList.get(position));
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
              mOnItemClickListener.onItemClick(position);
            }
        });

    }
    public void setOnItemClickListener(OnItemClickListener onItemClickListener){
        this.mOnItemClickListener=onItemClickListener;
        
    }

    @Override
    public int getItemCount() {
        return tList.size();
    }

   public abstract  void convert(ViewHolder holder,T itemData);


    public class ViewHolder extends RecyclerView.ViewHolder {

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

    }

    /**
     * 點(diǎn)擊事件接口
     */
    public interface OnItemClickListener{
        void onItemClick(int position);
    }



}

二茵宪、實(shí)例嘗試

帶入上一篇博客:RecyclerView全面解析之一:基本使用和分割線的設(shè)置 - 簡(jiǎn)書(shū) http://www.reibang.com/p/cfbb0c2e9ffc的代碼進(jìn)行嘗試。
代碼如下:

package com.lay.laykypro.adapter.ClidFragment;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.lay.laykypro.R;

import java.util.HashMap;
import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;


public class MainClidRecyclerViewAdapter extends BaseRecyclerViewAdapter<HashMap<String, Object>> {



    public MainClidRecyclerViewAdapter(List<HashMap<String, Object>> hashMaps, Context mContext, int layId) {
        super(hashMaps, mContext, layId);
    }

    @Override
    public void convert(ViewHolder holder, HashMap<String, Object> itemData) {
        TextView tvRecyclerView = (TextView) holder.itemView.findViewById(R.id.tv_recyclerview);
        tvRecyclerView.setText((String)itemData.get("type"));

    }
    



}

在這個(gè)地方我遇到了兩個(gè)報(bào)錯(cuò):
1.MainClidRecyclerViewAdapter 不是抽象的, 并且未覆蓋Adapter中的抽象方法onBindViewHolder(BaseRecyclerAdapter.ViewHolder,int)
2.方法不會(huì)覆蓋或?qū)崿F(xiàn)超類(lèi)型的方法
解決方法:Error:(13, 8) 錯(cuò)誤: xxx不是抽象的, 并且未覆蓋xxx中的抽象方法onBindViewHolder(BaseSimpleRecyclerAdapter.ViewHolder,int) - yql_running的博客 - CSDN博客 https://blog.csdn.net/yql_running/article/details/69054523

三瘦棋、優(yōu)化ViewHolder

之前是把所有的數(shù)據(jù)處理整理到了convert()方法中稀火,現(xiàn)在我們來(lái)優(yōu)化一下ViewHolder類(lèi)。

1.這里我們?cè)谑褂肕ap類(lèi)的時(shí)候進(jìn)行一下選擇赌朋,參考博客:
Android內(nèi)存優(yōu)化(使用SparseArray和ArrayMap代替HashMap) - 【博客地址永久遷移到】:http://zhengxiaoyong.me - CSDN博客 https://blog.csdn.net/u010687392/article/details/47809295

2.這里我們?cè)趯?xiě)setText方法的時(shí)候參考了一下:
String詳解, String和CharSequence區(qū)別, StringBuilder和StringBuffer的區(qū)別 (String系列之1) - 如果天空不死 - 博客園 http://www.cnblogs.com/skywang12345/p/string01.html

代碼如下:

 public class ViewHolder extends RecyclerView.ViewHolder {

        private SparseArray<View> mViewArray;

        public ViewHolder(View itemView) {
            super(itemView);
            mViewArray=new SparseArray<>();
        }

        /**
         * 通過(guò)id得到view
         * @param viewId
         * @param <V>
         * @return
         */
        public <V extends View>V getViewAtId(int viewId){
            View view = mViewArray.get(viewId);
            if(view==null){
                view=itemView.findViewById(viewId);
                mViewArray.put(viewId,view);
            }
            return (V)view;
        }

        /**
         * 設(shè)置文字
         * @param viewId
         * @param text
         * @return
         */
        public ViewHolder setText(int viewId,CharSequence text){
           TextView textView= getViewAtId(viewId);
           textView.setText(text);
           return  this;
        }

        /**
         * 通過(guò)網(wǎng)址設(shè)置圖片
         * @param viewId
         * @param imgUrl
         * @return
         */
        public ViewHolder setImageFromUrl(int viewId,String imgUrl){
           ImageView imageView= getViewAtId(viewId);
            Glide.with(imageView.getContext()).load(imgUrl).into(imageView);
            return this;
        }

        /**
         * 通過(guò)資源來(lái)設(shè)置圖片資源
         * @param viewId
         * @param resId
         * @return
         */
        public ViewHolder setImageFromRes(int viewId,int resId){
            ImageView imageView = getViewAtId(viewId);
            imageView.setImageResource(resId);
            return this;
        }

        /**
         * 設(shè)置控件透明度
         * @param viewId
         * @param visibility
         * @return
         */
        public ViewHolder setViewVisibility(int viewId,int visibility){
            View view = getViewAtId(viewId);
            view.setVisibility(visibility);
            return this;
        }


    }

優(yōu)化之后凰狞, MainClidRecyclerViewAdapter 代碼如下:

package com.lay.laykypro.adapter.ClidFragment;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.lay.laykypro.R;

import java.util.HashMap;
import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;


public class MainClidRecyclerViewAdapter extends BaseRecyclerViewAdapter<HashMap<String, Object>> {



    public MainClidRecyclerViewAdapter(List<HashMap<String, Object>> hashMaps, Context mContext, int layId) {
        super(hashMaps, mContext, layId);
    }

    @Override
    public void convert(ViewHolder holder, HashMap<String, Object> itemData) {
       holder.setText(R.id.tv_recyclerview,(String)itemData.get("type"));

    }

}

下一步,我們來(lái)考慮多布局沛慢。

四赡若、多布局接口的實(shí)現(xiàn)

我們經(jīng)常會(huì)看到列表的呈現(xiàn)形式并不是單一形式的,也就是item的多布局团甲,之前我們不斷封裝的過(guò)程中我們應(yīng)該可以領(lǐng)悟一個(gè)基本思想逾冬,就是不變的東西我們應(yīng)該把它丟到Base類(lèi)中去,只有具體的業(yè)務(wù)邏輯才應(yīng)該放到實(shí)現(xiàn)類(lèi)中來(lái)寫(xiě)躺苦。因此身腻,借由這個(gè)基本思想我們來(lái)看看什么才是多布局功能不變的部分。
多布局的基本邏輯是通過(guò)對(duì)item的具體數(shù)據(jù)分析匹厘,以此來(lái)判斷到底應(yīng)該得到哪一個(gè)item的布局嘀趟。
在這個(gè)邏輯中,通過(guò)每一個(gè)item的數(shù)據(jù)愈诚,得到具體布局是不變的部分她按,因此,接口可得炕柔,代碼如下:

  /**
     * 多布局接口
     * @param <T>
     */
    public interface  MultiTypeSupport<T>{
        int getLayoutId(T item);
    }

改寫(xiě)getItemViewType 方法酌泰,代碼如下:


    @Override
    public int getItemViewType(int position) {
        if (multiTypeSupport!=null){
            return multiTypeSupport.getLayoutId(tList.get(position));
        }
        return super.getItemViewType(position);
    }

在Adapter中設(shè)置多布局:

   public void setMultiTypeSupport(MultiTypeSupport<T> multiTypeSupport){
        this.multiTypeSupport=multiTypeSupport;
    }

改寫(xiě)onCreateViewHolder,代碼如下:

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        // 多布局支持
        if (multiTypeSupport != null) {
            mLayoutId = viewType;//這個(gè)就是getItemViewType方法的返回值匕累,我們直接使用布局Id來(lái)做返回值宫莱,可以省略設(shè)置靜態(tài)int值。
        }
        View view = mLayoutInflater.inflate(mLayoutId, parent, false);
        ViewHolder viewHolder = new ViewHolder(view);
        return viewHolder;
    }

在Fragment中來(lái)寫(xiě)具體的布局邏輯哩罪,代碼如下:

        mainClidRecyclerViewAdapter.setMultiTypeSupport(new BaseRecyclerViewAdapter.MultiTypeSupport<HashMap<String, Object>>() {
            @Override
            public int getLayoutId(HashMap<String, Object> item) {
                String type = (String) item.get("type");
                if(type.equals("horizontalScrollCard")){
                    return R.layout.item_recyclerview_clidfragment;
                }else if(type.equals("header5")){
                    return R.layout.item_recyclerview_nofragment;
                }


                return 0;
            }
        });

最后一個(gè)問(wèn)題授霸,我們應(yīng)該怎么在convert方法中實(shí)現(xiàn)對(duì)不同的布局進(jìn)行不同的數(shù)據(jù)加載呢巡验?畢竟我們這個(gè)方法的傳入?yún)?shù)可沒(méi)有ViewType。
回答:直接通過(guò)item數(shù)據(jù)來(lái)判斷就可以了碘耳。
至此邏輯理清显设,博客結(jié)束。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末辛辨,一起剝皮案震驚了整個(gè)濱河市捕捂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌斗搞,老刑警劉巖指攒,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異僻焚,居然都是意外死亡允悦,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)虑啤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)隙弛,“玉大人,你說(shuō)我怎么就攤上這事狞山∪疲” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵萍启,是天一觀的道長(zhǎng)总珠。 經(jīng)常有香客問(wèn)我,道長(zhǎng)勘纯,這世上最難降的妖魔是什么姚淆? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮屡律,結(jié)果婚禮上腌逢,老公的妹妹穿的比我還像新娘。我一直安慰自己超埋,他們只是感情好搏讶,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著霍殴,像睡著了一般媒惕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上来庭,一...
    開(kāi)封第一講書(shū)人閱讀 51,754評(píng)論 1 307
  • 那天妒蔚,我揣著相機(jī)與錄音,去河邊找鬼。 笑死肴盏,一個(gè)胖子當(dāng)著我的面吹牛科盛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播菜皂,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼贞绵,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了恍飘?” 一聲冷哼從身側(cè)響起榨崩,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎章母,沒(méi)想到半個(gè)月后母蛛,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡乳怎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年彩郊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舞肆。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡焦辅,死狀恐怖博杖,靈堂內(nèi)的尸體忽然破棺而出椿胯,到底是詐尸還是另有隱情,我是刑警寧澤剃根,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布哩盲,位于F島的核電站,受9級(jí)特大地震影響狈醉,放射性物質(zhì)發(fā)生泄漏廉油。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一苗傅、第九天 我趴在偏房一處隱蔽的房頂上張望抒线。 院中可真熱鬧,春花似錦渣慕、人聲如沸嘶炭。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)眨猎。三九已至,卻和暖如春强经,著一層夾襖步出監(jiān)牢的瞬間睡陪,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留兰迫,地道東北人信殊。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像逮矛,于是被迫代替她去往敵國(guó)和親鸡号。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

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