频道澳门葡京手机版网址
登录注册
澳门葡京手机版网址 > 澳门葡京手机版网址 > 移动开发 > Android > 正文
android之优雅书写多类型Adapter
2017-01-22 09:28:44         来源:小钟的专栏  
收藏   我要投稿

写了这么多年的adapter,一直以来都是在搬砖,按部就班,从没想过如何将adapter写的更加优雅,这个实在是不应该,内心着实惭愧,直到看了recyclerView书写的优雅adapter才恍然大悟

由于自己项目还是listview,所以参考了编辑写的recycleview,改动了一番。写下此博客一个是对可访问者模式的理解,一个是练习面向接口编程,将代码解耦。这篇文章简单,但是涉及东西还是值得一学

啰嗦一句,设计模式看的再多如果没有实际东西操作,真的很难去掌握,也很难深入骨髓,这也是编辑本人亲身感受,所以借此机会能够用上一番,对自己也有莫大的好处

涉及的常识点:

1、可访问者模式:这是设计模式中的一种,若还不了解自行百度,也比较简单

2、简单的泛型

基本这些就够了。接下来大家直接看代码

MutilTypeAdapter:

按照以前的写法,我相信很多人都是一下这样的或者根据model的type逐一分类

@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讲解访问者模式中的结构对象与访问者

结构对象:

在这里的结构对象就是数据源(item的model),所以大家这里定义个接口,所有类型的model都要实现它,因为访问者访问model后获取与其相对应的type值然后根据type值创建相应的holder与view:

public interface Visitable {

int type(TypeVisitorFactory typeFactory);

}

当所有model都实现这个接口后,他们就具有结构对象的能力,而且只要是实现这个接口就一定有这个能力,所以这又可以从复杂业务中解耦。那么大家再来看看访问者

访问者:TypeVisitoryFactory

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开始 详解https://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值。这是怎么做到的呢。

在回头捋一遍:model实现了Visitable接口具有结构对象的能力,只要model传入相应的访问者(TypeVisitoryFactory),访问者调用type(model),即可得到type。由于java的高级用法多态,使大家在调用方法时至多父类接口,它会自行调用子类的实现。所以只要adapter的数据源是一个visitable就很轻而易举的实现了现在的方式

结构对象和访问者讲完了,接下来看看adapter的数据源于getView是怎样工作的

数据源:

private List modelList = new ArrayList();

数据源果然是一个Visitable,那你是不是有个疑问,数据源是一个接口形式,那么每次获取model的时候是不是都要强转,而且还要根据类型去强转,一来二去的好麻烦的样子。其实不然,细心的你可能发现上边访问者的接口和实现时发现一个熟悉的方法,bingo,就是BaseViewHolder createViewHolder(type);想当然的是,根据type值返回一个holview对象。那么接下来大家看看BaseViewHolder的定义:

public abstract class BaseViewHolder {

private SparseArray 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);

}

这是一个抽象类,而且还是一个带泛型的抽象类,有一个抽象方法一定引起了你的注意:

setUpView(T model xxxx);

相信聪明的你已经猜出来了,T model就是大家各个类型对应的实际model,每种model对应一个holder类。所以这里要对泛型的常识有一些基础性的了解就很容易明白了。

这里只贴出我项目中优化过的holder实例:

public abstract class BaseViewHolder {

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);

}

这里不需要将所有findviewByid的view缓存到SparseArray中,直接按照以前的adapter的holder方式就行,只要findviewById一次就好:只要在各个实现类中实现initView,初始化一次就好了:

public class ArticleViewHolder extends BaseViewHolder { 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的相应方法就可以了;扩展性都好;

那么大家看看getView是怎么工作的:

@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)方法就完成了。

点击复制链接 与好友分享!回澳门葡京手机版网址澳门葡京手机版网址
上一篇:Android安卓最简单的弹幕实现
下一篇:androidstudio 插件 butterknife 安装及应用
相关文章
图文推荐
点击排行

关于大家 | 联系大家 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 澳门葡京手机版网址_澳门新莆京娱乐_www.88807.com - 点此进入--致力于做实用的IT技术学习网站

XML 地图 | Sitemap 地图