Android高效并简约的使用ListView(二)

BaseListAdapter实现

public abstract class BaseListAdapter extends BaseAdapter
BaseListAdapter是一个模板类继承Android SDK中的BaseAdapter,里面可以看到一个成员对象
private List list = new ArrayList<>();

通过该对象装载Adater中所需要的数据,经常看到有朋友在Adapter的外部持有Adapter中list引用用来改变Adapter中的数据,其实这样会导致两个对象同时对Adapter中的数据进行改变,风险是不可控的,而且外部的list生命周期在长于Adapter的时候会导致list来不及内存回收,如果做了一些非法的操作很容易使得list对象这块内存逃逸出去,比如用一个static的引用去指向它。在Adapter中增加一些数据改变的方法可以避免这一切情况的出现,而且很方便。

/**
     * 初始化list
     *
     * @param list
     */
    public void initList(List list) {
        if (this.list.size() > 0) {
            this.list.clear();
        }
        this.list.addAll(list);
        notifyDataSetChanged();
    }
    
    /**
     * add the list ,but no clear
     *
     * @param list
     */
    public void addList(List list) {
        this.list.addAll(list);
        notifyDataSetChanged();
    }
    
    /**
     * clear all list
     */
    public void clearList() {
        this.list.clear();
        notifyDataSetChanged();
    }
    ```
通过这些方法使得`Adapter`才真正成为`data controller`, 在每次需要数据操作的时候对`Adapter`进行发送消息。
    下面就是真正源码抽象的过程了:
##1. 首先是getView()方法的抽象
```java
    
    @Override
    public final View getView(int position, View convertView, ViewGroup parent) {
        BaseViewHolder viewHolder = null;
        if (convertView == null) {
            viewHolder = onCreateViewHolder(position, getViewBundles());
            convertView = createView(position, viewHolder);
        } else {
            viewHolder = (BaseViewHolder) convertView.getTag();

        }
        if (convertView == null || convertView.getTag() == null)
            throw new NullPointerException(" creatview fails");
        onSetViewHolder(position, getItem(position), viewHolder);
        return convertView;
    }
    ```
在`getView`中通过`position`参数实例化对应的`viewHolder`对象`onCreateViewHolder`方法会call`getItemViewType`方法通过子类实现的`List ViewBundles`对象取得需要的ViewHolder.class类型然而通过反射去实例化。
```java  
    protected BaseViewHolder onCreateViewHolder(int pos,List ViewBundles){
        Class clazz = ViewBundles
                .get(getItemViewType(pos)).vHClazz;
        return ViewUtil.getInstance(clazz);
    }

拿到ViewHolder对象后就可以开始进行相应的数据set了,
onSetViewHolder(position, getItem(position), viewHolder);这个是set data的方法:

/**
     * set data for viewHolder by viewType(through by getItemViewType)
     *
     * @param position
     * @param bean
     * @param baseViewHolder
     */
    protected void onSetViewHolder(int position, T bean,
                                   BaseViewHolder baseViewHolder) {
        baseViewHolder.setView(bean, getContext());

    }

在onSetViewHolder方法中可以看到回去调用ViewHolder父类的setView()方法,下面是BaseViewHolder的代码,通过baseViewHolder.setView(bean, getContext());可以调用子类的具体实现,这样就解耦了adapter和viewHolder之间的关联,并且不需要关注viewHolder的对象具体类型(因为在getViewTypeCount!=1的时候会在onBindViewHolder(List list)中绑定多个BaseViewHolder子类的类型),其实就是java多态的使用。

    public static abstract class BaseViewHolder {
        public BaseViewHolder() {
        }
        protected abstract void setView(T bean, Context context);
        public Class getClassTag() {
            return this.getClass();
        }
    }

2. ViewBundle的职能?

在子类实现中有这样的一个方法onBindViewHolder(List list),ViewBundle的作用是什么呢?

    public static class ViewBundle {
        public ViewBundle(int layoutId, Class clazz) {
            this.layoutId = layoutId;
            this.vHClazz = clazz;
        }

        public Class vHClazz;
        public int layoutId;

        @Override
        public String toString() {
            return "ViewBundle{" + "vHClazz=" + vHClazz + ", layoutId="
                    + layoutId + '}';
        }
    }

可以看到其实是一个layoutId和viewHolder类型的对应关系,通过list将数组对象传递给父类,通过下标取得对应的class值。

到这里代码解析全部完毕,主要工作就是Adapter的简单定制,让子类的具体实现最小化。这样的处理在开发中可以减少相应的繁琐重复工作并降低错误率,父类中输出了输出了必要的log并在子类实现除错的情况跑出了对应的异常,可以很快的定位原因。
有问题请联系我:[email protected]

你可能感兴趣的:(Android高效并简约的使用ListView(二))