今天随便逛逛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上了,请猛击这里。