我们都知道,在安卓中使用ListView显示多条数据的时候,必须要用一个适配器作为Data和View的桥梁,这种设计非常好, 能很简单就把ui和data分离开来,为ui的复用和维护代码提供方便。
但是每次写一个适配器,都要实现一大堆的重复逻辑,下面是一个常规的实现:
foo_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/activity_horizontal_margin"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical">
<TextView
android:id="@+id/txtTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="今天好天气" />
<TextView
android:id="@+id/txtContent"
android:layout_marginTop="10dp"
tools:text="我是一名android开发者"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
适配器:
public class FooNormalAdapter extends BaseAdapter {
private List<FooBean> datas = new ArrayList<>();
private Context _context;
public FooNormalAdapter(Context context) {
this._context = context;
}
public Context getContext() {
return _context;
}
public void setDataSource(List<FooBean> fooBeens) {
setDataSource(fooBeens,true);
}
public void setDataSource(List<FooBean> fooBeens,boolean isClear) {
if(isClear) this.datas.clear();
this.datas.addAll(fooBeens);
notifyDataSetChanged();
}
@Override
public int getCount() {
return datas.size();
}
@Override
public Object getItem(int position) {
return datas.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
FooViewHolder viewHolder = null;
if (convertView == null) {
convertView = View.inflate(getContext(), R.layout.foo_item, null);
convertView.setTag(new FooViewHolder(convertView));
} else {
viewHolder = (FooViewHolder) convertView.getTag();
}
FooBean fb = (FooBean) getItem(position);
viewHolder.txtTitle.setText(fb.getTitle());
viewHolder.txtContent.setText(fb.getContent());
return convertView;
}
public static class FooViewHolder {
private TextView txtTitle;
private TextView txtContent;
public FooViewHolder(View convertView) {
this.txtTitle = (TextView) convertView.findViewById(R.id.txtTitle);
this.txtContent = (TextView) convertView.findViewById(R.id.txtContent);
}
}
}
嗯,看起来没有什么问题,但是如果有十个adapter,就要写十次这种无意义的代码,我们不能干体力活啊~怎么办?
先分析一下adapter需要哪些元素:
1.首先要inflateView就必须用到Context.
2.需要一个数组来存储用于显示的数据源
3.需要一个viewholder来优化程序性能
4.可能有不同的viewType
分析完这个,代码随之而来:
public abstract class LBaseAdapter<E, V extends LBaseAdapter.BaseViewHolder> extends BaseAdapter {
private Context context;
private List<E> dataSource = new ArrayList<>(); //初始化一个防止getCount()空指针
public LBaseAdapter(Context context) {
this.context = context;
}
public Context getContext() {
return context;
}
//替换原有数据源
public void setDataSource(List<E> dataSource) {
setDataSource(dataSource,true);
}
//如果isClear==true,则替换原有数据源,否则加到数据源后面
public void setDataSource(List<E> dataSource, boolean isClear) {
if (isClear) this.dataSource.clear();
this.dataSource = dataSource;
notifyDataSetChanged();
}
//只加一个数据
public void addData(E data) {
this.dataSource.add(data);
notifyDataSetChanged();
}
//通过下标移除一条数据
public void removeData(int position) {
this.dataSource.remove(position);
notifyDataSetChanged();
}
//通过对象移除一条数据
public void removeData(E data) {
this.dataSource.remove(data);
notifyDataSetChanged();
}
@Override
public int getCount() {
return this.dataSource.size();
}
@Override
public E getItem(int position) {
return this.dataSource.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
V viewHolder = null;
if (convertView == null) {
viewHolder = createViewHolder(position, parent);
if (viewHolder == null || viewHolder.getRootView() == null) {
throw new NullPointerException("createViewHolder不能返回null或view为null的实例");
}
convertView = viewHolder.getRootView();
convertView.setTag(viewHolder);
}else{
viewHolder = (V) convertView.getTag();
}
//给当前复用的holder一个正确的position
viewHolder.setPosition(position);
bindViewHolder(viewHolder,position,getItem(position));
return viewHolder.getRootView();
}
protected abstract V createViewHolder(int position, ViewGroup parent);
protected abstract void bindViewHolder(V holder,int position, E data);
public static class BaseViewHolder {
private View rootView;
private SparseArray<View> viewCache = new SparseArray<>();
private int position = -1;
public View getRootView() {
return rootView;
}
void setPosition(int position) {
this.position = position;
}
public int getPosition() {
return position;
}
public BaseViewHolder(View rootView) {
this.rootView = rootView;
}
public <R> R getView(@IdRes int viewID) {
View cachedView = viewCache.get(viewID);
if(null == cachedView) {
cachedView = rootView.findViewById(viewID);
viewCache.put(viewID, cachedView);
}
return (R) cachedView;
}
}
}
1.我加一个构造函数,强制传context,并提供getContext()方法
2.加一个泛型E,允许子类提供随意实体类型
3.加一个BaseViewHolder,并提供一些常用方法
4.重写getView(),在父类里面把复用逻辑搞定,并提供两个抽象方法用于让子类提供viewholder,和绑定具体数据,嗯,我是仿着RecyclerView来的。:)
ok,这个通用的父类怎么使用呢?
代码说话:
public class FooSuperAdapter extends LBaseAdapter<FooBean, LBaseAdapter.BaseViewHolder> {
public FooSuperAdapter(Context context) {
super(context);
}
@Override
protected BaseViewHolder createViewHolder(int position, ViewGroup parent) {
return new BaseViewHolder(View.inflate(getContext(), R.layout.foo_item,null));
}
@Override
protected void bindViewHolder(BaseViewHolder holder, int position, FooBean data) {
TextView txtTitle = holder.getView(R.id.txtTitle);
TextView txtContent = holder.getView(R.id.txtContent);
txtTitle.setText(data.getTitle());
txtContent.setText(data.getContent());
}
}
之前的一大堆东西,现在都不用关心了, 只管设置itemview,和绑定数据就好了,是不是好看多了呢?
通用adapter用于了泛型,如果不了解可以留言,我将出一个泛型的专题来讨论希望我的博客能帮到你 :)