一、万用适配器简介
我们在做Android开发的时候,经常会用到ListView这个控件,我之前写过一篇文章:BaseAdapter与ListView解析。一般来说,我们都会为每一个ListView去建立一个适配器,那这样一个大型项目,不是要建立很多个适配器?!所以,为了提高代码的复用性,我们可以打造一个通用的适配器GeneralAdapter。
二、ViewHolder和GeneralAdapter的编写
一般我们编写Adapter时都会用到ViewHolder,通过convertView.setTag与convertView进行绑定,然后当convertView已经加载过时,直接从与之对于的ViewHolder(getTag)中取得convertView布局中的控件。也就是说,实际上们每个convertView会绑定一个ViewHolder对象,这个viewHolder主要用于帮convertView存储布局中的控件。所以我们只要写出一个通用的ViewHolder,然后对于任意的convertView,提供一个对象让其setTag即可;所以我们使用容器,专门存每个Item布局中的所有控件,而且还要能够查找出来;既然需要查找,那么ListView肯定是不行了,需要一个键值对进行保存,键为控件的Id,值为控件的引用,我们使用比Map效率更高的SparseArray这个类。
以下是ViewHolder类的代码,具体的解释写在代码注释里面:
/**
* Created by ZYQ on 2016/10/14.
* 一个通用的ViewHolder类
*/
public class ViewHolder {
// 使用了SparseArray用于存储与之对于的convertView的所有的控件
private SparseArray mViews;
private int mPosition;
private View mConvertView;
public ViewHolder(Context context, ViewGroup parent, int layoutID, int position) {
this.mPosition = position;
this.mViews = new SparseArray();
mConvertView = LayoutInflater.from(context).inflate(layoutID, parent, false);
mConvertView.setTag(this);
}
// 拿到一个ViewHolder对象
public static ViewHolder get(Context context, View convertView,
ViewGroup parent, int layoutID, int position) {
if (convertView == null) {
return new ViewHolder(context, parent, layoutID, position);
} else {
ViewHolder holder = (ViewHolder) convertView.getTag();
holder.mPosition = position;
return holder;
}
}
/**
* 使用泛型T,返回值是View的一个子类
* 通过viewID来获取控件
* 如果没有则将控件添加到SparseArray容器中
*/
public T getView(int viewID) {
View view = mViews.get(viewID);
if (view == null) {
view = mConvertView.findViewById(viewID);
mViews.put(viewID, view);
}
return (T) view;
}
public View getmConvertView() {
return mConvertView;
}
/**
* 封装setText方法
* 通过getView获得并设置TextView的值
*/
public ViewHolder setText(int viewID, String text) {
TextView tv = getView(viewID);
tv.setText(text);
return this;
}
/**
* 封装setImage方法
* 通过getView获得并设置ImageView的图片
*/
public ViewHolder setImage(int viewID, int imageResID) {
ImageView imageView = getView(viewID);
imageView.setImageResource(imageResID);
return this;
}
}
接下来我们需要的另一个重要的类就是GeneralAdapter。这个通用的Adapter一般需要保持一个List对象,存储一个Bean的集合,不同的ListView,Bean肯定是不同的,这个GeneralAdapter肯定需要支持泛型,内部维持一个List。
GeneralAdapter的代码如下,具体的解释写在代码注释里面:
/**
* Created by ZYQ on 2016/10/15.
* 将GeneralAdapter写成任何适配器的抽象基类
* 只有getView()中的convert方法是需要子类去实现的
*/
public abstract class GeneralAdapter extends BaseAdapter {
// 使用泛型可以装载多种Bean类型
protected List mDatas;
protected Context mContext;
protected LayoutInflater mInflater;
protected int mLayoutID;
public GeneralAdapter(Context context, List datas, int layoutID) {
this.mContext = context;
this.mDatas = datas;
mInflater = LayoutInflater.from(context);
this.mLayoutID = layoutID;
}
@Override
public int getCount() {
return mDatas.size();
}
@Override
public T getItem(int position) {
return mDatas.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 实例化一个viewHolder
ViewHolder viewHolder = ViewHolder.get(mContext, convertView, parent, mLayoutID, position);
convert(viewHolder, getItem(position));
return viewHolder.getmConvertView();
}
// 对外公布的convert方法,并将holder和Bean对象传出去
public abstract void convert(ViewHolder holder, T t);
}
完成了GeneralAdapter的编写之后,我们再也不需要为每一个ListView编写一个Adapter了,我们只需要在Activity里面使用匿名内部类就行了。
三、万用适配器实例测试
在activity_main里面添加listView控件,然后编写item布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/imageView"
android:layout_width="60dp"
android:layout_height="60dp"
android:src="@mipmap/ic_launcher"/>
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="30dp"
android:text="Title"
android:layout_toEndOf="@+id/imageView"
android:gravity="center"
android:textSize="25sp"/>
<TextView
android:id="@+id/tv_content"
android:layout_width="match_parent"
android:layout_height="30dp"
android:text="Content what what what what"
android:layout_toEndOf="@+id/imageView"
android:layout_below="@id/tv_title"
android:gravity="center_vertical"
android:textSize="20sp"/>
RelativeLayout>
接下来,我们只需要在MainActivity中实现ListView和初始化数据就好了(真正做项目开发的时候,为了降低代码的耦合度,还是建议遵循一定的设计模式,不要将数据的处理写在Activity里面,这里只是做个测试)
public class MainActivity extends AppCompatActivity {
private List mDatas;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initDatas();
initView();
}
private void initDatas() {
mDatas = new ArrayList();
for (int i = 0; i < 20; i++) {
mDatas.add(new ItemBean(
R.mipmap.ic_launcher, "标题", "我是正文 我是正文 我是正文"));
}
}
private void initView() {
ListView mListView = (ListView) findViewById(R.id.listview_main);
if (mListView != null) {
// 使用匿名内部类简化代码
mListView.setAdapter(new GeneralAdapter(MainActivity.this, mDatas, R.layout.item) {
@Override
public void convert(ViewHolder holder, ItemBean itemBean) {
// 使用链式编程
holder.setImage(R.id.imageView, R.mipmap.ic_launcher)
.setText(R.id.tv_title, itemBean.getItemTitle())
.setText(R.id.tv_content, itemBean.getItemContent());
}
});
}
}
}
最终实现的效果图还是和上一篇文章一样:
这样一来,代码量是不是简化了不少呢?