您的位置:首页 > 移动开发 > Android开发

Android中适用于ListView、GridView等组件的通用Adapter

2015-05-21 16:36 525 查看
今天随便逛逛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 :

[html] view
plaincopy





<?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代码

[java] view
plaincopy





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后要写的代码

[java] view
plaincopy





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是准备了一些数据, 代码如下 :

[java] view
plaincopy





/**

* 模拟一些数据

*

* @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实现

[java] view
plaincopy





/**

*

* 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实现

[java] view
plaincopy





/**

*

* 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辅助类

[java] view
plaincopy





/**

* 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上了,请猛击这里
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐