天天看点

ListView的“终极优化”,打造你的万能适配器

本篇文章你会了解以下内容:

1、抽取ViewHolder,实现优化第一步

2、抽取公共Adapter,告别冗余方法

3、控件直接赋值,让Adapter再次优化

执笔不易,觉得不错,请转发分享(更多Android文章,还请关注左侧栏中我的公众号)。

ListView不得不说是Android开发当中出现率相当高的一个控件,什么资讯列表,联系人列表,消息列表等等,无不有它的身影存在。既然项目中有太多的地方会用到它,传统的代码逻辑我们是怎么使用的呢?每写一个ListView,都要去写一个Adapter类,一个ViewHolder类,这几乎是我们必须要操作的,以致于有太多太多的冗余代码,让我们感到真的不厌其烦,一个两个还可以,十个八个,就真的有点太崩溃了,不仅代码繁琐,还会占用内存,为了解决这样的一个问题,下面就要开始对其抽取优化,代码之间从上到下进行衔接,不要跳过看啊。

1、抽取ViewHolder,实现优化第一步

我们知道正常的ViewHolder,会在Adapter的getView方法里去操作很多事情,创建实例,赋值控件,setTag和getTag等,如下代码所示,是我们最常用的模式,这里我只用了两个控件,其实开发中有很多的View,对ViewHolder的优化,就是把这些代码封装进行抽取。

@Override

public View getView(int position, View convertView, ViewGroup parent) {

    View view;

    ViewHolder viewHolder;

    if (convertView == null) {

        view = View.inflate(mContext, R.layout.item_view, null);

        viewHolder = new ViewHolder();

        viewHolder.tvTitle = (TextView) view.findViewById(R.id.tv_title);

        viewHolder.tvContent = (TextView) view.findViewById(R.id.tv_content);

        view.setTag(viewHolder);

    } else {

        view = convertView;

        viewHolder = (ViewHolder) view.getTag();

    }

viewHolder.tvTitle.setText(mDatas.get(position).getTitle());

viewHolder.tvContent.setText(mDatas.get(position).getContent());

    return view;

}

private static class ViewHolder {

    TextView tvTitle, tvContent;

}

自定义一个ViewHolder类,实现构造方法,既然要实现对以上的代码进行一个抽取,那么getView里的参数,ViewHolder类大部分都需要用到,在ViewHolder类中,写一个静态方法,用于获取ViewHolder,其判断逻辑和以上代码思路一致,convertView若不存在就去创建ViewHolder,若存在就复用。

public static ViewHolder getViewHolder(Context context, int position,

                                     View convertView, ViewGroup parent, int layoutId) {

    if (convertView == null) {

        return new ViewHolder(context, position, parent, layoutId);

    } else {

        ViewHolder viewHolder = (ViewHolder) convertView.getTag();

        viewHolder.mPosition = position;

        return viewHolder;

    }

}

实现其构造方法,用于convertView不存在时进行对其实例:

private SparseArray<View> mView;

private int mPosition;

private View mConvertView;

public ViewHolder(Context context, int position, ViewGroup parent, int layoutId) {

    mPosition = position;

    mView = new SparseArray<View>();

    mConvertView = View.inflate(context, layoutId, null);

    mConvertView.setTag(this);

}

mView是用于存储各个控件View,在这里为什么要用SparseArray而不用Map,我们可以去查看下源码,其实对于键为int类型,SparseArray要比map更加有效的使用内存。

mPosition是记录索引位置。

mConverView就是创建的View对象,注意看上述代码,它是在convertView为null的情况下,才去创建的。

写一个方法,用于通过ViewHolder来获取各个控件:

public <T extends View> T getView(int layoutId) {

    View view = mView.get(layoutId);

    if (view == null) {

        view = getmConvertView().findViewById(layoutId);

        mView.put(layoutId, view);

    }

    return (T) view;

}

再写一个获取layout的方法:

public View getmConvertView() {

    return mConvertView;

}

经过以上代码,对于ViewHolder,我们基本上就抽取完成了,回过来,我们再看adapter里的getView方法(注意,原来adapter里的ViewHolder类可以对其删除),是不是一下整洁了许多。

@Override

public View getView(int position, View convertView, ViewGroup parent) {

    ViewHolder viewHolder=ViewHolder.getViewHoder(mContext,position,

            convertView,parent,R.layout.item_view);

    ((TextView)viewHolder.getView(R.id.tv_title)).

            setText(mDatas.get(position).getTitle());

    ((TextView)viewHolder.getView(R.id.tv_content)).

            setText(mDatas.get(position).getContent());

    return viewHolder.getmConvertView();

}

2、抽取公共Adapter,告别冗余方法

ViewHolder抽取完之后,省去了太多的代码,但是我们会发现Adapter里除了getView方法,其它三个也是重复的,那么接下来,我们就对Adapter进行抽取优化,新建一个万能的Adapter类,继承于BaseAdapter,记住要是抽象的,把getView方法设成抽象方法,这样其它Adapter继承这个万能Adapter,只需要重写getView方法即可。

public abstract class UniversalAdapter<T> extends BaseAdapter {

    protected List<T> mDatas;

    protected Context context;

    private int layoutId;

    public UniversalAdapter(Context context, List<T> mDatas, int layoutId) {

        this.mDatas = mDatas;

        this.context = context;

        this.layoutId = layoutId;

    }

    @Override

    public int getCount() {

        return mDatas.size();

    }

    @Override

    public Object getItem(int position) {

        return mDatas.get(position);

    }

    @Override

    public long getItemId(int position) {

        return position;

    }

    @Override

    public abstract View getView(int position, View convertView, ViewGroup parent);

}

在上述代码中,细心的同志的可能看到了,实现其构造方法时,我传入了一个layoutId,其实就是一个XML资源,引入它,说白了,在getView方法里我连 ViewHolder viewHolder=ViewHolder.getViewHolder(mContext,position,convertView,parent,R.layout.item_view);这段代码我也不想去写,怎么办呢?很简单getView方法不去抽象化,而引出一个抽象的方法,具体可以改成以下方式:

@Override

public View getView(int position, View convertView, ViewGroup parent) {

    ViewHolder viewHoder = ViewHolder.getViewHolder(context, position, convertView, parent, layoutId);

    convert(viewHolder, mDatas.get(position));

    return viewHolder.getmConvertView();

}

protected abstract void convert(ViewHolder viewHolder, Object item);

以后我们所有的Adapter继承这个万能的Adapter之后,只需要重写convert这个方法就好了,Object item是一个Bean,获取时要进行强转。

public class MyAdapter extends CourAdapter {

    public MyAdapter(List<Bean> data, Context context, int layoutId) {

        super(context, data, layoutId);

    }

    @Override

    protected void convert(ViewHoder viewHoder, Object item) {

        ((TextView)viewHolder.getView(R.id.tv_title)).

                setText(((Bean)item).getTitle());

        ((TextView)viewHolder.getView(R.id.tv_content)).

                setText(((Bean)item).getContent());

    }

}

看看以上的代码,是不是就简单了很多呢。

3、控件直接赋值,让Adapter再次优化

经过以上的优化之后,代码大大的减少了,但是,我还想进行对其优化,也就是说,我连setText都不想去那么麻烦,这样的话,我们就是可以在ViewHolder这个类中去写控件赋值方法:

//TextView设置数据

public ViewHolder setText(int viewId, String txt) {

    TextView mTextView = getView(viewId);

    mTextView.setText(txt);

    return this;

}

再看convert方法,是不是又一下简单的很多。

@Override

protected void convert(ViewHolder viewHolder, Object item) {

    viewHolder.setText(R.id.tv_title,((Bean)item).getTitle())

            .setText(R.id.tv_content,((Bean)item).getContent());

}

当然了对于其它控件你也可以这样去做,比如ImageView:

public ViewHolder setPic(int viewId, String url) {

    ImageView mImageView = getView(viewId);

    //ImageLoader.getInstance().loadImage(url,mImageView);

    return this;

}

好了同志们,以上就是今天分享的所有内容,更多Android文章还请关注左侧栏中我的微信公众号,好累啊,拜拜了。