寫了這麼多年的adapter,一直以來都是在搬磚,按部就班,從沒想過如何將adapter寫的更加優雅,這個實在是不應該,內心着實慚愧,直到看了recyclerView書寫的優雅adapter才恍然大悟,以下是原作者的博客地址:
http://www.jianshu.com/p/1297d2e4d27a?winzoom=1
由於自己項目還是listview,所以參考了作者寫的recycleview,改動了一番。寫下此博客一個是對可訪問者模式的理解,一個是練習面向接口編程,將代碼解耦。這篇文章簡單,但是涉及東西還是值得一學
囉嗦一句,設計模式看的再多如果沒有實際東西操作,真的很難去掌握,也很難深入骨髓,這也是作者本人親身感受,所以藉此機會能夠用上一番,對自己也有莫大的好處
涉及的知識點:
@Override public int getItemViewType(int position) { // TODO Auto-generated method stub Object obj = mDataList.get(position); if (obj instanceof AdInfo) { return 0; } else if (obj instanceof UserListInfo) { return 1; } else if (obj instanceof TopicListInfo) { return 2; } else if (obj instanceof BangListInfo) { return 3; } else if (obj instanceof .GroupListInfo) { return 4; } else if (obj instanceof .TagListInfo) { return 5; } else if (obj instanceof VideoListInfo) { return 6; } else if (obj instanceof .KnowledgeListInfo) { return 7; } return super.getItemViewType(position); }
我相信絕大多人都寫過類似的爛代碼,這種陋習在沒學過訪問者模式或者沒熟用這個模式之前很容易就寫出來,而且絕大多數人都覺得這是理所應當的,可能還自我感覺良好,,接下來看看經過改良過的方法:
@Override
public int getItemViewType(int position) {
return modelList.get(position).type(typeFactory);
}
一行代碼就可以搞定,這是多麼神奇又令人興奮的事兒,這個不但是一行代碼的問題,它的可擴展性絕對比你之前的寫法要好上好多,也符合開閉原則“對外可擴展,又不用修改原有代碼”,何樂而不爲呢。接下來針對demo講解訪問者模式中的結構對象與訪問者
public interface Visitable {
int type(TypeVisitorFactory typeFactory);
}
當所有model都實現這個接口後,他們就具有結構對象的能力,而且只要是實現這個接口就一定有這個能力,所以這又可以從複雜業務中解耦。那麼我們再來看看訪問者public interface TypeVisitorFactory {
int type(One one);
int type(Two two);
int type(Three three);
int type(Normal normal);
BaseViewHolder createViewHolder(int type,Context context);
}
這一看也是個接口,而且重載了很多type()方法,這個是幹什麼用的呢?我們來看看它的一個實現:
public class TypeVisitorFactoryImp implements TypeVisitorFactory {
private static final String TAG = "TypeVisitorFactoryImp";
private final int TYPE_ONE = 0;//type 值必須從0開始 詳解http://www.cnblogs.com/RGogoing/p/5872217.html
private final int TYPE_TWO = 1;
private final int TYPE_THREE = 2;
private final int TYPE_NORMAL = 3;
@Override
public int type(One one) {
Log.d(TAG, "TYPE_ONE = " + TYPE_ONE);
return TYPE_ONE;
}
@Override
public int type(Two one) {
Log.d(TAG,"TYPE_TWO = "+TYPE_TWO);
return TYPE_TWO;
}
@Override
public int type(Three one) {
Log.d(TAG,"TYPE_THREE = "+TYPE_THREE);
return TYPE_THREE;
}
@Override
public int type(Normal normal) {
Log.d(TAG,"TYPE_NORMAL = "+TYPE_NORMAL);
return TYPE_NORMAL;
}
@Override
public BaseViewHolder createViewHolder(int type,Context context) {
if (TYPE_ONE == type) {
return new OneViewHolder(context);
} else if (TYPE_TWO == type) {
return new TwoViewHolder(context);
} else if (TYPE_THREE == type) {
return new ThreeViewHolder(context);
} else if (TYPE_NORMAL == type) {
return new NormalViewHolder(context);
}
return null;
}
}
這一看type()方法返回的都是類型,而且參數都是各個類型的model,也許你猜出來了,由於之前都是通過instanceof 來返回type值,但是現在只要通過實現相應的type(model),就可以返回相應的type值。這是怎麼做到的呢。
private List<Visitable> modelList = new ArrayList<Visitable>();
數據源果然是一個Visitable,那你是不是有個疑問,數據源是一個接口形式,那麼每次獲取model的時候是不是都要強轉,而且還要根據類型去強轉,一來二去的好麻煩的樣子。其實不然,細心的你可能發現上邊訪問者的接口和實現時發現一個熟悉的方法,bingo,就是BaseViewHolder createViewHolder(type);想當然的是,根據type值返回一個holview對象。那麼接下來我們看看BaseViewHolder的定義:public abstract class BaseViewHolder<T> {
private SparseArray<View> views;
private View mItemView;
public BaseViewHolder(Context context, int resId) {
views = new SparseArray<>();
this.mItemView = View.inflate(context, resId, null);
mItemView.setTag(this);
}
public View getmItemView() {
return mItemView;
}
public abstract void setUpView(T model, int position, MultiTypeAdapter adapter);
}
這是一個抽象類,而且還是一個帶泛型的抽象類,有一個抽象方法一定引起了你的注意:這裏只貼出我項目中優化過的holder實例:
public abstract class BaseViewHolder<T> {
private View mItemView;
protected Context context;
public BaseViewHolder(Context context, int resId) {
this.context = context;
this.mItemView = View.inflate(context, resId, null);
mItemView.setTag(this);
initView(mItemView);
}
public View getmItemView() {
return mItemView;
}
/**更新view的數據*/
public abstract void updateViewData(T model, int position, SearchResultAllAdapter adapter);
/***
* findViewById
*
* @param mItemView
*/
abstract void initView(View mItemView);
}
public class ArticleViewHolder extends BaseViewHolder<One > { private TextView tvArticleTitle; private TextView tvArticleContent; private TextView tvArticleFrom; public ArticleViewHolder(Context context) { super(context,R.layout.lmb_all_search_article_item); } @Override public void updateViewData(One model, int position, AllAdapter adapter) { tvArticleFrom.setText(model.bname); final String articleId = model.id; getmItemView().setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { } @Override void initView(View mItemView) { tvArticleTitle = (TextView) mItemView.findViewById(R.id.tv_search_article_title); tvArticleContent = (TextView) mItemView.findViewById(R.id.tv_search_article_content); tvArticleFrom = (TextView) mItemView.findViewById(R.id.tv_search_article_from); } }
這樣是不是很明瞭了,每個類型對應一個Holer,所有操作互不關心,只要是實現holder的相應方法就可以了;擴展性都好;
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 帖子信息
final BaseViewHolder holder;
if (convertView == null) {
Log.d(TAG, "convertView == null = " + position);
holder = typeFactory.createViewHolder(getItemViewType(position), mContext);
convertView = holder.getmItemView();
} else {
Log.d(TAG, "convertView != null = " + position);
holder = (BaseViewHolder) convertView.getTag();
}
holder.setUpView(modelList.get(position), position, this);
return convertView;
}
holder = typeFactory.createViewHolder(getItemViewType(position), mContext);
主要是這句:直接通過getItemViewType,創建相應的holder,然後直接調用holder.setUpView();直接將接口model傳遞過去,由於是泛型所以解決了上述說的強轉的問題;
這麼一看這樣的getview即簡潔又明瞭,而且後續增加新的item都不用動其中的代碼即可完成。只需要添加新的HolderView、新增類型的model且model實現Visitable 最後重載訪問者的 type(model)方法就完成了。