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

本文原创,转载请注明链接: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中的使用,例如:这里

时间: 2024-11-08 07:14:16

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

LIstview滑动时不加载图片,停止时加载!

//参照 http://blog.csdn.net/yy1300326388/article/details/45153813 public class CarWashDistanceAdapter extends BaseAdapter { private static final String TAG = "CarWashDistanceAdapter"; private Context context; private MyDialog myDialog; private Arr

ListView设置快速滑动时不加载图片

设置ListView 快速滑动不加载要设置滑动监听OnScrollListener, 当快速滑动是设置标志位不请求网络 这个也属于listview优化的一步 设置监听器 1 package com.example.listview_01; 2 3 import android.widget.AbsListView; 4 import android.widget.BaseAdapter; 5 import android.widget.AbsListView.OnScrollListener;

EasyUI 1.4.4 DataGrid(大数据量) bufferview滚动时不加载下一页数据解决方案

在使用Easyui DataGrid 过程中,发现若单页数据量超过300,IE浏览器加载速度很慢.也通过网上找寻了很多解决方案,最典型的就是去掉datagrid的自动列宽以及自动行高判断. 1.解决自动列宽:  设定列宽度可解决. 2.解决自动行高 : 注释掉下面的代码. 1 function _3e(_44,_45){ 2 //for(var i=0;i<_45.length;i++){ 3 ////var tr1=$(_44[i]); 4 ////var tr2=$(_45[i]); 5 /

Listview滑动时不加载数据,停下来时加载数据,让App更优

数据源配置(Adapter) package com.zhengsonglan.listview_loading.adapter; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Im

Android开源代码解读のOnScrollListener实现ListView滚屏时不加载数据

使用ListView过程中,如果滚动加载数据的操作比较费时,很容易在滚屏时出现屏幕卡住的现象,一个解决的办法就是不要在滚动时加载数据,而是等到滚动停止后再进行数据的加载.这同样要实现OnScrollListener接口,关于该接口的简要描述见上一篇文章,这里直接进行代码的分析: package hust.iprai.asce1885; import android.app.ListActivity; import android.content.Context; import android.o

android listview 滑动过程中不加载图片,停止时加载图片

今天闲来无事, 就测试了一下listview加载图片优化的功能, 在我们使用新浪微博的时候,细心的同学一定发现了,在滑动的过程中,图片是没有被加载的, 而是在滑动停止时,才加载图片了. 我们今天就做一个这样的效果吧. 我们先考虑两个问题: 1.在滑动停止的时候,如何获得需要加载的图片控件? 2.因为listiew在初始化完成的时候,OnScrollListener的onScrollStateChanged与onScroll并未被触发,如何初始化第一页的图片? package com.test.l

android 当ListView滚动时自动调用 onCheckedChanged 导致CheckBox 状态不停变化 的解决办法

今天在做一个含有CheckBox 的ListView时,发现当初始化CheckBox的状态后, 滚动ListView,其中CheckBox 的选中状态不停的发生变化.最后发现原因是 ListView滚动时自动调用 onCheckedChanged 导致的.在查看了各种博客的解决办法后,国外的网站上有一个办法解决了我的问题.写下来分享一下. 在自定义Adapter的getView方法中这样写就行了. Java代码   //在初始化CheckBox状态和设置状态变化监听事件之前,先把状态变化监听事件

android 当ListView滚动时自动调用 onCheckedChanged

今天在做一个含有CheckBox 的ListView时,发现当初始化CheckBox的状态后, 滚动ListView,其中CheckBox 的选中状态不停的发生变化.最后发现原因是 ListView滚动时自动调用 onCheckedChanged 导致的.在查看了各种博客的解决办法后,国外的网站上有一个办法解决了我的问题.写下来分享一下. 在自定义Adapter的getView方法中这样写就行了. //在初始化CheckBox状态和设置状态变化监听事件之前,先把状态变化监听事件设置为null h

RecyclerView使用 及 滑动时加载图片优化方案

RecyclerView使用 及 滑动时加载图片优化方案 简述 本篇博文主要给大家分享关于RecyclerView控件的使用及通过继承RecyclerView来实现滑动时加载图片的优化方案,也同样能解决防止图片乱序的问题,之前有在网上有看到大神对Android中ListView异步加载图片乱序问题进行过分析,并深入剖析原理后分别给出了3种对应的解决方案:一 .使用findViewWithTag.二.使用弱引用关联.三.使用Volley框架提供的NetworkImageView. 看了之后思索了很