前言
上一篇我們一起封裝了一個還算簡單實用的 Adapter,可惜是 ListView、GridView 用的。在 RecyclerView 大行其道的如今,對應 Adapter 的使用簡單也是一個必然的需求。
正文
按照慣例,先來看看一個原始的 RecyclerViewAdapter 是怎麼寫的
RecyclerView.Adapter adapter = new RecyclerView.Adapter() {
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(context).inflate(layoutId, parent, false);
return new Holder(itemView);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
Holder holder = (Holder) viewHolder;
holder.textView.setText(dataSource.get(position));
holder.button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//TODO
}
});
}
@Override
public int getItemCount() {
return dataSource.size();
}
};
複製程式碼
private class Holder extends RecyclerView.ViewHolder {
public TextView textView;
public Button button;
public Holder(View itemView) {
super(itemView);
textView = itemView.findViewById(textViewId);
button = itemView.findViewById(buttonId);
}
}
複製程式碼
以上程式碼純屬盲打,如有錯誤敬請諒解。
大致上我們能看到,相對於 ListView,RecyclerView 幫我們實現了 Item 的複用邏輯,不需要我們再寫,但還是每次都要寫一個 ViewHolder 類。之前我們是呼叫一個自己寫的靜態 ViewHolder 類,但是 RecyclerView 需要的 ViewHolder 一定是 RecyclerView.ViewHolder 或其子類,所以我們只能繼承 RecyclerView.ViewHolder 來做封裝工作。
public class RecyclerViewHolder extends RecyclerView.ViewHolder {
public RecyclerViewHolder(View itemView) {
super(itemView);
}
public <T extends View> T getChildView(int id) {
return (T) itemView.findViewById(id);
}
}
複製程式碼
其實只是新增了一個 getChildView 的方法用來獲取子 View 而已。
Adapter 的封裝也很簡單,只是新增了一個繫結資料的抽象方法:
public abstract class RecyclerAdapter<T> extends RecyclerView.Adapter<RecyclerViewHolder> {
/**
* 由子類實現繫結方法
* @param holder
* @param data
* @param position
*/
protected abstract void onBindData(RecyclerViewHolder holder, T data, int position);
protected Context mContext;
protected List<T> mData;
protected LayoutInflater mInflater;
protected int layoutRes;
public RecyclerAdapter(Context context, List<T> data, int layoutRes) {
this.mData = data;
this.mContext = context;
this.layoutRes = layoutRes;
this.mInflater = LayoutInflater.from(mContext);
}
public void refresh(List<T> data) {
try {
this.mData = data;
notifyDataSetChanged();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new RecyclerViewHolder(mInflater.inflate(layoutRes, parent, false));
}
@Override
public void onBindViewHolder(RecyclerViewHolder holder, int position) {
onBindData(holder, mData.get(position), position);
}
@Override
public int getItemCount() {
return mData.size();
}
}
複製程式碼
我們來看看現在使用起來方不方便?
RecyclerAdapter adapter = new RecyclerAdapter<String>(this, dataSource, layoutRes) {
@Override
protected void onBindData(RecyclerViewHolder holder, String data, int position) {
TextView textView = holder.getChildView(textViewId);
Button button = holder.getChildView(buttonId);
textView.setText(data);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//TODO
}
});
}
};
複製程式碼
有木有一種很親切的感覺?終於又回到了一個方法實現 Adapter 的日子了 T_T ~~
小結
封裝之後才發現,其實 RecyclerView 的 Adapter 是可以封裝得比 ListView 更為簡潔易用的,這應該歸功於 RecyclerView 自身就實現了快取複用。
真·正文
還記得上一篇文章我就說過這還不是極簡嗎?仔細看上面的程式碼,每次重寫繫結方法的時候,幾乎都要先用 holder 的 getChild 方法獲取到 view 例項,再進行填充資料、新增監聽等操作。有沒有一種辦法能一步到位呢?
當然有!
先看程式碼
public class RecyclerViewHolder extends RecyclerView.ViewHolder {
public RecyclerViewHolder(View itemView) {
super(itemView);
}
public <T extends View> T getChildView(int id) {
return (T) itemView.findViewById(id);
}
public RecyclerViewHolder setText(int viewId, CharSequence charSequence) {
TextView textView = getChildView(viewId);
textView.setText(charSequence);
return this;
}
......
}
複製程式碼
可以看出,我們給 ViewHolder 新增了 setText 方法,傳入了 viewId 和我們想要設定的值,在方法內部去 getChildView 並進行了資料填充。同樣的,設定監聽等方法也可以這樣子去實現。
RecyclerAdapter adapter = new RecyclerAdapter<String>(this, dataSource, layoutRes) {
@Override
protected void onBindData(RecyclerViewHolder holder, String data, int position) {
holder.setText(textViewId, data)
.setOnClickListener(buttonId, new View.OnClickListener() {
@Override
public void onClick(View v) {
//TODO
}
});
}
};
複製程式碼
再安利一下 lambda 表示式
RecyclerAdapter adapter = new RecyclerAdapter<String>(this, dataSource, layoutRes) {
@Override
protected void onBindData(RecyclerViewHolder holder, String data, int position) {
holder.setText(textViewId, data)
.setOnClickListener(buttonId, (v) -> {/** TODO */});
}
};
複製程式碼
是不是感覺棒棒噠?很多常用的方法都可以放到 ViewHolder 中去,比如 setTextSize、setTextColor、setVisibility、setImageResource、setBackgroundColor 等。
因為篇幅的關係,原本計劃一起寫的 ItemType 處理很難再塞進來。再加上 databinding 模式下的 Adapter 封裝,感覺 Adapter 這個系列就夠我寫好一陣了,會不會有點狗血?
感謝
鴻揚大神:https://github.com/hongyangAndroid/baseAdapter 以及我自己的開源專案:https://github.com/neverwoodsS/zy-open