本篇文章你會了解以下内容:
1、抽取ViewHolder,實作優化第一步
2、抽取公共Adapter,告别備援方法
3、控件直接指派,讓Adapter再次優化
執筆不易,覺得不錯,請轉發分享(更多Android文章,還請關注左側欄中我的公衆号)。
ListView不得不說是Android開發當中出現率相當高的一個控件,什麼資訊清單,聯系人清單,消息清單等等,無不有它的身影存在。既然項目中有太多的地方會用到它,傳統的代碼邏輯我們是怎麼使用的呢?每寫一個ListView,都要去寫一個Adapter類,一個ViewHolder類,這幾乎是我們必須要操作的,以緻于有太多太多的備援代碼,讓我們感到真的不厭其煩,一個兩個還可以,十個八個,就真的有點太崩潰了,不僅代碼繁瑣,還會占用記憶體,為了解決這樣的一個問題,下面就要開始對其抽取優化,代碼之間從上到下進行銜接,不要跳過看啊。
1、抽取ViewHolder,實作優化第一步
我們知道正常的ViewHolder,會在Adapter的getView方法裡去操作很多事情,建立執行個體,指派控件,setTag和getTag等,如下代碼所示,是我們最常用的模式,這裡我隻用了兩個控件,其實開發中有很多的View,對ViewHolder的優化,就是把這些代碼封裝進行抽取。
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view;
ViewHolder viewHolder;
if (convertView == null) {
view = View.inflate(mContext, R.layout.item_view, null);
viewHolder = new ViewHolder();
viewHolder.tvTitle = (TextView) view.findViewById(R.id.tv_title);
viewHolder.tvContent = (TextView) view.findViewById(R.id.tv_content);
view.setTag(viewHolder);
} else {
view = convertView;
viewHolder = (ViewHolder) view.getTag();
}
viewHolder.tvTitle.setText(mDatas.get(position).getTitle());
viewHolder.tvContent.setText(mDatas.get(position).getContent());
return view;
}
private static class ViewHolder {
TextView tvTitle, tvContent;
}
自定義一個ViewHolder類,實作構造方法,既然要實作對以上的代碼進行一個抽取,那麼getView裡的參數,ViewHolder類大部分都需要用到,在ViewHolder類中,寫一個靜态方法,用于擷取ViewHolder,其判斷邏輯和以上代碼思路一緻,convertView若不存在就去建立ViewHolder,若存在就複用。
public static ViewHolder getViewHolder(Context context, int position,
View convertView, ViewGroup parent, int layoutId) {
if (convertView == null) {
return new ViewHolder(context, position, parent, layoutId);
} else {
ViewHolder viewHolder = (ViewHolder) convertView.getTag();
viewHolder.mPosition = position;
return viewHolder;
}
}
實作其構造方法,用于convertView不存在時進行對其執行個體:
private SparseArray<View> mView;
private int mPosition;
private View mConvertView;
public ViewHolder(Context context, int position, ViewGroup parent, int layoutId) {
mPosition = position;
mView = new SparseArray<View>();
mConvertView = View.inflate(context, layoutId, null);
mConvertView.setTag(this);
}
mView是用于存儲各個控件View,在這裡為什麼要用SparseArray而不用Map,我們可以去檢視下源碼,其實對于鍵為int類型,SparseArray要比map更加有效的使用記憶體。
mPosition是記錄索引位置。
mConverView就是建立的View對象,注意看上述代碼,它是在convertView為null的情況下,才去建立的。
寫一個方法,用于通過ViewHolder來擷取各個控件:
public <T extends View> T getView(int layoutId) {
View view = mView.get(layoutId);
if (view == null) {
view = getmConvertView().findViewById(layoutId);
mView.put(layoutId, view);
}
return (T) view;
}
再寫一個擷取layout的方法:
public View getmConvertView() {
return mConvertView;
}
經過以上代碼,對于ViewHolder,我們基本上就抽取完成了,回過來,我們再看adapter裡的getView方法(注意,原來adapter裡的ViewHolder類可以對其删除),是不是一下整潔了許多。
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder=ViewHolder.getViewHoder(mContext,position,
convertView,parent,R.layout.item_view);
((TextView)viewHolder.getView(R.id.tv_title)).
setText(mDatas.get(position).getTitle());
((TextView)viewHolder.getView(R.id.tv_content)).
setText(mDatas.get(position).getContent());
return viewHolder.getmConvertView();
}
2、抽取公共Adapter,告别備援方法
ViewHolder抽取完之後,省去了太多的代碼,但是我們會發現Adapter裡除了getView方法,其它三個也是重複的,那麼接下來,我們就對Adapter進行抽取優化,建立一個萬能的Adapter類,繼承于BaseAdapter,記住要是抽象的,把getView方法設成抽象方法,這樣其它Adapter繼承這個萬能Adapter,隻需要重寫getView方法即可。
public abstract class UniversalAdapter<T> extends BaseAdapter {
protected List<T> mDatas;
protected Context context;
private int layoutId;
public UniversalAdapter(Context context, List<T> mDatas, int layoutId) {
this.mDatas = mDatas;
this.context = context;
this.layoutId = layoutId;
}
@Override
public int getCount() {
return mDatas.size();
}
@Override
public Object getItem(int position) {
return mDatas.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public abstract View getView(int position, View convertView, ViewGroup parent);
}
在上述代碼中,細心的同志的可能看到了,實作其構造方法時,我傳入了一個layoutId,其實就是一個XML資源,引入它,說白了,在getView方法裡我連 ViewHolder viewHolder=ViewHolder.getViewHolder(mContext,position,convertView,parent,R.layout.item_view);這段代碼我也不想去寫,怎麼辦呢?很簡單getView方法不去抽象化,而引出一個抽象的方法,具體可以改成以下方式:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHoder = ViewHolder.getViewHolder(context, position, convertView, parent, layoutId);
convert(viewHolder, mDatas.get(position));
return viewHolder.getmConvertView();
}
protected abstract void convert(ViewHolder viewHolder, Object item);
以後我們所有的Adapter繼承這個萬能的Adapter之後,隻需要重寫convert這個方法就好了,Object item是一個Bean,擷取時要進行強轉。
public class MyAdapter extends CourAdapter {
public MyAdapter(List<Bean> data, Context context, int layoutId) {
super(context, data, layoutId);
}
@Override
protected void convert(ViewHoder viewHoder, Object item) {
((TextView)viewHolder.getView(R.id.tv_title)).
setText(((Bean)item).getTitle());
((TextView)viewHolder.getView(R.id.tv_content)).
setText(((Bean)item).getContent());
}
}
看看以上的代碼,是不是就簡單了很多呢。
3、控件直接指派,讓Adapter再次優化
經過以上的優化之後,代碼大大的減少了,但是,我還想進行對其優化,也就是說,我連setText都不想去那麼麻煩,這樣的話,我們就是可以在ViewHolder這個類中去寫控件指派方法:
//TextView設定資料
public ViewHolder setText(int viewId, String txt) {
TextView mTextView = getView(viewId);
mTextView.setText(txt);
return this;
}
再看convert方法,是不是又一下簡單的很多。
@Override
protected void convert(ViewHolder viewHolder, Object item) {
viewHolder.setText(R.id.tv_title,((Bean)item).getTitle())
.setText(R.id.tv_content,((Bean)item).getContent());
}
當然了對于其它控件你也可以這樣去做,比如ImageView:
public ViewHolder setPic(int viewId, String url) {
ImageView mImageView = getView(viewId);
//ImageLoader.getInstance().loadImage(url,mImageView);
return this;
}
好了同志們,以上就是今天分享的所有内容,更多Android文章還請關注左側欄中我的微信公衆号,好累啊,拜拜了。