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

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

时间: 2024-10-02 23:37:48

Android中适用于ListView、GridView等组件的通用Adapter的相关文章

Android PullToRefresh (ListView GridView 下拉刷新) 使用详解

Android PullToRefresh (ListView GridView 下拉刷新) 使用详解 标签: Android下拉刷新pullToRefreshListViewGridView 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38238749,本文出自:[张鸿洋的博客] 群里一哥们今天聊天偶然提到这个git hub上的控件:pull-to-r

【转】整理一下Android中的ListView

原文网址:http://sunbofu.blog.51cto.com/6431507/1280441 Android中的listview目测是一个使用频率很高的组件,所以今天来总结一下listview的基础的用法. 一.最基本的绑定 java代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45

Android中使用ListView绘制自定义表格(2)

上回再写了<Android中使用ListView绘制自定义表格>后,很多人留言代码不全和没有数据样例.但因为项目原因,没法把源码全部贴上来.近两天,抽空简化了一下,做了一个例子. 效果图如 一.功能: 1.支持列合并 2.考虑了界面刷新优化 3.预留部分接口 4.支持左右滚动 1.枚举类:CellTypeEnum package csdn.danielinbiti.custometableview.item; public enum CellTypeEnum { STRING //字符 ,DI

Android中自定义ListView无法响应OnItemClickListener中的onItemClick方法问题解决方案

如果你的自定义ListViewItem中有Button或者Checkable的子类控件的话,那么默认focus是交给了子控件,而ListView 的Item能被选中的基础是它能获取Focus,也就是说我们可以通过将ListView中Item中包含的所有控件的focusable属性设置为 false,这样的话ListView的Item自动获得了Focus的权限,也就可以被选中了 我们可以通过对Item Layout的根控件设置其android:descendantFocusability="blo

Android中使用ListView绘制自定义表格(3)

把自定义表格又改进了一下,可以支持行合并.表格分为简单和复杂两种模式 1.简单模式就是<Android中使用ListView绘制自定义表格(2)>描述的方式.不支持行合并 2.复杂模式支持行列合并 1.基于上回上传的代码,改动文件如下 package csdn.danielinbiti.custometableview.item; public class ItemCell { private String cellValue = "";//单元格的值 private in

Android中的ListView使用案例(SimpleAdapter实现)

ListView是可以垂直的滚动的显示一组列表的内容,其中的每一个条目可以是一个单独的组件,也可以是由多个组件组成的组合控件. 实现一个ListView控件的步奏: 1.准备ListView所要显示的数据,一般为一维或二维动态数组. 2.构建适配器,由于ListView中的每一个条目可以很简单,也可以很复杂,根据需要可以选择ArrayAdapter.SimpleAdapter.或者BaseAdapter. 3.使用setAdapter为一个ListView控件设置适配器. 4.为ListView

浅谈android中的ListView合集系列之解决ScrollView和ListView嵌套冲突(一)

相信大家都已经可以熟练使用ListView和GridView,大神们估计都在使用RecyclerView了.如果还在使用ListView,你肯定有这样的一个深刻的感受,那就是在做一个APP的时候使用ListView和GridView很频繁,并且经常会遇到一个页面中除了有ListView或GridView可能还有一些其他的内容,但是可能内容很多,你第一时间就会想到让它整体滑动即可,那就是在总的布局外面包裹一个ScrollView.也就是出现了ScrollView中嵌套一个ListView的场景,或

Android中设置ListView内容刷新问题

最近在学习Android的listView控件时遇到了一个问题,如何添加一个Item到ListView中并及时的刷新出来.在网上查了很多帖子,很多人在问,也很多人在解答,但是总的来说都没找到详细的解决方案.对于ListView与数据库的同步,高手们建议使用ContentProvider对象.但是如果我不使用数据库呢?也有人回答用notifyDataSetChanged()方法.这倒是一个正确的解决方案,但是对于新手来说,还是比较困惑怎么去用,这里我贴一下我的用法: 代码 OnClickListe

关于android中线程,进程,组件,app的理解

android系统是一座房子,有一个正常运行的公司进驻这所座子 cpu是这家公司的老板 进程是公司中的办公室,办公室不干活 线程是办公室中的员工,干活的永远是员工 一间办公室中可有多个员工,并且办公室有个活动对外的人员叫主线程 公司肯定会有很多职能部门 activity  service provider brocastrecive 都是职能部门 现在公司有个客户有个需求,需要几个部门合作才能完成,于是公司的业务人员APP就跟老板申请了一间办公室来完成客户需求工作 当然,为了工作更加有效率,有的