支持多布局的CommonAdapter
以前总是打算要写一些博客,一方面是为了记录自己在Android开发中的心得,另一方面也跟大家分享一下,但总是觉得水平不够,总是拖着,心想在等等,后来看了看任玉刚大神的博客android学习路线:如何成长为高级工程师,觉得大神说的很有道理,“时不时把自己的知识汇总下写一篇博客出来,这对自己是一个提高,对别人也是一个帮助。这个怎么说呢,当你写博客,你就会发现一个知识你自己会了和写出来这是不一样的,能写出来才是真正地懂了。”我也是前一段时间一直再找listView中怎样设置多布局,通过查看大神写的一些优秀的代码自己稍微封装了一下做出来了,所以打算写一篇博客记录下来分享一下,也开启自己的博客之路,本人刚开始写博客,难免会出现一些问题,希望大家多多指教
图片效果
界面代码
public class MainActivity extends ActionBarActivity {
private ListView lv;
private List<String> mDatas = new ArrayList<String>();
private CommonAdapter<String> adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for (int i = 0; i < 200; i++) {
mDatas.add("当前的位置是=="+i);
}
lv = (ListView) findViewById(R.id.lv);
adapter = new CommonAdapter<String>(this, R.layout.item_list1, mDatas) {
@Override
public void convert(CommonViewHolder holder, String model,int position) {
switch (adapter.getItemViewType(position)) {
case 0:
holder.<TextView>getView(R.id.posttion).setText(model);;
break;
case 1:
holder.<ImageView>getView(R.id.iv).setBackgroundResource(R.drawable.emotion);
holder.<TextView>getView(R.id.posttion).setText(model);
break;
case 2:
holder.<ImageView>getView(R.id.iv).setBackgroundResource(R.drawable.emotion);
holder.<ImageView>getView(R.id.iv1).setBackgroundResource(R.drawable.expression_pressed);
holder.<TextView>getView(R.id.posttion).setText(model);
break;
}
}
};
adapter.setMultiItemTypeSupportListener(new MultiItemTypeSupportListener() {
/**
* 设置convertView的type类型
* @param position
* @return
*/
@Override
public int getItemViewType(int position) {
// TODO Auto-generated method stub
if (position % 3 == 0) {
return 0;
}
if (position % 10 == 0) {
return 1;
}
return 2;
}
/**
* 设置convertView的type数量
* @return
*/
@Override
public int getViewTypeCount() {
return 3;
}
/**
* 设置不同位置convertView的布局文件
* @param position
* @return
*/
@Override
public int getLayoutId(int position) {
if (position % 3 == 0) {
return R.layout.item_list;
}
if (position % 10 == 0) {
return R.layout.item_list1;
}
return R.layout.item_list2;
}
});
lv.setAdapter(adapter);
}
}
14行我们自定义一个CommonAdapter,其实它是继承BaseAdapter。我将它做成一个抽象的类,我们只需要实现convert这个抽象方法,并且为了适配不同的数据源我讲CommonAdapter设定了一个泛型(此处我的数据类型是Stirng类型的,你们自己用的时候可以设定相应的bean类型)。convert方法中是对数据加载和listView中item布局控件的封装,在下面将到CommonAdapter时候我们会具体分析。
36行我们调用了setMultiItemTypeSupportListener这个方法,其实这是一个自定义的监听,主要是为了实现listView不同item布局用的,其中getItemViewType方法,getViewTypeCount会在baseAdapter的getItemViewType方法,getViewTypeCount方法中调用,这两个方法是实现listView多布局的关键方法,下面介绍CommonAdapter时会说明一下
###CommonAdapter代码
public abstract class CommonAdapter<T> extends BaseAdapter {
private Context context;
// 为丰富程序功能,提供了两种常见的数据类型
private List<T> dataList = null;// 数据源List<T>
private T[] dataArray = null;// 数据源T[]
// 布局文件ID
private int layoutId;
protected MultiItemTypeSupportListener multiItemSupportListener;
/**
* 构造方法
*
* @param context
* @param layoutId
* @param dataList
*/
public CommonAdapter(Context context, int layoutId, List<T> dataList) {
this.context = context;
this.dataList = dataList;
this.layoutId = layoutId;
}
/**
* 构造方法(与上一个只有数据源不同)
*
* @param context
* @param layoutId
* @param dataArray
*/
public CommonAdapter(Context context, int layoutId, T[] dataArray) {
this.context = context;
this.dataArray = dataArray;
this.layoutId = layoutId;
}
public void setMultiItemTypeSupportListener(MultiItemTypeSupportListener multiItemSupportListener){
this.multiItemSupportListener = multiItemSupportListener;
}
@Override
public int getItemViewType(int position) {
if (multiItemSupportListener != null) {
return multiItemSupportListener.getItemViewType(position);
}
return super.getItemViewType(position);
}
@Override
public int getViewTypeCount() {
if (multiItemSupportListener != null) {
return multiItemSupportListener.getViewTypeCount();
}
return super.getViewTypeCount();
}
@Override
public int getCount() {
if (dataList != null) {
return dataList.size();
} else {
return dataArray.length;
}
}
@Override
public T getItem(int position) {
if (dataList != null) {
return dataList.get(position);
} else {
return dataArray[position];
}
}
@Override
public long getItemId(int position) {
if (multiItemSupportListener != null) {
return multiItemSupportListener.getLayoutId(position);
}
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
int layoutId;
if (multiItemSupportListener != null) {
layoutId = (int) getItemId(position);
}else{
layoutId = this.layoutId;
}
CommonViewHolder holder = CommonViewHolder.get(context, convertView, parent, layoutId, position);
convert(holder, getItem(position),position);
return holder.getConvertView();
}
/**
* 需实现的抽象方法
*
* @param holder
* @param model
*/
public abstract void convert(CommonViewHolder holder, T model,int position);
/**
* 支持多布局的item接口
*
* @author zlp
*
* @param <T>
*/
public interface MultiItemTypeSupportListener {
int getItemViewType(int position);
int getViewTypeCount();
int getLayoutId(int position);
}
}
CommonAdapter继承BaseAdapter 加泛型是为了设定数据源的类型,我们需要实现BaseAdapter的几个方法,其中getItemViewType(int position),getViewTypeCount()是实现多布局的关键方法。
讲到ListView的多布局的使用我们就要说一下RecycleBin机制,其实我们讲到listView中getView(int position, View convertView, ViewGroup parent)方法中convertView的复用主要运用RecycleBin机制,在RecycleBin中有ArrayList[] mScrapViews这样一个变量用来缓存复用的view,我们知道listView在item划出界面会将item中的view回收,其实就是将这个view缓存到mScrapViews中,等加载心得view时候,会调用getView这个方法,在此之前会从mScrapViews中取得缓存的view,取到就将其作为参数传给getView中convertView,否则就传入null,所以我们在getView中都要对convertView进行非空判断
如上所述复用convertView主要跟mScrapViews有关,那为什么说getItemViewType(int position),getViewTypeCount()是实现多布局的关键方法呢,ArrayList[] mScrapViews这个变量是一个数组,数组的元素是ArrayList,那么他是如何缓存复用的view呢,首先是mScrapViews这个数组的长度子啊初始化的时候会通过getViewTypeCount()的返回值来设定,另外缓存复用的view时候会通过getItemViewType(int position)的返回值决定由mScrapViews中那一个元素(元素是ArrayList)进行缓存,在复用缓存view时也是先通过getItemViewType(int position)的返回值决定由mScrapViews中那一个元素(元素是ArrayList)进行取出缓存的view,所以谨记一点getViewTypeCount()的返回值一定要大于等于getItemViewType(int position)返回的最大值
我们这里复写getItemViewType(int position)41行,getViewTypeCount()49行,这两个方法用到了自定义的监听MultiItemTypeSupportListener,这样实现是为了方便我们在代码中控制这个两个方法的返回值
接下来我们说一下getView(int position, View convertView, ViewGroup parent),84行我们先判断multiItemSupportListener != null,如果我们想使用多布局必定会设定这个监听,所以会进入85行,通过CommonAdapter的getItemId(position)返回一个id(这是个xml布局id,我们也可以不用CommonAdapter的这个方法设定不同的布局id,而是自定义一个方法来实现,但是我不想写太多的方法,所以就把CommonAdapter的这个方法重新实现作为不同position中返回不不同的item资源布局id),接着会进入89行,这里会创建一个CommonViewHolder, 我们通过 CommonViewHolder.get(context, convertView, parent, layoutId, position);返回一个 CommonViewHolder,我们将convertView,layoutId,等参数传入,通过判断convertView是否为空来决定重创建CommonViewHolder,还是复用已经存在的CommonViewHolder,具体的讲解我们会在CommonViewHolder代码中解释
###CommonViewHolder代码
public class CommonViewHolder {
private final SparseArray<View> mViews;
public int mPosition;
private View mConvertView;
public int layoutId;
CommonViewHolder(Context context, ViewGroup parent, int layoutId,
int position) {
this.mPosition = position;
this.layoutId = layoutId;
this.mViews = new SparseArray<View>();
mConvertView = LayoutInflater.from(context).inflate(layoutId, parent,
false);
// setTag
mConvertView.setTag(this);
}
/**
* 拿到一个ViewHolder对象
*
* @param context
* @param convertView
* @param parent
* @param layoutId
* @param position
* @return
*/
public static CommonViewHolder get(Context context, View convertView,
ViewGroup parent, int layoutId, int position) {
if (convertView == null) {
System.out.println("有新对象被创建了"+position);
return new CommonViewHolder(context, parent, layoutId, position);
}
// 复用convertView
CommonViewHolder existingHelper = (CommonViewHolder) convertView
.getTag();
return existingHelper;
}
public View getConvertView() {
return mConvertView;
}
/**
* 通过控件的Id获取对于的控件,如果没有则加入views
*
* @param viewId
* @return
*/
public <T extends View> T getView(int viewId) {
View view = mViews.get(viewId);
if (view == null) {
view = mConvertView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T)view;
}
public int getPosition() {
return mPosition;
}
}
我们自定义的CommonViewHolder原理和我们经常处理listView时定义的viewHolder没什么区别,只是我们这里用了一个成员变量SparseArray mViews对我们的mConvertView的子view进行一个缓存,52行getView方法中53行通过mViews.get(viewId),先从mViews中通过子view的id去取mConvertView的子view,如果没有缓存过肯定返回null就会进入55行, mConvertView.findViewById(viewId),通过id去取mConvertView的子view,接着56行通过mViews.put(viewId, view),将取到的子view缓存到mViews中,这样我们就可以通过CommonViewHolder的getView(int viewId)方法根据子view的id取出想要的子view
接着我们来说一说CommonViewHolder中最重要的方法CommonViewHolder get(Context context, View convertView,ViewGroup parent, int layoutId, int position),31行判断(convertView == null),第一次convertView肯定为null,所以进入到33行,创建一个CommonViewHolder变量直接返回,如果convertView是复用listView缓存在RecycleBin中的,会进入37行(CommonViewHolder) convertView .getTag()来获得。为什么convertView .getTag()可以的到CommonViewHolder,我们就不得不说一下CommonViewHolder的构造方法了,10行传入的layoutId,这是一个资源id就是我们要创建的mConvertView的具体的布局文件,12行会通LayoutInflater和layoutId创建出来mConvertView,15行会将创建的CommonViewHolder作为tag值设置给mConvertView。所以我们进入31行get方法中,只有当mConvertView为null是才会重新创建CommonViewHolder,如果是复用mConvertView这时mConvertView的tag是有值的而且是一个CommonViewHolder
源码下载连接[这里写链接内容]http://download.csdn.net/download/qq_25652907/9784298