Android实现自定义适配器详解

Android实现一个简单的自定义适配器

经常面试会被问以下的面试题:ListView的优化方案 答:

(1)如果自定义适配器,那么在getView方法中要考虑方法传进来的参数convertView是否为null,如果为null就创建convertView并返回,如果不为null则直接使用。在这个方法中尽可能少创建view。

(2)给convertView设置tag(setTag()),传入一个viewHolder对象,用于缓存要显示的数据,可以达到图像数据异步加载的效果。

(3)如果listview需要显示的item很多,就要考虑分页加载。比如一共要显示100条或者更多的时候,我们可以考虑先加载20条,等用户拉到列表底部的时候再去加载接下来的20条。


在安卓开发的过程中,我们经常会用到ListView控件,在ListView中也会有一个个的item,我们在使用的时候需要对其进行数据的适配,那么Android系统提供了一系列的适配器(Adapter)来实现数据的适配。又已知我常用的适配器包括:BaseAdapter、SimpleAdapter、ArrayAdapter。其中后两项SimpleAdapter和ArrayAdapter都是BaseAdapter的子类,相当于是Android提供的两种可以直接来用的适配器,在使用的时候我们可以根据自己的需要传入参数即可。那么其实在实际开发的时候,我们用的更多的是BaseAdapter这样一个适配器来做一个“自定义”的适配器。那么接下来,我会用一个小小的例子来介绍使用BaseAdapter。

1.首先我们需要用来写一个实体类,这个实体类用来封装我们需要适配显示的信息的集合。

比如我这里定义了一个联系人的实体类(类的名字有点拼音化,不是很规范,但是不影响效果敲打),然后我这里定义了三个String类型的变量:姓名、学号、简介。然后下面是getters和setters方法还有一个“满参”构造函数,还有一个无参构造函数。

  1. public class LianxirenBean {  
  2.     private String name;  
  3.     private String number;  
  4.     private String introduce;  
  5.   
  6.     public String getName() {  
  7.         return name;  
  8.     }  
  9.   
  10.     public void setName(String name) {  
  11.         this.name = name;  
  12.     }  
  13.   
  14.     public String getNumber() {  
  15.         return number;  
  16.     }  
  17.   
  18.     public void setNumber(String number) {  
  19.         this.number = number;  
  20.     }  
  21.   
  22.     public String getIntroduce() {  
  23.         return introduce;  
  24.     }  
  25.   
  26.     public void setIntroduce(String introduce) {  
  27.         this.introduce = introduce;  
  28.     }  
  29.   
  30.     public LianxirenBean() {  
  31.         super();  
  32.     }  
  33.   
  34.     public LianxirenBean(String name, String number, String introduce) {  
  35.         super();  
  36.         this.name = name;  
  37.         this.number = number;  
  38.         this.introduce = introduce;  
  39.     }  
  40.   
  41. }  
public class LianxirenBean {
    private String name;
    private String number;
    private String introduce;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    public String getIntroduce() {
        return introduce;
    }

    public void setIntroduce(String introduce) {
        this.introduce = introduce;
    }

    public LianxirenBean() {
        super();
    }

    public LianxirenBean(String name, String number, String introduce) {
        super();
        this.name = name;
        this.number = number;
        this.introduce = introduce;
    }

}
2.其次,我就需要新建一个类(LianxirenAdapter)继承BaseAdapter。

然后我先随意建一个类继承BaseAdapter然后看看里面都有什么

  1. import android.view.View;  
  2. import android.view.ViewGroup;  
  3. import android.widget.BaseAdapter;  
  4.   
  5. public class TestAdapter extends BaseAdapter {  
  6.   
  7.     @Override  
  8.     public int getCount() {  
  9.         // TODO Auto-generated method stub  
  10.         return 0;  
  11.     }  
  12.   
  13.     @Override  
  14.     public Object getItem(int position) {  
  15.         // TODO Auto-generated method stub  
  16.         return null;  
  17.     }  
  18.   
  19.     @Override  
  20.     public long getItemId(int position) {  
  21.         // TODO Auto-generated method stub  
  22.         return 0;  
  23.     }  
  24.   
  25.     @Override  
  26.     public View getView(int position, View convertView, ViewGroup parent) {  
  27.         // TODO Auto-generated method stub  
  28.         return null;  
  29.     }  
  30.   
  31. }  
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;

public class TestAdapter extends BaseAdapter {

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub
        return null;
    }

}
可以看出来,这里因为继承了BaseAdapter,那么就要实现四个方法:getCount()、getItem()、getItemId()和getView()。

显然,

getCount()方法是要返回ListView里面所有的item子项的个数,因为ListView里面是有很多个item子项需要被适配的,而且我上面已经定义好了一个实体类LianxirenBean用于封装每一个item子项内部需要适配的内容的一个集合,换句话说,每一个实体,就是一个完整的item里面所要适配的所有内容,那么我们又已知,一个ListView不一定只会有一个item,那么显然,我们需要在Adapter里面定义一个泛型为LianxirenBean的一个List,用于表示所有的item子项。即:private List<LianxirenBean> lxrs; 那么定义了之后,显然在getCount()方法下面,就要return lxrs.size();

getItem()方法是要返回每一个item子项,已知我们已经定义了一个泛型为LianxirenBean的List,那么我们如果是要得到每一个子项的话,我们就要用到getposition()方法用于动态的获得每一个item,即这里我们写:return lxrs.get(position);

getItemId()方法是要返回每个item子项的Id,显然我们这里可以直接写return position;即可。
如果说实现前面的三个方法只是做了准备工作了的话,那么显然getView方法就是真正的重头戏了。也就是在getView方法里面我们就要完成和ListView里面的每一个item子项的适配工作。那么接下来我就会用一个完整的代码来讲解这个部分怎样写,还有一个简单的Adapter里面所必须要写的内容。

  1. import java.util.List;  
  2.   
  3. import com.example.mynote.R;  
  4. import android.content.Context;  
  5. import android.view.LayoutInflater;  
  6. import android.view.View;  
  7. import android.view.ViewGroup;  
  8. import android.widget.BaseAdapter;  
  9. import android.widget.TextView;  
  10. import dxxy_swy_Bean.LianxirenBean;  
  11.   
  12. public class LianxirenAdapter extends BaseAdapter {  
  13.       
  14.     private List<LianxirenBean> lxrs;  
  15.     private Context ctx;  
  16.     private LayoutInflater mInflater;  
  17.       
  18.     public LianxirenAdapter(Context context, List<LianxirenBean> lxrs) {  
  19.         ctx = context;  
  20.         this.lxrs = lxrs;  
  21.         mInflater = LayoutInflater.from(context);  
  22.     }  
  23.   
  24.     @Override  
  25.     public int getCount() {  
  26.         // TODO Auto-generated method stub  
  27.         return lxrs.size();  
  28.     }  
  29.   
  30.     @Override  
  31.     public Object getItem(int position) {  
  32.         // TODO Auto-generated method stub  
  33.         return lxrs.get(position);  
  34.     }  
  35.   
  36.     @Override  
  37.     public long getItemId(int position) {  
  38.         // TODO Auto-generated method stub  
  39.         return position;  
  40.     }  
  41.     @Override  
  42.     public View getView(int position, View convertView, ViewGroup parent) {  
  43.         // TODO Auto-generated method stub  
  44.         LianxirenBean lxr = lxrs.get(position);  
  45.         ViewHolder viewHolder = null;  
  46.         if (convertView == null) {  
  47.             convertView = mInflater.inflate(R.layout.lianxiren_item, null);  
  48.             viewHolder = new ViewHolder();  
  49.             viewHolder.name = (TextView) convertView.findViewById(R.id.tv_name);  
  50.             convertView.setTag(viewHolder);  
  51.         } else {  
  52.             viewHolder = (ViewHolder) convertView.getTag();  
  53.         }  
  54.         viewHolder.name.setText(lxr.getName());  
  55.         return convertView;  
  56.     }  
  57.   
  58.     static class ViewHolder {  
  59.         public TextView name;  
  60.         public TextView number;  
  61.         public TextView introduce;  
  62.     }  
  63. }  
import java.util.List;

import com.example.mynote.R;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import dxxy_swy_Bean.LianxirenBean;

public class LianxirenAdapter extends BaseAdapter {

    private List<LianxirenBean> lxrs;
    private Context ctx;
    private LayoutInflater mInflater;

    public LianxirenAdapter(Context context, List<LianxirenBean> lxrs) {
        ctx = context;
        this.lxrs = lxrs;
        mInflater = LayoutInflater.from(context);
    }

    @Override
    public int getCount() {
        // TODO Auto-generated method stub
        return lxrs.size();
    }

    @Override
    public Object getItem(int position) {
        // TODO Auto-generated method stub
        return lxrs.get(position);
    }

    @Override
    public long getItemId(int position) {
        // TODO Auto-generated method stub
        return position;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // TODO Auto-generated method stub
        LianxirenBean lxr = lxrs.get(position);
        ViewHolder viewHolder = null;
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.lianxiren_item, null);
            viewHolder = new ViewHolder();
            viewHolder.name = (TextView) convertView.findViewById(R.id.tv_name);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }
        viewHolder.name.setText(lxr.getName());
        return convertView;
    }

    static class ViewHolder {
        public TextView name;
        public TextView number;
        public TextView introduce;
    }
}
上面的代码里边,我们首先是先写了Adapter的一个构造方法,这是因为我们要在实现逻辑的Activity里面获得适配器的对象,并且还要对适配器做初始化,那么适配器的对象就必须要实现适配器的构造方法。只有这样才能够真正的完成适配器的初始化,才能够在Activity中使用适配器。

我们这个Adapter的构造方法中有两个参数:Context(上下文,这个我是一般理解为“在哪个界面做适配”)、第二个参数是那个泛型为LianxirenBean的List的对象,这个我的理解是所需要适配的内容。

顺带的我在这里表达一下我对适配器这个概念的个人理解:所谓适配,我们首先需要知道我们在哪里适配(Context),还需要知道我们适配的内容是什么(List<LianxirenBean>lxrs),那么因为适配的内容是需要适配到每一个的item之中

那么针对item而言显然又有几个东西是必须要知道的:1.item的布局、2.item里面都有什么控件需要做适配、3.每个控件对应的都需要适配什么内容?

那么很显然,这些疑问是需要在getView()方法里面得到解决的。那么就来看getView方法。

在此之前我们先考虑一个问题,在android开发中Listview是一个很重要的组件,它以列表的形式根据数据的长自适应展示具体内容,用户可以自由的定义listview每一列的布局,但当listview有大量的数据需要加载的时候,会占据大量内存,影响性能,这时候就需要按需填充并重新使用view来减少对象的创建。解决这个问题可见,我们是利用了ViewHolder方法,这是因为ViewHolder在适配器里,可以在listview滚动的时候快速设置值,而不必每次都重新创建很多对象,从而提升性能。

在getView()方法中,我们首先定义了一个LianxirenBean实体类的一个对象,初始化的值是lxrs.getposition(),又因为上面实现的第二个方法:getItem()的返回值刚好也是lxrs.getposition(),那么也就是说每一个实体就是对应着每一个item子项,显然这跟我们上面说的是符合的。

然后初始化了一个ViewHolder的对象,初始值为空。可知在文件的最下边,我们定义了一个ViewHolder类,里面放着的是三个TextView属性的变量,刚好一方面对应着我们的实体类的三个属性,同时这个也是我们的每一个的item布局文件里面的三个控件的属性,那么显然,我们是需要对这三个变量进行绑定控件实例化。(注:我这个因为是只需要显示联系人的姓名,所以后边的学号和简介都没有做适配,对应的也没有获得实例)

  1. if (convertView == null) {  
  2.             convertView = mInflater.inflate(R.layout.lianxiren_item, null);  
  3.             viewHolder = new ViewHolder();  
  4.             viewHolder.name = (TextView) convertView.findViewById(R.id.tv_name);  
  5.             convertView.setTag(viewHolder);  
  6.         } else {  
  7.             viewHolder = (ViewHolder) convertView.getTag();  
  8.         }  
  9.         viewHolder.name.setText(lxr.getName());  
  10.         return convertView;  
if (convertView == null) {
            convertView = mInflater.inflate(R.layout.lianxiren_item, null);
            viewHolder = new ViewHolder();
            viewHolder.name = (TextView) convertView.findViewById(R.id.tv_name);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }
        viewHolder.name.setText(lxr.getName());
        return convertView;
这部分的代码就是完成我上面说过的三个疑问: 1.item的布局、2.item里面都有什么控件需要做适配、3.每个控件对应的都需要适配什么内容?

到此的话,一个基本的Adapter就完成了,那么也只是把适配器的部分完成了,我们同样需要在Activity中获得适配器的对象,然后为我们的ListView设置适配器才是真正完成了适配。

在Activity代码中,我把主要的逻辑代码给出

1.定义泛型为LianxirenBean的List的对象

private List<LianxirenBean> lxrs = new ArrayList<LianxirenBean>();

2.定义适配器对象,并实例化适配器

private LianxirenAdapter lxrAdapter;

lxrAdapter = new LianxirenAdapter(context, lxrs);

3.获得ListView的实例化并且给ListView绑定适配器

private ListView lianxiren_list;

lianxiren_list = (ListView) view.findViewById(R.id.lianxiren_list);

lianxiren_list.setAdapter(lxrAdapter);

4.给lxrs赋值(赋值的内容就是设置适配的内容,我这里是一个数据库查询语句)

private LianxirenOperator lxrOperator;

lxrOperator = new LianxirenOperator(context);

lxrs = lxrOperator.queryMany();

  1. // 查询所有的联系人信息  
  2.     public List<LianxirenBean> queryMany() {  
  3.         ArrayList<LianxirenBean> lxrs = new ArrayList<LianxirenBean>();  
  4.         Cursor c = db.rawQuery(”select * from lxrData”null);  
  5.         while (c.moveToNext()) {  
  6.             LianxirenBean lxr = new LianxirenBean();  
  7.             lxr.setName(c.getString(0));  
  8.             lxr.setNumber(c.getString(1));  
  9.             lxr.setIntroduce(c.getString(2));  
  10.             lxrs.add(lxr);  
  11.         }  
  12.         c.close();  
  13.         return lxrs;  
  14.     }  
// 查询所有的联系人信息 
public List<LianxirenBean> queryMany() {
ArrayList<LianxirenBean> lxrs = new ArrayList<LianxirenBean>();
Cursor c = db.rawQuery("select * from lxrData", null);
while (c.moveToNext()) {
LianxirenBean lxr = new LianxirenBean();
lxr.setName(c.getString(0));
lxr.setNumber(c.getString(1));
lxr.setIntroduce(c.getString(2));
lxrs.add(lxr);
}
c.close();
return lxrs;
}
至此的话,一个基本的自定义的适配器就基本实现功能了
然后,可能会因为写的时间比较赶,然后在某些细节就会避免不聊有忽略的问题,那么如果有对这里面的代码有没有理解的或者是还有问题的话,可以在评论区留言,笔者会尽自己所能回答问题。

猜你喜欢

转载自blog.csdn.net/zhaomengszu/article/details/79854122