Android之自定义实现BaseAdapter(通用适配器一)

Android之自定义实现BaseAdapter(通用适配器)

通过前面的优化布局之后,我们接着来讲如何打造一个通用的适配器,那么通用适配器能干吗呢?很简单,减少我们对代码的书写,下面开始上代码了。

MyAdapter.java

public class MyAdapter extends BaseAdapter {
    private List<Student> data;
    public MyAdapter(List<Student> data) {
        this.data = data;
    }
    @Override
    public int getCount() {
        return data == null ? 0 : data.size();
    }
    @Override
    public Object getItem(int position) {
        return data.get(position);
    }
    @Override
    public long getItemId(int position) {
        return position;
    }
    /**
     *
     * @param position
     * @param convertView
     * @param parent
     * @return
     */
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
      ViewHolder holder = null;
        if(convertView == null){
            //解析布局
            convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item,null);
            //创建ViewHolder持有类
            holder = new ViewHolder();
            //将每个控件的对象保存到持有类中
            holder.tvName = (TextView)convertView.findViewById(R.id.mTv1);
            holder.tvSex = (TextView)convertView.findViewById(R.id.mTv2);
            //将每个convertView对象中设置这个持有类对象
            convertView.setTag(holder);
        }
        //每次需要使用的时候都会拿到这个持有类
        holder = (ViewHolder)convertView.getTag();
        //然后可以直接使用这个类中的控件,对控件进行操作,而不用重复去findViewById了
        holder.tvName.setText(data.get(position).getName());
        holder.tvSex.setText(data.get(position).getSex());
        return convertView;
    }

    /**
     * 通过这个类来保存当前所有的控件id
     */
    static class ViewHolder{
        TextView tvName;
        TextView tvSex;
    }
}

在上面的代码中,我们先看看有哪些代码的格式或形式都是重复在使用的,首先不难看出,public int getCount() 、public long getItemId(int position) 、public long getItemId(int position)这三个方法是不是每次都要实现呢,由此我们可以先将这些代码提取出来,放到MyBaseAdapter中,由于我们每次的重要部分是实现getView方法,所以这个方法我们不需要在这里面写,直接将MyBaseAdapter设置为抽象类就可以了,然需要实现getView的类来继承他即可,因此MyAdapter可以继承MyBaseAdapter然后实现getView方法即可

MyBaseAdapter.java

public abstract class MyBaseAdapter extends BaseAdapter {
    protected List<Student> data;
    public MyBaseAdapter(List<Student> data){
        this.data = data;
    }
    @Override
    public int getCount() {
        return data == null ? 0 : data.size();
    }

    @Override
    public Object getItem(int position) {
        return data.get(position);
    }

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

MyAdapter.java

public class MyAdapter extends MyBaseAdapter {
    public MyAdapter(List<Student> data) {
        super(data);
        this.data = data;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
      ViewHolder holder = null;
        if(convertView == null){
            convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item,null);
            holder = new ViewHolder();
            holder.tvName = (TextView)convertView.findViewById(R.id.mTv1);
            holder.tvSex = (TextView)convertView.findViewById(R.id.mTv2);
            convertView.setTag(holder);
        }
        holder = (ViewHolder)convertView.getTag();
        holder.tvName.setText(data.get(position).getName());
        holder.tvSex.setText(data.get(position).getSex());
        return convertView;
    }
    static class ViewHolder{
        TextView tvName;
        TextView tvSex;
    }
}

这样,每次自定义只需要继承MyBaseAdapter就可以了,不过还是那句话,没有最优,只有更优,所以我们还要接着优化,接着封装,那么我们接着从上面的getView方法中看,还有哪些代码是我们经常重复使用到的代码呢?其实你会发现每次我们都需要操作相同的这段代码:

 ViewHolder holder = null;
        if(convertView == null){
            convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item,null);
            holder = new ViewHolder();
            holder.tvName = (TextView)convertView.findViewById(R.id.mTv1);
            holder.tvSex = (TextView)convertView.findViewById(R.id.mTv2);
            convertView.setTag(holder);
        }
        holder = (ViewHolder)convertView.getTag();
        holder.tvName.setText(data.get(position).getName());
        holder.tvSex.setText(data.get(position).getSex());
        return convertView;
    }

我们可以先再次理解下这段代码,首先我们每次都要创建一个holder持有者对象,设置到对应的converView的setTag中,然后,每次要拿到hoder对象,对hoder对象中的控件进行操作,对于上面的代码我们可以直接将他简化为以下几个步奏

1.ViewHolder holder = 拿到holder //每次拿到对应的holder对象即可
2.TextView tv = holder.getView() //拿到每个控件对应的id
3. tv.setText() //对控件进行操作
4. return view //返回view即可

下面开始写一个通用的ViewHolder通用类,来优化我们的实现,代码如下:

ViewHolder.java

public class ViewHolder {
    //被点击的当前位置
    private int position;

    //用一个map集合来保存每个控件的id,这个SparseArray是android提供的一个比map使用效率更高的一个
    //集合,但是局限是,key只能是int类型,所以当键值对涉及到key是int类型时,可以优先考虑使用这个集合
    private SparseArray<View> array;

    //复用的布局
    private View convertView;

    //上下文
    private Context context;

    //解析的布局资源id
    private int layout;
    public ViewHolder(){

    }

    //带三个构造的方法,这里将构造方法私有,防止外界去创建,通过自身的静态方法去创建对象即可
    private ViewHolder(ViewGroup parent,int position,int layout){
        this.position = position;
        this.context = parent.getContext();
        //每次创建对象,就将布局解析出来
        convertView = LayoutInflater.from(parent.getContext()).inflate(layout,null);
        //然后将对象保存到convertView对应的setTag中,方便每次该获取
        convertView.setTag(this);
        array = new SparseArray<>();
    }

    //通过这个方法,可以创建ViewHolder对象
    public static ViewHolder getHolder(View convertView, ViewGroup parent, int position,int layout){
        //每次判断converView是否为空,如果为空就直接返回这个创建的对象
        if(convertView == null){
            return new ViewHolder(parent,position,layout);
        }else{
            //不为空的情况,就跟我们上面的代码一样,每次通过复用的控件拿到对应的ViewHolder对象
            ViewHolder holder = (ViewHolder)convertView.getTag();
            //这里一定要更新下下标的位置,虽然对象相同,但是我们每次都要更新现有的位置,
            holder.position = position;
            //返回已经创建好的holder对象
            return holder;
        }
    }

    /**
    * 这个方法是通过控件id拿到对应的控件
    */
    public <T extends View> T getView(int viewId){
        //每次通过viewId键去拿到到对应的控件
        View view =  array.get(viewId);
        //如果为空,表示该集合中还没有存入该控件
        if(view == null){
            //先要去通过converView拿到控件id
            view = convertView.findViewById(viewId);
            //保存到集合中,以便下次直接获取
            array.put(viewId,view);
        }
        //返回View的子类控件,采用泛型的方便是不需要强制转换了
        return (T)view;
    }
    //得到converView布局
    public View getConvertView(){
        return convertView;
    }

通过上面的代码我们就已经封装好了一个通用的ViewHolder类了,下面我们的MyAdapter.java则可以更加简单的只写一下代码了:

public class MyAdapter extends MyBaseAdapter {
    public MyAdapter(List<Student> data) {
        super(data);
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = ViewHolder.getHolder(convertView,parent,position, R.layout.list_item);
        TextView tvName = holder.getView(R.id.mTv1);
        TextView tvSex = holder.getView(R.id.mTv2);
        tvName.setText(data.get(position).getName());
        tvSex.setText(data.get(position).getSex());
        return holder.getConvertView();
    }
}

好了,上面的代码是不更简单了呢,其实我们这里只是封装了ViewHolder类哦,还有更通用的等着我们去封装呢,下次我们需要封装的就是如何把getView中的代码再一次进行封装,已达到更优。

  • 2
    点赞
  • 3
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:编程工作室 设计师:CSDN官方博客 返回首页
评论

打赏作者

David_GodV

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值