如何寫一個通用的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 ? : 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 = -;
protected static final int TYPE_ITEM = -;
protected static final int TYPE_FOOTER = -;
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 == ) {
throw new NullPointerException("header 資源ID尚未初始化");
} else {
holder = new BaseViewHolder(LayoutInflater.from(mContext).inflate(headerLayoutID, parent, false));
}
} else if (viewType == TYPE_FOOTER) {
if (footerLayoutID == ) {
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 - );
}
} 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 == )
return;
this.headerLayoutID = headLayoutID;
isHasHeader = true;
}
public List<M> getmLists() {
return mLists;
}
public void addFooter(@LayoutRes int footLayoutID) {
if (footLayoutID == )
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() - ? true : false;
}
protected boolean isHeaderPosition(int position) {
return position == ? true : false;
}
@Override
public int getItemCount() {
int count = mLists == null ? : 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