如何爲Recyclerview寫一個通用的adapter

如何寫一個通用的RecyclerView.adapter

項目源碼地址https://github.com/jiang111/SuperRecyclerViewAdapter
歡迎star和fork

隨着需求的不斷修改,在項目中越來越流行使用recyclerview。
那麼如何構建出一個通用的adapter來提高我們的快速開發能力。
下面我們就來看看如何構建出一個完善的baseadapter。

1.分析

想要構建出一個很nice的adapter,那你必須得熟悉recyclerview.adapter的執行流程,以及一些常用的方法吧。 通過分析,我們知道adapter中幾個主要的方法。

*1. onCreateViewHolder() //用來創建一個viewholder(viewholder必須繼承recyclerview.viewholder)

*2. onBindViewHolder() //用來根據當前item的位置進行數據綁定。

*3. getItemViewType() //返回你當前的adapter裏有幾種item

*4. getItemCount() //返回item的總個數

*5. 還有就是ViewHolder這個類 ,這個類主要用於拿到item中的相關控件

2.實戰

既然 viewholder的作用主要是拿到item中相關控件, 那我們完全可以寫一個通用的viewholder然後提供一個getview()的方法,讓adapter去拿相應的view.進行設置值。
看代碼


public class BaseViewHolder extends RecyclerView.ViewHolder {

    protected final SparseArray<View> mViews;
    protected View mConvertView;


    public BaseViewHolder(View itemView) {
        super(itemView);
        mViews = new SparseArray<>();
        mConvertView = itemView;
    }


    /**
     * 通過控件的Id獲取對應的控件,如果沒有則加入mViews,則從item根控件中查找並保存到mViews中
     *
     * @param viewId
     * @return
     */
    public <T extends View> T getView(@IdRes int viewId) {
        View view = mViews.get(viewId);
        if (view == null) {
            view = mConvertView.findViewById(viewId);
            mViews.put(viewId, view);
        }
        return (T) view;
    }

    public View getmConvertView() {
        return mConvertView;
    }


}

下面我們的adapter只需要使用BaseViewHolder,就可以了, 不需要在新建viewholder了。

public abstract class BaseAdapter<M> extends RecyclerView.Adapter<BaseViewHolder> {


    protected List<M> mLists;
    protected Context mContext;
    protected int layoutID;

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

    private OnItemClickListener onItemClickListener;

    public BaseAdapter(List<M> mLists, Context mContext, int layoutID) {
        this.mLists = mLists;
        this.mContext = mContext;
        this.layoutID = layoutID;
    }

    public List<M> getmLists() {
        return mLists;
    }

    @Override
    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        BaseViewHolder holder = new BaseViewHolder(LayoutInflater.from(mContext).inflate(layoutID, parent, false));
        return holder;
    }

    @Override
    public void onBindViewHolder(BaseViewHolder holder, final int position) {
        if (onItemClickListener != null) {
            holder.getmConvertView().setClickable(true);
            holder.getmConvertView().setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    onItemClickListener.onItemClick(position);
                }
            });
        }
        onBindView(holder, position);
    }

    protected abstract void onBindView(BaseViewHolder holder, int position);

    @Override
    public int getItemCount() {
        return mLists == null ? 0 : mLists.size();
    }


}

onCreateViewHolder方法我們以及寫好了,只需要再暴露出onBindView這個抽象方法即可。
然後我們在我們的activity中。

mRecyclerView.setAdapter(new BaseAdapter<Character>(mLists, this, R.layout.item_main) {
            @Override
            protected void onBindView(BaseViewHolder holder, final int position) {
                TextView mTitle = holder.getView(R.id.item_tv);
                mTitle.setText(this.getmLists().get(position) + "");

            }

        });

這樣的adapter不包含head和foot的。
那我們再寫一個包含head和foot的adapter


public abstract class BaseHeadFootAdapter<M> extends RecyclerView.Adapter<BaseViewHolder> {


    protected List<M> mLists;
    protected Context mContext;
    protected int layoutID;
    private boolean isHasHeader = false;
    private boolean isHasFooter = false;

    private int headerLayoutID;
    private int footerLayoutID;


    protected static final int TYPE_HEADER = -2;
    protected static final int TYPE_ITEM = -1;
    protected static final int TYPE_FOOTER = -3;

    public BaseHeadFootAdapter(List<M> mLists, Context mContext, int layoutID) {
        this.mLists = mLists;
        this.mContext = mContext;
        this.layoutID = layoutID;
    }

    public BaseHeadFootAdapter(int layoutID, Context mContext, List<M> mLists, int headerLayoutID, int footerLayoutID) {
        this.layoutID = layoutID;
        this.mContext = mContext;
        this.mLists = mLists;
        addHeader(headerLayoutID);
        addFooter(footerLayoutID);
    }

    @Override
    public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        BaseViewHolder holder;
        if (viewType == TYPE_HEADER) {
            if (headerLayoutID == 0) {
                throw new NullPointerException("header 資源ID尚未初始化");
            } else {
                holder = new BaseViewHolder(LayoutInflater.from(mContext).inflate(headerLayoutID, parent, false));
            }
        } else if (viewType == TYPE_FOOTER) {
            if (footerLayoutID == 0) {
                throw new NullPointerException("footer 資源ID尚未初始化");
            } else {
                holder = new BaseViewHolder(LayoutInflater.from(mContext).inflate(footerLayoutID, parent, false));
            }
        } else {
            holder = new BaseViewHolder(LayoutInflater.from(mContext).inflate(layoutID, parent, false));
        }
        return holder;
    }

    @Override
    public void onBindViewHolder(BaseViewHolder holder, final int position) {
        if (isHasFooter) {
            if (isFooterPosition(position)) {
                onBindFooterView(holder, position);
                return;
            }
        }
        if (isHasHeader) {
            if (isHeaderPosition(position)) {
                onBindHeaderView(holder, position);
                return;
            } else {
                onBindView(holder, position - 1);
            }
        } else {
            onBindView(holder, position);
        }

    }

    protected abstract void onBindHeaderView(BaseViewHolder holder, int position);

    protected abstract void onBindFooterView(BaseViewHolder holder, int position);

    protected abstract void onBindView(BaseViewHolder holder, int position);

    public void addHeader(@LayoutRes int headLayoutID) {
        if (headLayoutID == 0)
            return;
        this.headerLayoutID = headLayoutID;
        isHasHeader = true;
    }

    public List<M> getmLists() {
        return mLists;
    }

    public void addFooter(@LayoutRes int footLayoutID) {
        if (footLayoutID == 0)
            return;
        this.footerLayoutID = footLayoutID;
        isHasFooter = true;
    }


    @Override
    public int getItemViewType(int position) {

        int viewType = TYPE_ITEM;
        if (isHasHeader) {
            if (isHeaderPosition(position)) {
                viewType = TYPE_HEADER;
            }
        }
        if (isHasFooter) {
            if (isFooterPosition(position)) {
                viewType = TYPE_FOOTER;
            }
        }
        return viewType;
    }


    protected boolean isFooterPosition(int position) {
        return position == getItemCount() - 1 ? true : false;

    }

    protected boolean isHeaderPosition(int position) {
        return position == 0 ? true : false;
    }

    @Override
    public int getItemCount() {
        int count = mLists == null ? 0 : mLists.size();
        if (isHasFooter) {
            count++;
        }
        if (isHasHeader) {
            count++;
        }
        return count;
    }


}

好了,這樣我們通用的recyclerview的adapter就寫好了。但是這樣有個問題,如果我要想加載不同的佈局,那怎麼實現呢,顯然BaseAdapter是不夠用的,給大家一個思路, 如果要加載不同的佈局,顯然需要多個LayoutID和多個ViewType,既然他們都是int類型, 那完全可以把LayoutID作爲adapter的ViewType來實現,然後在oncreateView的時候需要壓入的layout直接拿viewType來使用就可以了。該思路實現的具體代碼在 https://github.com/jiang111/SuperRecyclerViewAdapter

歡迎star和fork

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章