文档章节

通用Adapter与ListView滚动时不加载图片的封装

爱看博客
 爱看博客
发布于 2015/10/22 10:02
字数 1319
阅读 7
收藏 0
点赞 0
评论 0

本文原创,转载请注明链接:http://blog.kymjs.com/

在Android开发中写Adapter是一件非常麻烦的事情,枯燥重复,却又不得不去做。 对于Adapter一般都继承BaseAdapter复写几个方法,getView里面使用ViewHolder存储,其实大部分的代码都是类似的。那么本文就带大家一起做一次将Adapter封装成一个通用的Adapter。

关于本文的完整Demo,可以参考KJFrameForAndroid开发框架2.2版本中封装的实例,KJAdapterAdapterHolder这两个类。
那么接下来我们进入正文,下面这个类似的代码应该是我们看的最多的:

public class EmojiGridAdapter extends BaseAdapter {

    private List<Emojicon> datas;
    private final Context cxt;

    public EmojiGridAdapter(Context cxt, List<Emojicon> datas) {
        this.cxt = cxt;
        if (datas == null) {
            datas = new ArrayList<Emojicon>(0);
        }
        this.datas = datas;
    }

    public void refresh(List<Emojicon> datas) {
        if (datas == null) {
            datas = new ArrayList<Emojicon>(0);
        }
        this.datas = datas;
        notifyDataSetChanged();
    }

    @Override
    public int getCount() {
        return datas.size();
    }

    @Override
    public Object getItem(int position) {
        return datas.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    private static class ViewHolder {
        ImageView image;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        if (convertView == null) {
            holder = new ViewHolder();
            ......
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        holder.image.setImageResource(datas.get(position).getResId());
        return convertView;
    }}


初步抽取

其中BaseAdapter的四个方法必须写,但是基本上前三个都是一模一样的, 所以可以使用泛型,写一个基类出来,把数据封装到基类里面,只需要构造方法传入就行了

public class KJBaseAdapter<T> extends BaseAdapter {
	List<T> datas;
 
	KJBaseAdapter(Context cxt,List<T> datas){
		......
	}

	@Override
    public int getCount() {
        return datas.size();
    }

    @Override
    public Object getItem(int position) {
        return datas.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }}

然后是我们唯一需要动脑的getView()方法,首先是判断converView是否空,然后载入item布局,然后ViewHolder挨个初始化控件,然后通过tag保存holder,最后设置View的显示。
步棸都知道了,那么我们慢慢来观察:ViewHolder一定是包含了item子控件的一个静态类。那么我们就干脆把item所有的子控件都放到ViewHolder里面,但是既然我们要通用,item肯定不是固定的,这就没办法把ViewHolder写的像上面的那种属性的形式。
这里我们使用一个键值对来存储Map<id, view>全部的控件,这样就可以在需要的时候直接通过id来找到对应的子View了。

mViews = new Map<Integaer, View>();/**
 * 通过控件的Id获取对于的控件,如果没有则加入views
 * 
 * @param viewId
 * @return
 */public <T extends View> T getView(int viewId) {
    View view = mViews.get(viewId);
    if (view == null) {
        view = mConvertView.findViewById(viewId);
        mViews.put(viewId, view);
    }
    return (T) view;}

封装ViewHolder

只看getView,其他方法都一样;首先调用ViewHolder的get方法,如果convertView为null,new一个ViewHolder实例,通过使用mInflater.
inflate加载布局,然后new一个HashMap用于存储View,最后setTag(this); 如果存在那么直接getTag最后通过getView(id)获取控件,如果存在则直接返回,否则调用findViewById,返回存储,返回。

那么最后封装好的ViewHolder就是这样的

public class AdapterHolder {
    private final Map<Integer,View> mViews;
    private final int mPosition;
    private final View mConvertView;

    private AdapterHolder(ViewGroup parent, int layoutId, int position) {
        this.mPosition = position;
        this.mViews = new HashMap<Integer, View>();
        mConvertView = LayoutInflater.from(parent.getContext()).inflate(
                layoutId, parent, false);
        // setTag
        mConvertView.setTag(this);
    }

    /**
     * 拿到一个ViewHolder对象
     */
    public static AdapterHolder get(View convertView, ViewGroup parent,
            int layoutId, int position) {
        if (convertView == null) {
            return new AdapterHolder(parent, layoutId, position);
        } else {
            return (AdapterHolder) convertView.getTag();
        }
    }

    /**
     * 通过控件的Id获取对于的控件,如果没有则加入views
     * 
     * @param viewId
     * @return
     */
    public <T extends View> T getView(int viewId) {
        View view = mViews.get(viewId);
        if (view == null) {
            view = mConvertView.findViewById(viewId);
            mViews.put(viewId, view);
        }
        return (T) view;
    }}

结合前面的基类,我们的Adapter就变成了这样的

public class EmojiGridAdapter<T> extends KJBaseAdapter<T> {  
  
    @Override  
    public View getView(int position, View convertView, ViewGroup parent){  
        ViewHolder viewHolder = ViewHolder.get(mContext, convertView, parent,  
                R.layout.item_single_str, position);  
        TextView mTitle = viewHolder.getView(R.id.id_tv_title);  
        mTitle.setText((String) mDatas.get(position));  
        return viewHolder.getConvertView();  
    }  }

最终的封装

再仔细观察,第一行的ViewHolder.get()和最后一行的return方法肯定也是不变的,果断进一步封装。
那么就完全可以是只需要抽出getView中可变的部分————通过ViewHolder把View找到,通过Item设置值;这一块单独写出来了。那么我们写一个方法就叫convert()来做这件事。至此代码简化到这样,剩下的已经不需要单独写一个Adapter了,直接Activity匿名内部类足够了。

protected void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    setContentView(R.layout.activity_main);  
    mListView = (ListView) findViewById(R.id.id_lv_main);  
  
    //设置适配器  
    mListView.setAdapter(mAdapter = new CommonAdapter<String>(  
            getApplicationContext(), mDatas, R.layout.item_single_str) {  
        @Override  
        public void convert(ViewHolder c, String item) {  
            TextView view = viewHolder.getView(R.id.id_tv_title);  
            view.setText(item);  
        }  
    });  }

最后的优化

现在我们再来对比KJFrameForAndroid中的封装,可以看到使用了SparseArray来替代我们的Map,SparseArray实际上就是一个拥有两个数组的类,第一个数组是一个int[],用来当做key,第二个就是泛型这里使用的是View[],它是google推荐用来替代int作为key的Map集合的一个类。

还有一个细节就是KJFrameForAndroid中的封装,加入了一个absListView属性,并设置了滚动监听,这样就可以很方便的在基类中实现例如listview滚动过程中不加载图片等功能。

最后我们封装好以后的代码:就可以直接查看KJAdapterAdapterHolder这两个类。 以及使用方法可以参考KJBlog中的使用,例如:这里

版权声明:本文原创,转载请注明来自 http://kymjs.com/

本文转载自:http://blog.csdn.net/kymjs/article/details/45364547

共有 人打赏支持
爱看博客
粉丝 5
博文 103
码字总数 23887
作品 0
深圳

暂无相关文章

Linux中的端口大全

1 被LANA定义的端口 端口 名称 描述 1 tcpmux TCP 端口服务多路复用 5 rje 远程作业入口 7 echo Echo 服务 9 discard 用于连接测试的空服务 11 systat 用于列举连接了的端口的系统状态 13 d...

寰宇01 ⋅ 22分钟前 ⋅ 0

Confluence 6 如何备份存储文件和页面信息

备份的 ZIP 文件包含有 entities.xml,这个 XML 文件包含有 Confluence 的所有页面内容和存储附件的目录。 备份 Zip 文件结构 页面的附件是存储在附件存储目录中的,通过页面和附件 ID 进行识...

honeymose ⋅ 24分钟前 ⋅ 0

【每天一个JQuery特效】根据状态确定是否滑入或滑出被选元素

主要效果: 本文主要采用slideToggle()方法实现以一行代码同时实现以展开或收缩的方式显示或隐藏被选元素。 主要代码如下: <!DOCTYPE html><html><head><meta charset="UTF-8">...

Rhymo-Wu ⋅ 28分钟前 ⋅ 0

度量.net framework 迁移到.net core的工作量

把现有的.net framework程序迁移到.net core上,是一个非常复杂的工作,特别是一些API在两个平台上还不能同时支持。两个类库的差异性,通过人工很难识别全。好在微软的工程师们考虑到了我们顾...

李朝强 ⋅ 33分钟前 ⋅ 0

请不要在“微服务”的狂热中迷失自我!

微服务在过去几年一直是一个非常热门的话题(附录1)。何为“微服务的疯狂”,举个例子: 众所周知,Netflix在DevOps上的表现非常棒。Netfix可以做微服务。因此:如果我做微服务,我也将非常...

harries ⋅ 35分钟前 ⋅ 0

oAuth2 升级Spring Cloud Finchley.RELEASE踩坑分享

背景 6.19号,spring团队发布了期待已久的 Spring Cloud Finchley.RELEASE 版本。 重要变化: 基于Spring Boot 2.0.X 不兼容 Spring Boot 1.5.X 期间踩过几个坑,分享出来给大伙,主要是关于...

冷冷gg ⋅ 今天 ⋅ 0

OSChina 周一乱弹 —— 理发师小姐姐的魔法

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @冰冰棒- :分享田馥甄的单曲《My Love》 《My Love》- 田馥甄 手机党少年们想听歌,请使劲儿戳(这里) @Li-Wang :哎,头发又长了。。。又要...

小小编辑 ⋅ 今天 ⋅ 9

Kafka1.0.X_消费者API详解2

偏移量由消费者管理 kafka Consumer Api还提供了自己存储offset的功能,将offset和data做到原子性,可以让消费具有Exactly Once 的语义,比kafka默认的At-least Once更强大 消费者从指定分区...

特拉仔 ⋅ 今天 ⋅ 0

NEO智能合约之发布和升级(二)

接NEO智能合约之发布和升级(一),我们接下来说说智能合约的升级功能。 一 准备工作 合约的升级需要在合约内预先设置好升级接口,以方便在升级时调用。接下来我们对NEO智能合约之发布和升级...

红烧飞鱼 ⋅ 今天 ⋅ 0

个人博客的运营模式能否学习TMALL天猫质量为上?

心情随笔|个人博客的运营模式能否学习TMALL天猫质量为上? 中国的互联网已经发展了很多年了,记得在十年前,个人博客十分流行,大量的人都在写博客,而且质量还不错,很多高质量的文章都是在...

原创小博客 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部