Google推出控件RecyclerView推出以後。ListView逐漸被RecyclerView取代。
那麼RecyclerView 和ListView相比有哪些優點呢?
- RecylerView封裝了viewholder的回收複用,也就是說RecylerView标準化了ViewHolder,編寫Adapter面向的是ViewHolder而不再是View了,複用的邏輯被封裝了,寫起來更加簡單。
- 高度的解耦,擴充性強,針對一個Item的顯示RecylerView專門抽取出了相應的類,來控制Item的顯示,使其的擴充性非常強。
- 可以控制Item增删的動畫,可以通過ItemAnimator這個類進行控制,當然針對增删的動畫,RecylerView有其自己預設的實作。
前面講到RecylerView封裝了viewholder的回收複用,在我們使用RecyclerView就會用到RecyclerView.Adapter,而每建立一個adapter就要建立一個RecyclerView.ViewHolder類,實際開發中由于會用到不止一個RecyclerView,不斷重複以上步奏使得代碼臃腫,實作起來非常費勁。大大的增加了程式員的代碼工作量。這顯然不是我們想要的。
對Adapter封裝進行需求分析
繼承RecyclerView.BaseAdapter需要實作3個抽象方法:
1. onCreateViewHolder
2. onBindViewHolder
3. getItemCount
需要一個RecyclerView.ViewHolder的實作類
其中getItemCount()可以構造方法傳入資料List,直接實作。
而onCreateViewHolder則需要傳回一個ViewHolder的對象,而建立ViewHolder對象需要一個子條目的根View。考慮到一個Adapter可能有多個activity用到,而布局可能不同,是以我們在構造方法中傳入layoutId用來建立view。
Adapter簡單實作
public class SimpleQuickAdapter<B> extends RecyclerView.Adapter<SimpleQuickAdapter.SimpleViewHolder> {
private int layoutId;
protected List<B> mData;
/**
* @param layoutId 子條目的布局
* @param list 資料
*/
public SimpleQuickAdapter(@LayoutRes int layoutId, List<B> list) {
this.layoutId = layoutId;
this.mData = list != null ? list : new ArrayList<B>();
}
@Override
public SimpleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return null;
}
@Override
public void onBindViewHolder(SimpleViewHolder holder, int position) {
}
@Override
public int getItemCount() {
return mData.size();
}
}
public class SimpleViewHolder extends RecyclerView.ViewHolder{
public SimpleViewHolder(View itemView) {
super(itemView);
}
}
我們先建立一個SimpleViewHolder類繼承RecyclerView.ViewHolder作為Adapter的泛型,因為資料的類型是不确定的,我們這裡也設計為泛型。
構造方法傳入layoutId後,我們就可以建立ViewHolder,然後實作onCreateViewHolder方法,考慮到Context參數經常在綁定資料的時候用到,而view又能拿到context,是以我們在實作onCreateViewHolder方法的時候順便拿到Context對象
protected Context mContext;
@Override
public SimpleViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
mContext = parent.getContext();
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
View view = layoutInflater.inflate(layoutId, parent, false);
SimpleViewHolder holder = new SimpleViewHolder(view);
return holder;
}
在onBindViewHolder方法中,我們可以進行資料綁定,事件監聽。
資料綁定顯然要交給子類去處理,我們把類改為抽象類,然後定義抽象方法。事件綁定我們就寫一個最常用的字條目點選事件。
private OnItemClickListener onItemClickListener;
@Override
public void onBindViewHolder(final SimpleViewHolder holder, final int position) {
if (this.onItemClickListener != null) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onItemClickListener.onItemClick(SimpleQuickAdapter.this, holder.itemView, mData.get(position), position);
}
});
}
convert(holder, position, mData.get(position));
}
protected abstract void convert(SimpleViewHolder holder, int position, B b);
public interface OnItemClickListener {
void onItemClick(SimpleQuickAdapter adapter, View itemView, Object b, int position);
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener){
this.onItemClickListener = onItemClickListener;
}
經過以上分裝adapter基本上已經可以用了
但是每次用viewHolder綁定資料時,都要用holder.itemView.findViewById(viewId)找到控件後才能對控件進行操作,這就顯得有點繁瑣。是以我們有必要對Holder進行簡單的封裝。
public class SimpleViewHolder extends RecyclerView.ViewHolder {
public SimpleViewHolder(View itemView) {
super(itemView);
}
public <V extends View>V getView(int viewId){
return itemView.findViewById(viewId);
}
public SimpleViewHolder setText(int viewId, String text) {
TextView view = getView(viewId);
view.setText(text);
return this;
}
public SimpleViewHolder setText(@IdRes int viewId, @StringRes int strId) {
TextView view = getView(viewId);
view.setText(strId);
return this;
}
}
我們可以在這裡對圖檔設定,背景設定,設定是否顯示…等常用功能進行封裝,原理差不多,就不多寫了。
我們每次getView都會去通路itemView.findViewById(viewId),如果對同一控件進行多次操作就沒這個必要。我們可以拿個容器去存儲用過的view來提升效率。
private SparseArray<View> views;
public SimpleViewHolder(View itemView) {
super(itemView);
this.views = new SparseArray<>();
}
//當集合views中有view時直接傳回view,當集合中沒有view的時候findViewById擷取view,并且把這個view存入集合
public <V extends View>V getView(int viewId){
View view = views.get(viewId);
if (view == null) {
view = itemView.findViewById(viewId);
views.put(viewId, view);
}
return (V) view;
}
github傳送門