代码优化——抽象ViewHolder和BaseAdapter

本文探讨如何通过抽象ViewHolder和BaseAdapter来简化Android应用中ListView和GridView适配器的实现,减少重复代码。作者提出创建一个通用的CommonViewHolder,包含初始化ViewHolder的方法和获取Item布局的接口,以及一个CommonAdapter抽象基类,用于接受数据模型和ViewHolder。通过这种方式,当需要新增或修改ListView时,只需几步即可完成:定义新的布局文件,创建继承自CommonViewHolder的子类,以及实现继承自CommonBaseAdapter的子类。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文介绍通过抽象ViewHolder和BaseAdapter来简化我们重写BaseAdapter时的步骤

前提描述:

我们在使用ListView时自然要用ViewHolder来优化ListView,步骤往往是,1.继承BaseAdapter,2.创建一个ViewHolder类,3.在getView中做处理

当我们app有很多很多个ListView或者GridView时,写很多很多BaseAdapter就是个麻烦事了...

既然ViewHolder都是类似的,我们何不把他抽象出来。还有BaseAdapter也可以在抽象一层出来,会省很多事哦。


抽象共同的ViewHolder

ViewHolder的工作是完成ViewHolder下面定义的各种子控件,并在必要的时候把这个ViewHolder setTag给getView 的 convertView中,复用的时候再拿出来继续用。除此之外就没有其他的工作了。回想一下常规的写法,我们需要在getView中用LayoutInflate.from(this).inflate()去初始化ViewHolder。所以ViewHolder初始化的方法不能少,初始化的时候需要根据layoutId,所有得有一个接口留给子类调用。

抽象ViewHolder

——view viewInflate(context,viewgroup,attach)

——getItemLayout() 留给子类实现的接口,届时传递一个布局的id经来,用来初始化ViewHolder

初始化的控件需要放在一个容器中暂时存起来

容器ViewHolder

——容器

——View 当前视图

——根据当前视图找自己的子控件

CommonViewHolder.java (抽象的ViewHolder)

public abstract class CommonViewHolder {

	private Context mContext;
	/**
	 * 当前项的view
	 */
	private View mItemView;
	/**
	 * 用来零时存储ViewHolder的子View和初始化它们
	 */
	private CommonViewHolderHelper mHolderHelper;

	/**
	 * 用来加载ViewHolder的视图View,根据layoutId
	 */
	public View viewInflate(Context context, ViewGroup parent, boolean attachToRoot) {
		mContext = context;
		mItemView = LayoutInflater.from(mContext).inflate(getItemLayout(), parent, attachToRoot);
		mItemView.setTag(this);
		mHolderHelper = new CommonViewHolderHelper(mItemView);
		initItemView();
		return mItemView;
	}

	/**
	 * 根据layoutI找到ItemView,这个工作只在这里声明,然后在viewInflaite里面调用,具体的实现工作交给子类去完成
	 * 
	 * @return  发挥当前ViewHolder所需的布局或者是视图
	 */
	public abstract int getItemLayout();

	/**
	 * 初始化item的子View
	 */
	public void initItemView() {
	}

	/**
	 * 初始化View根据layoutId
	 * 
	 * @param layoutId  需要初始化的View的id
	 * @return
	 */
	public <T extends View> T findViewById(int layoutId) {
		return mHolderHelper.findViewById(layoutId);
	}
}
CommonViewHolderHelper.java (抽象的ViewHolder帮助类,用来存子View)

public class CommonViewHolderHelper {

	/**
	 * 用来装ViewHolder的View的容器
	 */
	private SparseArray<View> mViewArray = new SparseArray<View>();
	/**
	 * ViewHolder的布局
	 */
	private View mConvertView;

	public CommonViewHolderHelper(View view) {
		mConvertView = view;
	}

	public CommonViewHolderHelper(Context context, int layoutId) {
		this(context, null, layoutId);
	}

	public CommonViewHolderHelper(Context context, ViewGroup group, int layoutId) {
		this(LayoutInflater.from(context).inflate(layoutId, group, false));
	}

	public View getView() {
		return mConvertView;
	}

	/**
	 * 从SparseArray找出子View根据id
	 * 
	 * @param layoutId  返回itemview中子控件时所需的layoutid
	 * @return
	 */
	public <T extends View> T findViewById(int layoutId) {
		View view = mViewArray.get(layoutId);
		if (view == null) {
			view = mConvertView.findViewById(layoutId);
			if (view != null) {
				mViewArray.put(layoutId, view);
			}
		}
		return view == null ? null : (T) view;
	}
}

然后接下来就是重写BaseAdapter了,前面我们定义的CommonViewHolder就是给这一步准备的。我们以泛型的方式把CommonViewHolder和数据模型传给CommonBaseAdapter,然后在CommonBaseAdapter里面做好处理,并且留给外界访问的接口,到时候直接调用就跟方便了。

留给子类以传入ViewHolder的接口,getView使用该返回值来进行ViewHolder的初始化。

留给子类对ItemView的数据进行操作的接口

CommonAdapter.java (抽象一个BaseAdapter,传入数据模型和ViewHolder)

public abstract class CommonAdapter<T, H extends CommonViewHolder> extends BaseAdapter {
	private Context mContext;
	/**
	 * 用来存储adapter数据的List
	 */
	private List<T> mDatas = new ArrayList<T>();

	public CommonAdapter(Context context) {
		this.mContext = context;
	}

	public void setDatas(List<T> list) {
		mDatas = list;
		notifyDataSetChanged();
	}

	@Override
	public int getCount() {
		return mDatas.size();
	}

	@Override
	public Object getItem(int arg0) {
		return mDatas.get(arg0);
	}

	@Override
	public long getItemId(int arg0) {
		return arg0;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup group) {
		if (convertView == null) {
			convertView = initViewHolder().viewInflate(mContext, group, false);
		}
		H h = (H) convertView.getTag();
		initItemData(position, h, convertView);
		return convertView;
	}

	/**
	 * 返回ViewHolder子视图的抽象方法,在getView中调用,然后在子类中具体实现
	 * 
	 * @return 返回ViewHolder
	 */
	protected abstract H initViewHolder();

	/**
	 * 设置Item中的数据,抽象的方法,在getView中调调用,然后在子类中初始化
	 * 
	 * @param position
	 *            itemview的索引
	 * @param viewholder
	 *            实现的ViewHolder子类
	 * @param root
	 *            itemview 的子视图
	 */
	protected abstract void initItemData(int position, H viewholder, View root);

}

这样就大功告成了,我们来看看如何使用:

1.创建一个ViewHolder,它继承自CommonViewHolder

2.用刚刚创建好的ViewHolder来创建CommonBaseAdapter

3.给数据,调用

MyViewHolder.java (测试用的ViewHolder)

public class MyViewHolder extends CommonViewHolder {
	public TextView mTitle;
	public TextView mContent;

	@Override
	public int getItemLayout() {
		return R.layout.test_listview_item;
	}

	@Override
	public void initItemView() {
		super.initItemView();
		mTitle = findViewById(R.id.test_listview_item1);
		mContent = findViewById(R.id.test_listview_item2);
	}

}
所需的布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/test_listview_item1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_marginRight="76dp"
        android:layout_marginTop="33dp" />

    <TextView
        android:id="@+id/test_listview_item2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="40dp"
        android:layout_marginTop="16dp" />

</RelativeLayout>
MyAdapter.java (使用上面的MyViewHolder来创建一个Adapter)
public class MyAdapter extends CommonAdapter<TestBean, MyViewHolder> {

	public MyAdapter(Context context) {
		super(context);
	}

	@Override
	protected MyViewHolder initViewHolder() {
		return new MyViewHolder();
	}

	@Override
	protected void initItemData(int position, MyViewHolder viewholder, View root) {
		TestBean bean = (TestBean) getItem(position);
		viewholder.mTitle.setText(bean.getmTitle().toString());
		viewholder.mContent.setText(bean.getmContent().toString());
	}

}

在Activity中使用时:

@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mListView = (ListView) findViewById(R.id.test_listview);
		mBeans = new ArrayList<TestBean>();
		for (int i = 0; i < 100; i++) {
			TestBean bean = new TestBean("title=" + i, "content" + i);
			mBeans.add(bean);
		}
		// mListView.setAdapter(new TestAdapter(this, mBeans));
		MyAdapter adapter = new MyAdapter(this);

		// MyAdapter2 adapter = new MyAdapter2(this);
		// adapter.setDatas(mBeans);
		mListView.setAdapter(adapter);
	}
效果:



然后,当我们有一个新的ListView的时候,或者要给这个ListView跟换内容时,只需要三步

1.新建一个布局文件

2.新建一个CommonViewHolder的子类,在里面初始化子控件

3.新建一个CommentBaseAdapter的子类,在里面给子控件赋值,把这个adapter设置给ListView

下载链接:http://download.csdn.net/detail/u013045971/9215387






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值