Android中適用于ListView、GridView等組件的通用Adapter
來源:程序員人生 發布時間:2014-10-12 19:33:18 閱讀次數:3858次
今天隨便逛逛CSDN,看到主頁上推薦了一篇文章Android 快速開發系列 打造萬能的ListView GridView 適配器,剛好這兩天寫項目自己也封裝了類似的CommonAdapter,以前也在github上看到過這樣的庫,于是自己也把自己的代碼再次整理出來與大家分享,也希望能夠在CSDN這個平臺上學到更多的東西,下面就一起來看看吧。
平時我們在項目中使用到ListView和GridView組件都是都會用到Adapter,比較多的情況是繼承自BaseAdapter,然后實現getCount、getView等方法,再使用ViewHolder來提高一下效率.我們看下面一個簡單的例子 :
ListView布局文件
fragment_main.xml :
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.push_demo_1.MainActivity$PlaceholderFragment" >
<ListView
android:id="@+id/my_listview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
ListView子項的布局文件
listview_item_layout.xml :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<ImageView
android:id="@+id/my_imageview"
android:layout_width="64dp"
android:layout_height="64dp"
android:contentDescription="@string/app_name" />
<TextView
android:id="@+id/my_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:textSize="18sp" />
</LinearLayout>
曾經我們要寫下的Adapter代碼
public class NormalAdapter extends BaseAdapter {
Context mContext;
LayoutInflater mInflater;
List<ListViewItem> mDataList;
/**
* @param context
* @param data
*/
public NormalAdapter(Context context, List<ListViewItem> data) {
mContext = context;
mInflater = LayoutInflater.from(context);
mDataList = data;
}
@Override
public int getCount() {
return mDataList.size();
}
@Override
public ListViewItem getItem(int position) {
return mDataList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.listview_item_layout, null, false);
viewHolder = new ViewHolder();
viewHolder.mImageView = (ImageView) convertView.findViewById(R.id.my_imageview);
viewHolder.mTextView = (TextView) convertView.findViewById(R.id.my_textview);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.mImageView.setImageResource(getItem(position).mDrawableId);
viewHolder.mTextView.setText(getItem(position).mText);
return convertView;
}
/**
* ViewHolder
*
* @author mrsimple
*/
static class ViewHolder {
ImageView mImageView;
TextView mTextView;
}
}
然而寫過多遍以后我們發現我們總是重復地在寫這些getCount、getItem、getView方法以及ViewHolder,導致了很多重復工作,而且及其無聊,于是我把這些重復工作抽象起來(以前也有在github上看到這樣的通用Adapter實現),整理一下也便于自己使用,也是自己學習的一個過程。下面我們看看使用CommonAdapter后我們做與上面同樣的工作需要怎么寫。
使用CommonAdapter后要寫的代碼
CommonAdapter<ListViewItem> listAdapter = new CommonAdapter<ListViewItem>(getActivity(),
R.layout.listview_item_layout, mockListViewItems()) {
@Override
protected void fillItemData(CommonViewHolder viewHolder, ListViewItem item) {
// 設置圖片
viewHolder.setImageForView(R.id.my_imageview, item.mDrawableId);
// 設置text
viewHolder.setTextForTextView(R.id.my_textview, item.mText);
}
}
其中mockListViewImtes是準備了一些數據, 代碼如下 :
/**
* 模擬一些數據
*
* @return
*/
private List<ListViewItem> mockListViewItems() {
List<ListViewItem> dataItems = new ArrayList<ListViewItem>();
dataItems.add(new ListViewItem(R.drawable.girl_96, "girl_96.png"));
dataItems.add(new ListViewItem(R.drawable.fire_96, "fire_96.png"));
dataItems.add(new ListViewItem(R.drawable.grimace_96, "grimace_96.png"));
dataItems.add(new ListViewItem(R.drawable.laugh_96, "laugh_96.png"));
return dataItems;
}
可以看到,我們的代碼量減少了很多,如果一個項目中有好幾個ListView、GridView等組件,我們就不需要重復做那么多無聊的工作了。我們看看效果圖 :

CommonAdapter實現
/**
*
* created by Mr.Simple, Aug 28, 201412:26:52 PM.
* Copyright (c) 2014, hehonghui@umeng.com All Rights Reserved.
*
* #####################################################
* # #
* # _oo0oo_ #
* # o8888888o #
* # 88" . "88 #
* # (| -_- |) #
* # 0 = /0 #
* # ___/`---'\___ #
* # .' | |# '. #
* # / ||| : |||# #
* # / _||||| -:- |||||- #
* # | | - #/ | | #
* # | \_| ''---/'' |_/ | #
* # .-\__ '-' ___/-. / #
* # ___'. .' /--.-- `. .'___ #
* # ."" '< `.___\_<|>_/___.' >' "". #
* # | | : `- `.;` _ /`;.`/ - ` : | | #
* # `_. \_ __ /__ _/ .-` / / #
* # =====`-.____`.___ \_____/___.-`___.-'===== #
* # `=---=' #
* # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
* # #
* # 佛祖保佑 永無BUG #
* # #
* #####################################################
*/
package com.uit.commons;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import java.util.List;
/**
* 這是一個通用、抽象的適配器類,覆寫了BaseAdapter的getCount, getItem, getItemId,
* getView方法,在getView方法中通過
* 通用的CommonViewHolder來對convertView的進行處理,并且緩存convertView中的其他View元素
* ,降低了ListView、GridView 等組件的Adapter和ViewHolder的代碼量.
* 用戶只需要在fillItemData函數中將第position位置里的數據填充到listview或者gridview的第position的view中即可
* ,具體使用實例參考文檔.
*
* @author mrsimple
* @param <T> 數據源的類型
*/
public abstract class CommonAdapter<T> extends BaseAdapter {
/**
* Context
*/
Context mContext;
/**
* 要展示的數據列表
*/
List<T> mData;
/**
* 每一項的布局id,例如R.layout.my_listview_item.
*/
private int mItemLayoutId = -1;
/**
* @param context Context
* @param itemLayoutResId
* 每一項(適用于listview、gridview等AbsListView子類)的布局資源id,例如R.layout.
* my_listview_item.
* @param dataSource 數據源
*/
public CommonAdapter(Context context, int itemLayoutResId, List<T> dataSource) {
checkParams(context, itemLayoutResId, dataSource);
mContext = context;
mItemLayoutId = itemLayoutResId;
mData = dataSource;
}
/**
* 檢查參數的有效性
*
* @param context
* @param itemLayoutResId
* @param dataSource
*/
private void checkParams(Context context, int itemLayoutResId, List<T> dataSource) {
if (context == null || itemLayoutResId < 0 || dataSource == null) {
throw new RuntimeException(
"context == null || itemLayoutResId < 0 || dataSource == null, please check your params");
}
}
/**
* 返回數據的總數
*/
@Override
public int getCount() {
return mData.size();
}
/**
* 返回position位置的數據
*/
@Override
public T getItem(int position) {
return mData.get(position);
}
/**
* item id, 返回position
*/
@Override
public long getItemId(int position) {
return position;
}
/**
* 返回position位置的view, 即listview、gridview的第postion個view
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 獲取ViewHolder
CommonViewHolder viewHolder = CommonViewHolder.getViewHolder(mContext, convertView,
mItemLayoutId);
// 填充數據
fillItemData(viewHolder, getItem(position));
// 返回convertview
return viewHolder.getConvertView();
}
/**
* 用戶必須覆寫該方法來講數據填充到視圖中
*
* @param viewHolder 通用的ViewHolder, 里面會裝載listview,
* gridview等組件的每一項的視圖,并且緩存其子view
* @param item 數據源的第position項數據
*/
protected abstract void fillItemData(CommonViewHolder viewHolder, T item);
}
CommonViewHolder實現
/**
*
* created by Mr.Simple, Aug 28, 201412:32:45 PM.
* Copyright (c) 2014, hehonghui@umeng.com All Rights Reserved.
*
* #####################################################
* # #
* # _oo0oo_ #
* # o8888888o #
* # 88" . "88 #
* # (| -_- |) #
* # 0 = /0 #
* # ___/`---'\___ #
* # .' | |# '. #
* # / ||| : |||# #
* # / _||||| -:- |||||- #
* # | | - #/ | | #
* # | \_| ''---/'' |_/ | #
* # .-\__ '-' ___/-. / #
* # ___'. .' /--.-- `. .'___ #
* # ."" '< `.___\_<|>_/___.' >' "". #
* # | | : `- `.;` _ /`;.`/ - ` : | | #
* # `_. \_ __ /__ _/ .-` / / #
* # =====`-.____`.___ \_____/___.-`___.-'===== #
* # `=---=' #
* # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #
* # #
* # 佛祖保佑 永無BUG #
* # #
* #####################################################
*/
package com.uit.commons;
import android.content.Context;
import android.graphics.Bitmap;
import android.view.View;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;
import com.uit.commons.utils.ViewFinder;
/**
* 這是一個通用的ViewHolder, 將會裝載AbsListView子類的item View, 并且將item
* view中的子視圖進行緩存和索引,使得用戶能夠方便的獲取這些子view, 減少了代碼重復。
*
* @author mrsimple
*/
public class CommonViewHolder {
/**
* 構造函數
*
* @param context Context
* @param layoutId ListView、GridView或者其他AbsListVew子類的 Item View的資源布局id
*/
protected CommonViewHolder(Context context, int layoutId) {
// 初始化布局, 裝載ContentView
ViewFinder.initContentView(context, layoutId);
// 將ViewHolder存儲在ContentView的tag中
ViewFinder.getContentView().setTag(this);
}
/**
* 獲取CommonViewHolder,當convertView為空的時候從布局xml裝載item view,
* 并且將該CommonViewHolder設置為convertView的tag, 便于復用convertView.
*
* @param context Context
* @param convertView Item view
* @param layoutId 布局資源id, 例如R.layout.my_listview_item.
* @return 通用的CommonViewHolder實例
*/
public static CommonViewHolder getViewHolder(Context context, View convertView, int layoutId) {
if (convertView == null) {
return new CommonViewHolder(context, layoutId);
}
return (CommonViewHolder) convertView.getTag();
}
/**
* @return 當前項的convertView, 在構造函數中裝載
*/
public View getConvertView() {
return ViewFinder.getContentView();
}
/**
* 為id為textViewId的TextView設置文本內容
*
* @param textViewId 視圖id
* @param text 要設置的文本內容
*/
public void setTextForTextView(int textViewId, CharSequence text) {
TextView textView = ViewFinder.findViewById(textViewId);
if (textView != null) {
textView.setText(text);
}
}
/**
* 為ImageView設置圖片
*
* @param imageViewId ImageView的id, 例如R.id.my_imageview
* @param drawableId Drawable圖片的id, 例如R.drawable.my_photo
*/
public void setImageForView(int imageViewId, int drawableId) {
ImageView imageView = ViewFinder.findViewById(imageViewId);
if (imageView != null) {
imageView.setImageResource(drawableId);
}
}
/**
* 為ImageView設置圖片
*
* @param imageViewId ImageView的id, 例如R.id.my_imageview
* @param bmp Bitmap圖片
*/
public void setImageForView(int imageViewId, Bitmap bmp) {
ImageView imageView = ViewFinder.findViewById(imageViewId);
if (imageView != null) {
imageView.setImageBitmap(bmp);
}
}
/**
* 為CheckBox設置是否選中
*
* @param checkViewId CheckBox的id
* @param isCheck 是否選中
*/
public void setCheckForCheckBox(int checkViewId, boolean isCheck) {
CheckBox checkBox = ViewFinder.findViewById(checkViewId);
if (checkBox != null) {
checkBox.setChecked(isCheck);
}
}
}
ViewFinder輔助類
/**
* view finder, 方便查找View。用戶需要在使用時調用initContentView,
* 將Context和布局id傳進來,然后使用findViewById來獲取需要的view
* ,findViewById為泛型方法,返回的view則直接是你接收的類型,而不需要進行強制類型轉換.比如,
* 以前我們在Activity中找一個TextView一般是這樣 :
* TextView textView = (TextView)findViewById(viewId);
* 如果頁面中的控件比較多,就會有很多的類型轉換,而使用ViewFinder則免去了類型轉換,
* 示例如下 :
* TextView textView = ViewFinder.findViewById(viewId);
*
* @author mrsimple
*/
public final class ViewFinder {
/**
* LayoutInflater
*/
static LayoutInflater mInflater;
/**
* 每項的View的sub view Map
*/
private static SparseArray<View> mViewMap = new SparseArray<View>();
/**
* Content View
*/
static View mContentView;
/**
* 初始化ViewFinder, 實際上是獲取到該頁面的ContentView.
*
* @param context
* @param layoutId
*/
public static void initContentView(Context context, int layoutId) {
mInflater = LayoutInflater.from(context);
mContentView = mInflater.inflate(layoutId, null, false);
if (mInflater == null || mContentView == null) {
throw new RuntimeException(
"ViewFinder init failed, mInflater == null || mContentView == null.");
}
}
/**
* @return
*/
public static View getContentView() {
return mContentView;
}
/**
* @param viewId
* @return
*/
@SuppressWarnings("unchecked")
public static <T extends View> T findViewById(int viewId) {
// 先從view map中查找,如果有的緩存的話直接使用,否則再從mContentView中找
View tagetView = mViewMap.get(viewId);
if (tagetView == null) {
tagetView = mContentView.findViewById(viewId);
mViewMap.put(viewId, tagetView);
}
return tagetView == null ? null : (T) mContentView.findViewById(viewId);
}
}
代碼都在Github上了,請猛擊這里。
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈