前言
還記得我初學(xué) Android 沒多久又需要用到 ListView 的時(shí)候還不會寫 Adapter掩幢,結(jié)果我居然硬生生的用 TableLayout 和 LinearLayout 把 ListView 給替代了。現(xiàn)在回過神了想想苛坚,我當(dāng)時(shí)還真是厲害啊妓羊,在另一種意義上胯究。。
其實(shí)要會寫 Adapter躁绸,乃至于封裝一個能提高生產(chǎn)效率的 Adapter裕循,是離不開對 ListView、GridView 到 RecyclerView 等一系列視圖的Item復(fù)用機(jī)制的理解和掌握的净刮。不熟悉的朋友們請移駕
http://blog.csdn.net/lmj623565791/article/details/24333277
正文
我還依稀記得最早是這么寫一個 Adapter 的(為什么是依稀呢剥哑?)
BaseAdapter adapter = new BaseAdapter() {
@Override
public int getCount() {
return list.size();
}
@Override
public String getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
convertView = inflater.inflate(layoutId, parent, false);
TextView textView = convertView.findViewById(textViewId);
Button button = convertView.findViewById(buttonId);
String data = getItem(position);
textView.setText(data);
button.setOnClickListener(onClickListener);
return convertView;
}
};
后來我才意識到,這樣子寫在滑動的時(shí)候會很頻繁的去 inflate淹父,開銷很大株婴,是不合格的寫法,于是改成了下面這樣:
BaseAdapter adapter = new BaseAdapter() {
@Override
public int getCount() {
return list.size();
}
@Override
public String getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if (convertView == null) {
convertView = inflater.inflate(layoutId, parent, false);
viewHolder = new ViewHolder();
viewHolder.textView = convertView.findViewById(textViewId);
viewHolder.button = convertView.findViewById(buttonId);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
String data = getItem(position);
viewHolder.textView.setText(data);
viewHolder.button.setOnClickListener(onButtonClickeListener);
convertView.setTag(viewHolder);
convertView.setOnClickListener(onConvertViewClickedListener);
return convertView;
}
};
class ViewHolder {
TextView textView;
Button button;
}
這樣子寫基本上沒有遇到遇到太大問題,也就最初容易遇到因?yàn)?item 復(fù)用導(dǎo)致的各種混亂困介,在理解了復(fù)用機(jī)制之后其實(shí)很容易避免大审。我也曾經(jīng)見到過將 convertView 傳入 ViewHolder 中,在 viewHolder 里去實(shí)現(xiàn)大部分邏輯的寫法座哩,這里就不一一贅述了徒扶。
現(xiàn)在來想一下,寫一個 Adapter根穷,我們要做些什么姜骡?大致有這些吧:
1.實(shí)現(xiàn) BaseAdapter 的 getCount、getItem屿良、getItemId 這三個方法圈澈;
2.新建一個 ViewHolder 類,當(dāng)中要包含一個 item 中需要操作的所有 view管引;
3.在 BaseAdapter 的 getView 方法中實(shí)現(xiàn) item 復(fù)用士败,處理好 viewHolder 與 convertView 的關(guān)系;
4.處理 item褥伴,設(shè)置數(shù)據(jù)至 view 谅将,添加監(jiān)聽等。
看上去也不太多嘛重慢,就4步饥臂。但是實(shí)際上多寫幾次就很難受了,要是項(xiàng)目中 ListView似踱、GridView 特別多隅熙,估計(jì)能把一只猿活活寫吐了!
因?yàn)?核芽、2囚戚、3的代碼基本上每次都差不多,不同之處大多都在于4轧简。一個復(fù)雜的 ListView 寫下來感覺腰酸背痛手抽筋驰坊,簡單的則感覺不會再愛了,畢竟花在寫4的時(shí)間上好像遠(yuǎn)遠(yuǎn)小于123的重復(fù)工作哮独。久而久之拳芙,我一聽到要做 ListView 就會這樣 --> T_T
直到在一個月黑風(fēng)高的夜晚,我意外得到了一件神器
public class ViewHolder {
public static <T extends View> T get(View view, int id) {
SparseArray<View> viewHolder = (SparseArray<View>) view.getTag();
if (viewHolder == null) {
viewHolder = new SparseArray<View>();
view.setTag(viewHolder);
}
View childView = viewHolder.get(id);
if (childView == null) {
childView = view.findViewById(id);
viewHolder.put(id, childView);
}
return (T) childView;
}
}
從代碼上看皮璧,這個 ViewHolder 類會往傳入的 view 中存一系列 id-view 的鍵值對作為 tag舟扎,代碼并不復(fù)雜,相信大家都看得懂悴务。
我們再看看這神器到底能干什么
BaseAdapter adapter = new BaseAdapter() {
@Override
public int getCount() {
return list.size();
}
@Override
public String getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(layoutId, parent, false);
}
TextView textView = ViewHolder.get(convertView, textViewId);
Button button = ViewHolder.get(convertView, buttonId);
String data = getItem(position);
textView.setText(data);
button.setOnClickListener(onButtonClickListener);
return convertView;
}
};
完全不用專門寫 ViewHolder 類了有木有睹限,對于我這樣的懶人簡直就是福音得意
以上就是神器 ViewHolder 的功效。
難道這是全部了嗎?作為一個懶惰的程序猿邦泄,相信大家肯定和我一樣不滿足于此删窒。放眼上述的代碼,不管是哪個 adapter 都寫了幾個重復(fù)的方法顺囊,那些方法要怎么干掉呢?
其實(shí)蕉拢,一個簡單的基類加上對泛型的支持就可以了
public abstract class ListAdapter<T> extends BaseAdapter {
protected abstract void setItem(View convertView, T data, int position);
protected List<T> mData;
protected Context mContext;
protected LayoutInflater mInflater;
protected int mLayoutRes;
public ListAdapter(Context context, List<T> data, int layoutRes) {
this.mData = data;
this.mContext = context;
this.mLayoutRes = layoutRes;
this.mInflater = LayoutInflater.from(mContext);
}
/**
* 刷新 adapter
* 考慮到可能會發(fā)生數(shù)據(jù)完全改變的情況特碳,故提供此方法
* @param data
*/
public void refresh(List<T> data) {
try {
this.mData = data;
notifyDataSetChanged();
} catch (Exception e) {
e.printStackTrace();
}
}
@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) {
try {
if (convertView == null) {
convertView = mInflater.inflate(mLayoutRes, null);
}
setItem(convertView, getItem(position), position);
} catch (Exception e) {
e.printStackTrace();
}
return convertView;
}
public <V extends View> V getChildView(View view, int id) {
return ViewHolder.get(view, id);
}
}
封裝到此結(jié)束,讓我們看看是怎么使用的吧
BaseAdapter adapter = new ListAdapter<String>(MainActivity.this, list, layoutId) {
@Override
protected void setItem(View convertView, String data, int position) {
TextView textView = getChildView(convertView, textViewId);
textView.setText(data);
getChildView(convertView, buttonId)
.setOnClickListener(onButtonClickListener);
}
};
沒仔細(xì)看前面代碼的圍觀群眾:WTF晕换?這就完了午乓?
沒錯,傳入上下文闸准、數(shù)據(jù)集合益愈,還有布局文件,重寫一個 setItem 方法就完了夷家。相比最初的 adapter蒸其,我們只需要專注于布局、數(shù)據(jù)和視圖的綁定库快,已經(jīng)事件的監(jiān)聽摸袁,不需要重寫相同的代碼,不需要寫累贅的 ViewHolder 類义屏,不需要寫八股文一般的 Item 復(fù)用代碼靠汁。
雖然并不完美,也還沒做到極簡闽铐,但一個簡單好用的 Adapter 已經(jīng)初步成型了蝶怔。謝謝各位看官賞臉看到現(xiàn)在。
下一篇文章打算在這次的 ListAdapter 的基礎(chǔ)上封裝 RecyclerView 的 Adapter兄墅,并且提供對 ItemType 的支持方式的參考思路踢星。
項(xiàng)目源碼:https://github.com/neverwoodsS/zy-open