Android开发专栏之ListView一两种适配器的使用和优化

来源:互联网 发布:网络直播策划案脚本 编辑:程序博客网 时间:2024/06/08 13:39

前言:

最近花了几天时间研究了下ListView的优化,然后写了一些demo,现在把这些天的东西总结。ListView的适配器主要有三种,ArrayAdapter,SimpleAdapter,SimpleCursorAdapter,我对于简单游标适配器还不了解,这里只说前两种。优化方式这里提到的主要就是converView,ViewHolder.还有加载图片的优化,这里先不涉及,我到后面会补充哦。
不同的适配器的区别主要是使用的数据结构不同,数组适配器就是使用数组,而简单适配器一般使用HashMap或者Map。

两种适配器:

1.ArrayAdapter:

数组适配器有三种使用方法
第一种: 是直接使用一个TextView作为根布局

<?xml version="1.0" encoding="utf-8"?><TextView    xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/tv_itemContent_arrayAdapter1"    android:layout_width="wrap_content"    android:layout_height="wrap_content" />

然后直接调用API里的ArrayAdapter就可以了:

//用这种方式来创建listView        ListView mlistView=new ListView(this);        String[] items={"1.ArrayAdapterData1",                "2.ArrayAdapterData2",                "3.ArrayAdapterData3",                "4.ArrayAdapterData4"};        //创建数组适配器方式1        //参数1:context        //参数2:listView的layout的id,要注意的是这个layout里面只能有一个控件就是TextView,连layout都不可以有!否则程序崩溃        //数组 ArrayAdapter<String> arrayAdapter=new  ArrayAdapter<String>(this,R.layout.listview_arrayadapter_layout1,items);

Demo运行:

这里写图片描述

第二种:可以有Layout作为ListView的根布局,并且可以有几个控件,但是只能填充其中一个TextView的内容

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="horizontal"    android:layout_width="match_parent"    android:layout_height="match_parent"><TextView    android:id="@+id/tv_itemContent_arrayAdapter2"    android:layout_width="wrap_content"    android:layout_height="wrap_content" /><Button    android:id="@+id/bt_itemButton_arrayAdapter2"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:text="button"    android:onClick="onButtonClick"    /></LinearLayout>

然后这样调用:

 //用这种方式来创建listView        ListView mlistView=new ListView(this);        String[] items={"1.ArrayAdapterData1",                "2.ArrayAdapterData2",                "3.ArrayAdapterData3",                "4.ArrayAdapterData4"};  //创建数组适配器方式2        //参数1:context        //参数2:listView的layout的id,里面有多个控件        //要填充数组数据的textView的id        //数组  ArrayAdapter<String>  arrayAdapter=                                new  ArrayAdapter<String>(this,R.layout.listview_arrayadapter_layout2,R.id.tv_itemContent_arrayAdapter2,items);

demo运行:
这里写图片描述

第三种:可以自定义ArrayAdapter,因为数组适配器的内容只能是一个元素,无法填充每行有多个控件的ListView,所以只要把数组元素换成自定义的类对象就可以了,然后还要重写ArrayAdapter,因为原本的ArrayAdapter的getView()方法无法根据我们自定义的类对象来填充数据。
自定义类:

/**自定义数组适配器的item类 * Created by daniel on 15-8-14. */public class Fruit {    private int imageId;    private String name;    public Fruit(int imageId,String name){        this.imageId=imageId;        this.name=name;    }    public int getImageId(){        return imageId;    }    public void setImageId(int imageId){        this.imageId=imageId;    }    public String getName(){        return name;    }    public void setName(String name){        this.name=name;    }    @Override    public String toString(){        return "Fruit{"+                "imageId="+this.imageId+                ",name='"+this.name+'\''+                '}';    }}

自定义的适配器类:

/**自定义的ArrayAdapter * Created by daniel on 15-8-14. */public class FruitAdapter extends ArrayAdapter<Fruit>{    private List<Fruit> list;    private Context context;    private int resourceId;    private LayoutInflater inflater;    /**     * 构造方法     * @param context     * @param resourceId ListView的layout的id     * @param list 填充数据     */    public FruitAdapter(Context context,int resourceId,List<Fruit> list){        super(context,resourceId,list);        inflater=LayoutInflater.from(context);        this.context=context;        this.resourceId=resourceId;        this.list=list;    }    @Override    public View getView(int position,View convertView,ViewGroup parent){        ViewHolder viewHolder=null;        Fruit fruit=getItem(position);        if(convertView==null){            convertView=inflater.inflate(R.layout.listview_arrayadapter_layout3,parent,false);            viewHolder=new ViewHolder();           viewHolder.mitemImage=(ImageView)convertView.findViewById(R.id.iv_itemImage_arrayAdapter3);            viewHolder.mitemName=(TextView)convertView.findViewById(R.id.tv_itemContent_arrayAdapter3);            convertView.setTag(viewHolder);        }else{            viewHolder=(ViewHolder)convertView.getTag();        }        viewHolder.mitemImage.setImageResource(fruit.getImageId());        viewHolder.mitemName.setText(fruit.getName());        return convertView;    }    private final static class ViewHolder{        ImageView mitemImage;        TextView mitemName;    }}

这么调用:

 private List<Fruit> mfruitList;    private ListView mlistView;    FruitAdapter fruitAdapter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_list_view__array_adapter__demo2);        mlistView=(ListView)findViewById(R.id.listView_arrayAdapter3);        mfruitList=new ArrayList<Fruit>();        initData();        fruitAdapter=new FruitAdapter(this,R.layout.listview_arrayadapter_layout3,mfruitList);        mlistView.setAdapter(fruitAdapter);        mlistView.setOnItemClickListener(new AdapterView.OnItemClickListener() {            @Override            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {                Fruit fruit=mfruitList.get(position);                Toast.makeText(ListView_Extends_ArrayAdapter_Demo.this, fruit.toString(), Toast.LENGTH_SHORT).show();            }        });    }    /**     * 填充数据到数组里     */    private void initData() {        Fruit apple=new Fruit(R.drawable.ic_launcher,"苹果");        mfruitList.add(apple);        Fruit waterMelon=new Fruit(R.drawable.ic_launcher,"西瓜");        mfruitList.add(waterMelon);    }

demo运行:
这里写图片描述

2.SimpleAdapter:

SimpleAdapter一般使用的是HashMap,根据不同的key向不同的控件内填充内容。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    android:paddingBottom="@dimen/activity_vertical_margin"    tools:context="com.study.daniel.gridviewtest.ListView_SimpleAdapter_Demo"><ImageView    android:id="@+id/iv_itemImage_simpleAdapter"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_marginLeft="3dp"    android:src="@drawable/ic_launcher"    /><TextView    android:id="@+id/tv_itemName_simpleAdapter"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_toRightOf="@+id/iv_itemImage_simpleAdapter"    android:layout_marginTop="5dp"    android:text="itemName"    /><TextView    android:id="@+id/tv_itemPhone_simpleAdapter"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_below="@+id/tv_itemName_simpleAdapter"    android:layout_toRightOf="@+id/iv_itemImage_simpleAdapter"    android:layout_alignBaseline="@+id/iv_itemImage_simpleAdapter"    android:text="itemPhone"    /><TextView    android:id="@+id/tv_itemRegion_simpleAdapter"    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_toRightOf="@+id/tv_itemPhone_simpleAdapter"    android:layout_alignBaseline="@+id/tv_itemPhone_simpleAdapter"    android:layout_marginLeft="10dip"    android:text="itemRegion"    /></RelativeLayout>

调用:

 private String[] itemNames={"Mr 1","Mr 2","Mr 3","Mr 4" ,"Miss 5"};    private String[] itemPhones={"000","001","002","003","004"};    private String[] itemRegions={"纽约","东京","哥伦比亚","安徽","未知"};    private ArrayList<Map<String,Object>> mInfos=new ArrayList<Map<String,Object>>();    private ListView mlistView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        // setContentView(R.layout.listview_simpleadapter_layout);        //数据填充        for(int i=0;i<itemNames.length;i++){            Map<String,Object> item=new HashMap<String,Object>();            item.put("itemName",itemNames[i]);            item.put("itemPhone",itemPhones[i]);            item.put("itemRegion",itemRegions[i]);            item.put("itemImage",R.drawable.ic_launcher);            mInfos.add(item);        }        mlistView=new ListView(this);        SimpleAdapter simpleAdapter=new SimpleAdapter(this,mInfos,R.layout.listview_simpleadapter_layout,                new String[]{"itemName","itemPhone","itemRegion","itemImage"},                new int[]{R.id.tv_itemName_simpleAdapter,R.id.tv_itemPhone_simpleAdapter,R.id.tv_itemRegion_simpleAdapter,R.id.iv_itemImage_simpleAdapter}        );        mlistView.setAdapter(simpleAdapter);        setContentView(mlistView);}

demo运行如下:
这里写图片描述

优化:

关于优化主要就是两步,(这个理解起来要花一些时间,我研究了三天才搞懂)一个是防止每次都将ListView的每一行的布局文件实例化,所以在向上翻动的时候重用第一页的布局对象,及getView的convertView参数。但是这只是重用了布局文件,防止了布局文件实例化的IO操作,ListView每一行可能有很多控件,比如Button,TextView,ImageView等,这些控件从xml文件里面读取并且实例化成对象也是非常耗费内存的,所以也得重用,可以使用ViewHolder,这个类的对象中将保存listView每一行的控件为成员变量,然后用setTag和这个convertView绑定在一起,其实看下源码就是保存这个Object对象在convertView里面了,方便下次取出。然后每次getView的时候只要给ViewHolder的里面的成员变量设置显示内容就可以了。因为这些子布局的控件已经变成viewHolder对象的成员变量了,设定它们的内容就是设定控件的内容。
另外,如果ListView不同的行具有不同的子布局的话,就需要不同的ViewHolder了,有几种布局就需要几种viewHolder,因为保存的控件不同了,然后重写两个函数getViewTypeCount,getViewType,在getView里面显式调用getViewType方法,在选择不同的viewHolder时候加一个switch判断就好了。代码这里就不展示,代码会上传,大家自行下载。
demo运行
只有一种布局的优化demo:
这里写图片描述
多种布局的优化demo:
这里写图片描述

非常容易犯错的问题:

在写多种布局的优化demo时候,程序总是崩溃,调bug都调不出来,后来找了整整一天,才发现是因为布局种类的type下标必须从0开始!!如果从1开始,就会造成程序崩溃!

代码上传:

1.ArrayAdapter使用方式1,2的demo:
http://download.csdn.net/detail/u012320459/9007389
2.ArrayAdapter使用方式3(自定义)的demo:
http://download.csdn.net/detail/u012320459/9007423
3.SimpleAdapter使用demo:
http://download.csdn.net/detail/u012320459/9007463
4.扩展自BaseAdapter的适配器的单一布局的优化demo:
http://download.csdn.net/detail/u012320459/9007469
5.扩展自BaseAdapter的适配器的多种布局的优化demo:
http://download.csdn.net/detail/u012320459/9007655

0 0
原创粉丝点击