BaseAdapter优雅的封装

在使用ListView的过程中,为了复用ListView中的convertView以及优化getView()时的findViewById操作,我们通常会引入一个ViewHolder类,来持有itemView的子view。但是不便的是,我们需要为每一种显示不同数据的ListView都重写其BaseAdapter的getView(),于是就有了对其的一种封装:
创建一个通常的ViewHolder,里面用SparseArray<View>来缓存,如下:

public class ViewHolder {
    private final View itemView;
    private SparseArray<View> mHolderViews;

    public ViewHolder(View view) {
        itemView = view;
        view.setTag(this);
        mHolderViews = new SparseArray<>();
    }

    public void hold(int... resIds) {
        for (int id : resIds) {
            mHolderViews.put(id, itemView.findViewById(id));
        }
    }

    public <V> V get(int id) {
        return (V) mHolderViews.get(id);
    }
}

再通过重写BaseAdapter的getView方法,作如下封装:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    final ViewHolder holder;
    if (convertView == null) {
        holder = createHolder(position, parent);
        convertView = holder.itemView;
    } else {
        holder = (ViewHolder) convertView.getTag();
    }
    bindData(position, holder, getItem(position));
    return convertView;
}

public abstract ViewHolder createHolder(int position, ViewGroup parent);

public abstract void bindData(int position, ViewHolder holder, T data);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

这样使用的时候只需要继承并实现两个抽象方法即可。

这样写法,在后来我觉得还是有些别扭的地方。一是使用的时候还是需要去继承我们封装过的Adapter;二是在bindData()方法中,需要对每一个我们要设置的控件调用holder.get(id)方法,才能开始赋值或进行其他的设置,当我们的item的控件较多时,这些调用会使bindData()显得臃肿而不够纯粹,于是我又对其进行了另一种封装。

这样使用的时候只需要继承并实现两个抽象方法即可。

这样写法,在后来我觉得还是有些别扭的地方。一是使用的时候还是需要去继承我们封装过的Adapter;二是在bindData()方法中,需要对每一个我们要设置的控件调用holder.get(id)方法,才能开始赋值或进行其他的设置,当我们的item的控件较多时,这些调用会使bindData()显得臃肿而不够纯粹,于是我又对其进行了另一种封装。

另一种优雅封装

这里我主要是通过接口解决重写Adapter的问题,然后再借鉴RecyclerView.ViewHolder,封装结果如下:

假设我们重写BaseAdapter的类为BaseListAdapter,那么首先定义它的一个静态内部类:

    public static abstract class ViewHolder {
        public final View itemView;

        public ViewHolder(View itemView) {
            this.itemView = itemView;
            itemView.setTag(this);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

然后我们定义一个接口,用于创建ViewHolder以及绑定数据,如下:

package com.githang.android.snippet.demo.adapter.adapter;

import android.view.ViewGroup;

public interface ViewCreator<T, H extends BaseListAdapter.ViewHolder> {

    H createHolder(int position, ViewGroup parent);

    /**
     * 设置列表里的视图内容
     *
     * @param position 在列表中的位置
     * @param holder   该位置对应的视图
     */
    void bindData(int position, H holder, T data);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

这里定义了两个泛型 ,一个是T,表示我们Adapter里的数据对象,另一个是H,为我们的ViewHolder的子类,表示我们最终创建出来的ViewHolder。

接下来,修改我们的BaseListAdapter,如下:

public class BaseListAdapter<T, H extends BaseListAdapter.ViewHolder> extends BaseAdapter {
    private final List<T> mData;
    private final ViewCreator<T, H> mViewCreator;

    public BaseListAdapter(ViewCreator<T, H > creator) {
        this(new ArrayList<T>(), creator);
    }

    public BaseListAdapter(List<T> data, ViewCreator<T, H> creator) {
        mData = data == null ? new ArrayList<T>() : data;
        mViewCreator = creator;
    }

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

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

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

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final H holder;
        if (convertView == null) {
            holder = mViewCreator.createHolder(position, parent);
            convertView = holder.itemView;
        } else {
            holder = (H) convertView.getTag();
        }
        mViewCreator.bindData(position, holder, getItem(position));
        return convertView;
    }

    public void update(List<T> data) {
        mData.clear();
        addData(data);
    }

    public void addData(List<T> data) {
        if (data != null) {
            mData.addAll(data);
        }
        notifyDataSetChanged();
        }

    public static abstract class ViewHolder {
        public final View itemView;

        public ViewHolder(View itemView) {
            this.itemView = itemView;
            itemView.setTag(this);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

在这个BaseListAdapter里,同样定义了两个泛型,与我们的ViewCreator相同。我们创建ViewHolder及绑定数据的操作,通过调用该接口来执行(见getView方法),而该接口实例则通过构造方法传入。
这样,我们在使用的时候就可以先像使用RecyclerView一样,定义一个实现我们ViewHolder的子类,然后使我们的Fragment或Activity实现这个ViewCreator接口就可以了。
最后,我们还可以继承ViewHolder,封装一个通用的ViewHolder,代码如下:

    public static class DefaultViewHolder extends ViewHolder {
        private SparseArray<View> mHolderViews;

        public DefaultViewHolder(View view) {
            super(view);
            mHolderViews = new SparseArray<>();
        }

        public void hold(int... resIds) {
            for (int id : resIds) {
                mHolderViews.put(id, itemView.findViewById(id));
            }
        }

        public <V> V get(int id) {
            return (V) mHolderViews.get(id);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

接下来是使用示例:

public class SampleFragment extends Fragment implements ViewCreator<User,SampleFragment.UserViewHolder> {

    private BaseListAdapter<User, UserViewHolder> mAdapter = new BaseListAdapter<User, UserViewHolder>(this);

    @Override
    public UserViewHolder createHolder(int position, ViewGroup parent) {
        return new UserViewHolder(LayoutInflater.from(getActivity()).inflate(R.layout.item_user, parent, false));
    }

    @Override
    public void bindData(int position, UserViewHolder holder, User user) {
        holder.name.setText(user.name);
        holder.email.setText(user.email);
    }

    static class UserViewHolder extends BaseListAdapter.ViewHolder {
        public final TextView name;
        public final TextView email;

        public UserViewHolder(View itemView) {
            super(itemView);
            name = (TextView) itemView.findViewById(R.id.name);
            email = (TextView) itemView.findViewById(R.id.email);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

全部的封装代码加上注释等也不过一百来行,但是可以看到,在这样的封装下,我们的代码已经变得很清晰。

本文的代码见:http://download.csdn.net/detail/maosidiaoxian/9700700

特此转载
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值