代碼優化——抽象ViewHolder和BaseAdapter

本文介紹通過抽象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






發佈了95 篇原創文章 · 獲贊 18 · 訪問量 31萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章