对BaseAdapter的进一步封装,使得BaseAdapter用起来更方便

原创 2015年11月21日 13:52:24

一个项目中一般会使用到多个ListView,在看了慕课网的“打造万能适配器BaseAdapter”之后,我第一次发现原来BaseAdapter被封装过后再使用是如此地简单,下面我记录一下封装的全过程:

1.因为ListView中要使用到ViewHolder来避免多次组件重复加载的情况,所以这里首先把ViewHolder封装成一个对象:

import android.content.Context;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;

public class ViewHolder {
	/*
	 * 使用SparseArray将读取速度提高,因为这个类是android包中的类,
	 * 它比HashMap还要快,这个在android的API中有说道:
	 * It is intended to be more memory efficientthan 
	 * using a HashMap to map Integers to Objects
	 */
	private SparseArray<View> views;
	/*
	 * position的保存为了以后如果要保存的是CheckBox这样的组件的时候,
	 * 会出现选中一个就选中了多个的现象。
	 */
	private int position;
	/*
	 * 保存BaseAdapter中getView方法的convertView,用来在这里findViewById组件
	 */
	private View convertView;
	
	/**
	 * 构造方法,这里接收的参数都是在BaseAdapter的getView方法中的参数。
	 * @param context
	 * @param parent
	 * @param layoutId
	 * @param position
	 */
	public ViewHolder(Context context, ViewGroup parent, int layoutId, int position) {
		/*
		 * 完成各种初始化
		 */
		this.position = position;
		views = new SparseArray<View>();
		convertView = LayoutInflater.from(context).inflate(layoutId, parent, false);
		/*
		 * 把传统的setTag放在,因为第一次初始化ViewHolder的时候
		 * convertView也是第一次创建。
		 */
		convertView.setTag(this);
	}
	
	/**
	 * 得到ViewHolder
	 * @param context 传入的context,如果没有创建过convertView和ViewHolder,那么就要用到这个参数来创建ViewHolder和convertView
	 * @param convertView getView中的convertView
	 * @param parent getView中的ViewGroup,用来初始化convertView
	 * @param layoutId ListView中的每一个Item布局
	 * @param position 表示这是第几个列表项
	 * @return ViewHolder对象
	 */
	public static ViewHolder getViewHolder(Context context, View convertView
			, ViewGroup parent, int layoutId, int position) {
		/*
		 * 如果convertView为空,那么构造一个ViewHolder就行了,因为构造方法里实现了convertView的初始化
		 */
		if (convertView == null) {
			return new ViewHolder(context, parent, layoutId, position);
		} else {
			/*
			 * 如果不为空,那么只需要getTag即可
			 */
			ViewHolder holder = (ViewHolder) convertView.getTag();
			holder.position = position;
			return holder;
		}
	}
	
	/**
	 * 通过组件的id获取组件,因为组件的父类都是View,所以这里用了泛型
	 * @param viewId 组件的id
	 * @return 该组件View
	 */
	public <T extends View> T getView(int viewId) {
		/*
		 * 如果改组件已经初始化,那么SparseArray一定保存了改组件,
		 * 所以只需要判断从SparseArray获取指定viewId的组件如果不为空,就新建,否则就返回即可
		 */
		View view = views.get(viewId);
		if (view == null) {
			view = convertView.findViewById(viewId);
			views.append(viewId, view);
		}
		return (T) view;
	}
	
	/**
	 * 返回convertView,因为BaseAdapter中的getView方法最后要返回convertView,
	 * 所以也把返回封装到这里来
	 * @return convertView
	 */
	public View getConvertView() {
		return convertView;
	}
	
	/**
	 * 为指定组件TextView来setText,这里使用了链式编程,返回的是ViewHolder本身
	 * @param id 该TextView的id
	 * @param str 要设置的内容
	 * @return ViewHolder
	 */
	public ViewHolder setText(int id, String str) {
		((TextView)getView(id)).setText(str);
		return this;
	}
	
	/**
	 * 为CheckBox设置内容
	 * @param id
	 * @param str
	 * @return
	 */
	public ViewHolder setCheckBoxText(int id, String str) {
		((CheckBox)getView(id)).setText(str);
		return this;
	}
	/**
	 * 为Button设置内容
	 * @param id
	 * @param str
	 * @return
	 */
	public ViewHolder setButtonText(int id, String str) {
		((Button)getView(id)).setText(str);
		return this;
	}

}

2.新建一个CommonAdapter来实现通用的Adapter,这个类是抽象类:

import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;

public abstract class CommonAdapter<T> extends BaseAdapter {
	protected Context context;
	protected List<T> datas;
	protected int layoutId;
	protected LayoutInflater inflater;
	
	public CommonAdapter(Context context, List<T> datas, int layoutId) {
		// TODO Auto-generated constructor stub
		this.context = context;
		this.datas = datas;
		this.layoutId = layoutId;
		inflater = LayoutInflater.from(context);
	}

	@Override
	public int getCount() {
		// TODO Auto-generated method stub
		return datas.size();
	}

	@Override
	public Object getItem(int position) {
		// TODO Auto-generated method stub
		return datas.get(position);
	}

	@Override
	public long getItemId(int position) {
		// TODO Auto-generated method stub
		return position;
	}

	/**
	 * 这里把getView都封装起来了,因为这里的ViewHolder和返回值都是多次重复的
	 * 所以用户只需要实现的是往ViewHolder中的组件设置内容或者监听器这些即可,那
	 * 么这里使用了抽象类的回调方法来实现,对外提供一个convert方法,这个方法就是
	 * 知道了viewHolder对象,但是工具类的类型不知道,这里我们使用了泛型,用户只
	 * 需要实现这个抽象方法,然后怎么设置就要看用户的实际需求了
	 */
	@Override
	public  View getView(int position, View convertView, ViewGroup parent) {
		ViewHolder viewHolder = ViewHolder.getViewHolder(context, convertView, parent, layoutId, position);
		convert(viewHolder, datas.get(position));
		return viewHolder.getConvertView();
	}
	/**
	 * 对外提供的抽象方法,用户只需要考虑如何实现这个方法即可
	 * @param viewHolder
	 * @param t
	 */
	public abstract void convert(ViewHolder viewHolder, T t);
	

}

3.有了这个CommonAdapter之后,那么用户需要继承的就不是BaseAdapter了,用户只需要继承这个CommonAdapter,实现里面的convert方法即可,这就大大减少了用户的代码量,因为很多操作都封装起来了,这对用户来说是透明的。实现的方法如下:

public class MyAdapterWithCommonHolder extends CommonAdapter<Bean> {
	
	public MyAdapterWithCommonHolder(Context context, List<Bean> datas, int layoutId) {
		// TODO Auto-generated constructor stub
		super(context, datas, layoutId);
	}

	/**
	 * 实现convert方法,在实现这个方法的时候就,第二个参数就不是泛型类了
	 * 而是一个具体的类
	 */
	@Override
	public void convert(ViewHolder viewHolder, final Bean t) {
		// TODO Auto-generated method stub
		/*
		 * 里面的操作用户自行设置,这里可以添加监听器,设置内容等
		 * 下面给出一个实例
		 */
		viewHolder.setText(R.id.tv, t.getTxt())
			.setCheckBoxText(R.id.cb, t.getTxt())
			.setButtonText(R.id.btn, t.getTxt());
		final CheckBox cb = viewHolder.getView(R.id.cb);
		cb.setChecked(t.isCheck());
		cb.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				t.setCheck(cb.isChecked());
			}
		});
	}

}

4.接下来只要使用listView的setAdapter方法设置Adapter即可


从上面可以看出,用户自己实现的代码就只有convert方法,用户甚至可以直接用匿名来实现,例如:

lv.setAdapter(new CommonAdapter<Bean>(MainActivity.this, datas) {
			public void convert(ViewHolder viewHolder, Bean t) {
				viewHolder.setText(R.id.tv, t.getTxt());
			};
		});
这里的lv代表listView。

版权声明:本文为博主原创文章,未经博主允许不得转载。

超级封装BaseAdapter,让你省下千万行代码

首先打造超级Viewholder package com.ljh.powerfulladapter.util;import android.content.Context; import androi...

Android开发技巧——BaseAdapter的另一种优雅封装

RecyclerView虽然因其灵活性、高效性等特点而备受好评,但也不是一定得用它把ListView给替代掉。在某些场景中,ListView还是相对更适合的。比如数据量不大,不频繁更新,并且需要简单地...

9个小窍门让OS X中Finder用起来更顺手

From: http://digi.tech.qq.com/a/20130309/000051.htm

十招让Ubuntu 16.04用起来更得心应手

Ubuntu 16.04是一种长期支持版本(LTS),是Canonical承诺发布五年的更新版。也就是说,你可以让这个版本在电脑上运行五年!这样一来,一开始就设置好显得特别重要。你应该确保你的软件是最...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:对BaseAdapter的进一步封装,使得BaseAdapter用起来更方便
举报原因:
原因补充:

(最多只允许输入30个字)