RecyclerView雖然因其靈活性、高效性等特色而備受好評,但也不是必定得用它把ListView給替代掉。在某些場景中,ListView仍是相對更適合的。好比數據量不大,不頻繁更新,而且須要簡單地設置一下divider或header、footer的時候,相對於RecyclerView的繁瑣,ListView在實現上則表現得更方便和簡潔。java
在使用ListView的過程當中,爲了複用ListView中的convertView以及優化getView()時的findViewById操做,咱們一般會引入一個ViewHolder類,來持有itemView的子view。可是不便的是,咱們須要爲每一種顯示不一樣數據的ListView都重寫其BaseAdapter的getView(),因而就有了對其的一種封裝:
建立一個一般的ViewHolder,裏面用SparseArray<View>
來緩存,以下:android
public class ViewHolder {
private final View itemView;
private SparseArray<View> mHolderViews;
public ViewHolder(View view) {
itemView = view;
view.setTag(this);
mHolderViews = new SparseArray<>();
}
public void hold(int... resIds) {
for (int id : resIds) {
mHolderViews.put(id, itemView.findViewById(id));
}
}
public <V> V get(int id) {
return (V) mHolderViews.get(id);
}
}
再經過重寫BaseAdapter的getView方法,做以下封裝:git
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if (convertView == null) {
holder = createHolder(position, parent);
convertView = holder.itemView;
} else {
holder = (ViewHolder) convertView.getTag();
}
bindData(position, holder, getItem(position));
return convertView;
}
public abstract ViewHolder createHolder(int position, ViewGroup parent);
public abstract void bindData(int position, ViewHolder holder, T data);
這樣使用的時候只須要繼承並實現兩個抽象方法便可。github
這樣寫法,在後來我以爲仍是有些彆扭的地方。一是使用的時候仍是須要去繼承咱們封裝過的Adapter;二是在bindData()
方法中,須要對每個咱們要設置的控件調用holder.get(id)
方法,才能開始賦值或進行其餘的設置,當咱們的item的控件較多時,這些調用會使bindData()
顯得臃腫而不夠純粹,因而我又對其進行了另外一種封裝。緩存
這裏我主要是經過接口解決重寫Adapter的問題,而後再借鑑RecyclerView.ViewHolder,封裝結果以下:markdown
假設咱們重寫BaseAdapter的類爲BaseListAdapter,那麼首先定義它的一個靜態內部類:ide
public static abstract class ViewHolder {
public final View itemView;
public ViewHolder(View itemView) {
this.itemView = itemView;
itemView.setTag(this);
}
}
而後咱們定義一個接口,用於建立ViewHolder以及綁定數據,以下:優化
package com.githang.android.snippet.demo.adapter.adapter;
import android.view.ViewGroup;
public interface ViewCreator<T, H extends BaseListAdapter.ViewHolder> {
H createHolder(int position, ViewGroup parent);
/** * 設置列表裏的視圖內容 * * @param position 在列表中的位置 * @param holder 該位置對應的視圖 */
void bindData(int position, H holder, T data);
}
這裏定義了兩個泛型 ,一個是T,表示咱們Adapter裏的數據對象,另外一個是H,爲咱們的ViewHolder的子類,表示咱們最終建立出來的ViewHolder。this
接下來,修改咱們的BaseListAdapter
,以下:spa
public class BaseListAdapter<T, H extends BaseListAdapter.ViewHolder> extends BaseAdapter {
private final List<T> mData;
private final ViewCreator<T, H> mViewCreator;
public BaseListAdapter(ViewCreator<T, H > creator) {
this(new ArrayList<T>(), creator);
}
public BaseListAdapter(List<T> data, ViewCreator<T, H> creator) {
mData = data == null ? new ArrayList<T>() : data;
mViewCreator = creator;
}
@Override
public int getCount() {
return mData.size();
}
@Override
public T getItem(int position) {
return mData.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final H holder;
if (convertView == null) {
holder = mViewCreator.createHolder(position, parent);
convertView = holder.itemView;
} else {
holder = (H) convertView.getTag();
}
mViewCreator.bindData(position, holder, getItem(position));
return convertView;
}
public void update(List<T> data) {
mData.clear();
addData(data);
}
public void addData(List<T> data) {
if (data != null) {
mData.addAll(data);
}
notifyDataSetChanged();
}
public static abstract class ViewHolder {
public final View itemView;
public ViewHolder(View itemView) {
this.itemView = itemView;
itemView.setTag(this);
}
}
}
在這個BaseListAdapter
裏,一樣定義了兩個泛型,與咱們的ViewCreator
相同。咱們建立ViewHolder及綁定數據的操做,經過調用該接口來執行(見getView方法),而該接口實例則經過構造方法傳入。
這樣,咱們在使用的時候就能夠先像使用RecyclerView同樣,定義一個實現咱們ViewHolder的子類,而後使咱們的Fragment或Activity實現這個ViewCreator接口就能夠了。
最後,咱們還能夠繼承ViewHolder,封裝一個通用的ViewHolder,代碼以下:
public static class DefaultViewHolder extends ViewHolder {
private SparseArray<View> mHolderViews;
public DefaultViewHolder(View view) {
super(view);
mHolderViews = new SparseArray<>();
}
public void hold(int... resIds) {
for (int id : resIds) {
mHolderViews.put(id, itemView.findViewById(id));
}
}
public <V> V get(int id) {
return (V) mHolderViews.get(id);
}
}
接下來是使用示例:
public class SampleFragment extends Fragment implements ViewCreator<User,SampleFragment.UserViewHolder> {
private BaseListAdapter<User, UserViewHolder> mAdapter = new BaseListAdapter<User, UserViewHolder>(this);
@Override
public UserViewHolder createHolder(int position, ViewGroup parent) {
return new UserViewHolder(LayoutInflater.from(getActivity()).inflate(R.layout.item_user, parent, false));
}
@Override
public void bindData(int position, UserViewHolder holder, User user) {
holder.name.setText(user.name);
holder.email.setText(user.email);
}
static class UserViewHolder extends BaseListAdapter.ViewHolder {
public final TextView name;
public final TextView email;
public UserViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.name);
email = (TextView) itemView.findViewById(R.id.email);
}
}
}
所有的封裝代碼加上註釋等也不過一百來行,可是能夠看到,在這樣的封裝下,咱們的代碼已經變得很清晰。
本文的代碼見:http://download.csdn.net/detail/maosidiaoxian/9700700
更詳細的封裝及相關代碼見個人開源項目:https://github.com/msdx/AndroidSnippet/tree/master/library/src/main/java/com/githang/android/snippet/adapter