天天看点

通用Adapter的实现套路(ListView篇)

一般情况,我们ListView的Adapter,继续自BaseAdapter,很多方法都是重复性的工作,如:getItem(),getCount()等,为了更高效,我们要自己创建ViewHolder,用于缓存View。随着开发经验的积累,我们发现其中的一些套路,可以将这些不变的部分放到抽象类中,将变换的部分,写成抽象方法,这样,在使用时,直接继续自我们的CommonAdapter,复写抽象方法即可。

1、CommonAdapter

public abstract class CommonAdapter<T> extends BaseAdapter {

    protected Context mContext;
    private List<T> datas;
    private int mLayoutId;

    public CommonAdapter(Context context, List<T> datas, int layoutId) {
        this.mContext = context;
        this.datas = datas;
        this.mLayoutId = layoutId;
    }

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

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

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder = ViewHolder.getViewHolder(mContext, convertView, mLayoutId);

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

        return viewHolder.getConvertView();
    }

    protected abstract void convert(ViewHolder viewHolder, T data, int position);
}
           

2、ViewHolder

public class ViewHolder {

    private View convertView;
    private SparseArray<WeakReference<View>> mViews;

    public ViewHolder(Context context, View convertView, int layoutId) {
        convertView = View.inflate(context, layoutId, null);
        convertView.setTag(this);

        this.convertView = convertView;

        mViews = new SparseArray<>();
    }

    public static ViewHolder getViewHolder(Context context, View convertView, int layoutId) {
        if (convertView == null) {
            ViewHolder viewHolder = new ViewHolder(context, convertView, layoutId);
            return viewHolder;
        } else {
            return (ViewHolder) convertView.getTag();
        }
    }

    public View getConvertView() {
        return convertView;
    }

    public ViewHolder setText(int viewId, CharSequence text) {
        TextView tv = getView(viewId);
        if (tv != null) {
            tv.setText(text);
        }

        return this;
    }

    public ViewHolder setImageRes(int viewId, int resId) {
        ImageView iv = getView(viewId);
        if (iv != null) {
            iv.setImageResource(resId);
        }

        return this;
    }

    public ViewHolder setImageUrl(int viewId, String url) {
        ImageView iv = getView(viewId);
        if (iv != null) {
            //这里要配合网络框架使用...
        }
        return this;
    }

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

        WeakReference<View> viewWeakReference = mViews.get(viewId);
        View view = null;
        if (viewWeakReference != null) {
            view = viewWeakReference.get();
        }

        if (view == null) {
            view = convertView.findViewById(viewId);
            mViews.put(viewId, new WeakReference<View>(view));
        }

        return (T) view;
    }
}
           

将之前在getView()中判断convertView的内容,移到了getViewHolder()方法中,然后提供了常用的setText()和setImage()方法,访问网络图片的,这里没写完。其他控件可以通过调用getView()方法进行内容设置。

还有两点,需要注意:

  1. setText()这几个方法,我们采用链式调用的方式,一行代码,设置任意多个控件。
  2. getView()的写法,我们不是简单的省去findViewById()这种操作,更重要的是,使用SparseArray对View的缓存,我们并没有直接将View放到集合中,而是用WeakReference对View进行了一层包装,防止内存泄漏。

3、如何用?

public class MyAdapter extends CommonAdapter<Person> {

    public MyAdapter(Context context, List datas) {
        super(context, datas, R.layout.item_layout);
    }

    @Override
    protected void convert(ViewHolder viewHolder, Person data, int position) {
        viewHolder.setText(R.id.item_tv, "测试")
        .setText(R.id.activity_main, "hello");

    }
}
           

说明:

  1. 只要指定泛型参数,就可以直接在convert()方法中设置相应的值了。
  2. 我们在封装CommonAdapter时,构造方法中有一个layoutId参数,它是用来设置item布局的,只要在新建的Adapter中,调用super()方法,并设置相应的布局即可,不用再抛给外面设置了。
  3. 在convert()方法中,只接调用viewHolder,就可以进行setText()等赋值操作。

如何获取代码?

git clone https://github.com/droid4j/anKataLite.git

本篇对应的标签 v0.2

git checkout v0.2

继续阅读