支持多布局的CommonAdapter

支持多布局的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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值