您的位置:首页 > 其它

ListView进一步详解

2014-07-25 14:58 197 查看
ListView应该是android里最常用的显示数据的控件了。它用来将集合数据一个个抽取出来按照从头到尾往下显示的控件里,这些是在手机中经常看到的一些布局。

列表的显示需要三个元素:

1.ListVeiw 用来展示列表的View。

2.适配器 用来把数据映射到ListView上的中介。

3.数据 具体的将被映射的字符串,图片,或者基本组件。

根据列表的适配器类型,列表分为三种,ArrayAdapter,SimpleAdapter和SimpleCursorAdapter

其中以ArrayAdapter最为简单,只能展示一行字。SimpleAdapter有最好的扩充性,可以自定义出各种效果。SimpleCursorAdapter可以认为是SimpleAdapter对数据库的简单结合,可以方面的把数据库的内容以列表的形式展示出来。

简单的ListView布局(单行显示信息)

首先我们先创建存放ListView的Activity所需要的布局xml文件。

[c-sharp] view
plaincopyprint?

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

>

<ListView

android:id="@+id/list1"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

/>

</LinearLayout

上面代码我们创建了一个布局配置文件,里面只放了一个ListView控件,将其ID设置为:list1。

接下来是java代码,大概步骤是:先找出ListView,然后往ListView里填充东西。

[c-sharp] view
plaincopyprint?

package com.king.listview;

import android.app.Activity;

import android.os.Bundle;

import android.widget.ArrayAdapter;

import android.widget.ListView;

public class MainActivity extends Activity {

final String[] data = new String[]{

"第一章","第二章","第三章","第四章","第五章"

};

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

ListView lv = (ListView) findViewById(R.id.list1);

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,

android.R.layout.simple_list_item_1, data);

lv.setAdapter(adapter);

}

}

以上是运行这个运行这个ListView,并往里面填充东西的代码。在android中,由于数据来源多种多样,如:从资源文件读取、从数据库中读取、从网络上其他地方读取而最终这些数据都将被展示在ListView中,所以这边android就用了adapter设计模式,对应每种数据来源使用对应的adapter来连接数据和视图。

以上的代码很简单,运行效果如下:



上面代码有一定的局限性,就是比如你要创建稍微复杂点的布局,如名片卡,显示名字,电话信息,地址等。像这种稍微复杂的布局就无法用上面的来做,因为在上面的例子中,ArrayAdapter里装的数据都是String型,单行仅能显示一条数据。

进阶的ArrayAdapter,自定义布局来显示ArrayAdapter里面装的对象。这边我们沿用上面的xml布局,因为要往ArrayAdapter里面装对象,所以这边我们先定义一个对象,最后要做的就是将对象的信息显示在ListView中。

[c-sharp] view
plaincopyprint?

package com.king.listview;

public class Person {

public String name;

public int age;

public String email;

public String address;

public Person(String name, int age, String email, String address) {

super();

this.name = name;

this.age = age;

this.email = email;

this.address = address;

}

@Override

public String toString() {

return "Person [name=" + name + ", age=" + age + ", email=" + email

+ ", address=" + address + "]";

}

}

接下来将这个对象装进ArrayAdapter显示出来。

[c-sharp] view
plaincopyprint?

package com.king.listview;

import android.app.Activity;

import android.os.Bundle;

import android.widget.ArrayAdapter;

import android.widget.ListView;

public class ArrayAdapterList extends Activity {

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

//创建数据源.

Person[] data = new Person[]{

new Person("蔡志坤",25,"ffczk86@gmail.com","厦门市"),

new Person("李杰华",25,"aa@bb.com","漳州市"),

new Person("张亮",25,"cc@gmail.com","厦门市"),

new Person("陈旭",25,"ccadd@gmail.com","厦门市"),

new Person("刘玄德",25,"ffczk86@gmail.com","福州市")

};

ListView lv = (ListView) findViewById(R.id.list1);

//使用ArrayAdapter, 使用的布局仍然是由android提供的simple_list_item_1

ArrayAdapter<Person> adapter = new ArrayAdapter<Person>(this,

android.R.layout.simple_list_item_1, data);

lv.setAdapter(adapter);

}

}

ArrayAdapter(Context context, int textViewResourceId, List<T> objects)来装配数据,要装配这些数据就需要一个连接ListView视图对象和数组数据的适配器来两者的适配工作,ArrayAdapter的构造需要三个参数,依次为this,布局文件(注意这里的布局文件描述的是列表的每一行的布局,android.R.layout.simple_list_item_1是系统定义好的布局文件只显示一行文字,数据源(一个List集合)。同时用setAdapter()完成适配的最后工作。

效果图是:



依据上面的结果,可以得出结论,ArrayAdapter默认是调用它里面装的对象的toString方法来展示数据。但是上面的信息显得太凌乱,所以这边要介绍另外一个ArrayAdapter的使用方法,就是扩展ArrayAdapter,通过继承它来实现自定义视图的展现。所以刚开始要做的是先创建一个xml布局文件,确定ListView里每行数据显示的样式。

[c-sharp] view
plaincopyprint?

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent">

<TextView

android:id="@+id/person_name"

android:textSize="23sp"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

/>

<TextView

android:id="@+id/person_age"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

/>

<TextView

android:id="@+id/person_email"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

/>

<TextView

android:id="@+id/person_address"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

/>

</LinearLayout>

接下来通过继承ArrayAdapter,然后重写getView方法,用来将自定义布局的View注入到ListView中,然后将每条数据装进对应的View中。

[c-sharp] view
plaincopyprint?

class PersonAdapter extends ArrayAdapter<Person>{

LayoutInflater mLayoutInflater;

int resourceId;

Context mContext;

public PersonAdapter(Context context, int resourceId, Person[] objects) {

super(context, resourceId, objects);

//获取LayoutInflater 服务,用来从预定义的xml布局创建view对象.

this.resourceId = resourceId;

mLayoutInflater = LayoutInflater.from(context);

}

@Override

public View getView(int position, View convertView, ViewGroup parent) {

if(convertView == null){

//创建新的view视图.

convertView = mLayoutInflater.inflate(resourceId, null);

}

//获取当前要显示的数据

Person person = getItem(position);

TextView name = (TextView) convertView.findViewById(R.id.person_name);

TextView age = (TextView) convertView.findViewById(R.id.person_age);

TextView email = (TextView) convertView.findViewById(R.id.person_email);

TextView address = (TextView) convertView.findViewById(R.id.person_address);

name.setText(person.name);

age.setText(String.valueOf(person.age));

email.setText(person.email);

address.setText(person.address);

return convertView;

}

}

最终的效果图是:



以上继承ArrayAdapter类,重写getView方法,里面的实现还不是最高效率,这边涉及到ListView的懒加载机制和实现的原理,以后详论。

通过上面两个例子我们可以简单的了解创建ListView的一般步骤,总的说来可以总结为下面几个步骤。

创建ListView里item的布局xml文件,第一个例子引用了android.R.layout.simple_list_item_1 ,所以省略掉创建的步骤,缺点是只能显示一条信息。
创建数据源,数据源可能是手动添加或查询数据库获取。
通过继承ArrayAdapter,重写getView方法来进行数据绑定。

我们在分析下到底ListView是如何创建整个UI,而我们要如何才能写出运行效率高的代码,也顺便了解下如何使用android内置的支持更方便的编写一个ListView。

假设我们要显示一个有1000行数据的ListView,这样的数据量在手机中应该算很庞大了,那ListView会如何处理呢,以让系统运行时占用的空间时间最优化呢?

其实在ListView中有使用懒加载机制以提高显示的效率。在很多系统架构中我们都可以看到懒加载机制,如hibernate的懒加载机制,spring bean也有懒加载,所谓的懒加载应该是在需要时才用到,不需要时能不创建就不创建,尽量减少系统消耗。就以ListView来说:假设ListView总共要显示1000条数据,但是用户一次仅能看到10条左右,那就有其余的990条是看不到的,这时候系统可以不用创建这些View,等用户往下或往上拉动数据再创建新的要显示的View,这就是ListView中的懒加载机制。所以我们在写代码时候要特别注意这点,这边用图形来解释下这个机制。



如上面实际上要显示的条目可能有几十上百条,但是因为手机一个屏幕只能看到4条,所以ListView就创建4个View来显示ListView内部的Item,当用户往下拉动的时候:



当用户往下拉动时,原本装Item1的视图就看不到了,而本来看不到的Item5就显示出来了,系统还是一样只能看到5条item。ListView就将本来装Item1的View拖出来,放到最底端,然后更新里面的数据变成Item5,但是此时系统还是一样只创建了4个View,这样就节省了很多View的创建时间,只需要做替换内容的操作,提高了系统的效率。因此我们可以得出一个结论:假设每个View里面有4个TextView,因为在这个ListView中,系统一屏只能显示4个View,所以总共只需要创建16个TextView,而不管ListView里面有几百条记录,也都是由这16个TextView替换内容来完成显示。但是每次要替换内容都需要查找4个View,为了使系统的效率最大化,我们可以对它进行优化,我们创建一个静态类,保存对每个View中的4个TextView的跟踪,这样就不必每次都查找。具体实现如下中的例子:

[c-sharp] view
plaincopyprint?

class PersonAdapter extends ArrayAdapter<Person>{

LayoutInflater mLayoutInflater;

int resourceId;

Context mContext;

public PersonAdapter(Context context, int resourceId, Person[] objects) {

super(context, resourceId, objects);

//获取LayoutInflater 服务,用来从预定义的xml布局创建view对象.

this.resourceId = resourceId;

mLayoutInflater = LayoutInflater.from(context);

}

@Override

public View getView(int position, View convertView, ViewGroup parent) {

if(convertView == null){

//创建新的view视图.

convertView = mLayoutInflater.inflate(resourceId, null);

}

ViewHolder holder = (ViewHolder) convertView.getTag();

if(holder == null){

holder = new ViewHolder();

//查找每个ViewItem中,各个子View,放进holder中

holder.name = (TextView) convertView.findViewById(R.id.person_name);

holder.age = (TextView) convertView.findViewById(R.id.person_age);

holder.email = (TextView) convertView.findViewById(R.id.person_email);

holder.address = (TextView) convertView.findViewById(R.id.person_address);

//保存对每个显示的ViewItem中, 各个子View的引用对象

convertView.setTag(holder);

}

//获取当前要显示的数据

Person person = getItem(position);

holder.name.setText(person.name);

holder.age.setText(String.valueOf(person.age));

holder.email.setText(person.email);

holder.address.setText(person.address);

return convertView;

}

}

static class ViewHolder {

TextView name;

TextView age;

TextView email;

TextView address;

}

上面代码是优化后的结果。在上面代码中我们进行了优化,使ListView没有每次要替换内容都查找内部的View,而是保存引用,总共一屏能显示N条,就查找N次,而不是之前的总显示N条,就查找N次。

首先我们先了解下ListActivity这个类。

下面我们看看如何使用android内置对ListView的支持来更好的辅助我们编写一个ListView。

ListActivity继承了Activity,然后扩展了一些针对ListView操作的方法,如设置选中第几个,点击事件,设置adapter等。实际上ListActivity就是一个内置了一个ListView的Activity,我们在使用它时,可以不用调用setContentView方法就能显示出ListView,那到底这个ListView放在哪里呢?

使用ListActivity,android会帮忙创建一个id是android.R.id.list的ListView,所有的对ListActivity内置的ListView的操作也都是作用在这个id上,同时系统本身还会判断,如果这个ListView里的数据是空的,它会对应的显示一个id是android.R.id.empty的控件,用来指示用户这个ListView的数据为空,所有这些逻辑流程转换都是由系统完成,用户只需要关心显示的内容即可,所以大大方便了用户的开发。下面我们创建一个xml布局,简单介绍下如何使用内置的ListView。

[c-sharp] view
plaincopyprint?

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent">

<ListView android:drawSelectorOnTop="false"

android:id="@android:id/list" android:layout_height="0dip"

android:layout_weight="1" android:layout_width="fill_parent"/>

<TextView android:id="@android:id/empty"

android:layout_height="wrap_content"

android:layout_width="fill_parent" android:text="没有数据"/>

</LinearLayout

上面代码创建了布局文件,其中id是:@android:id/list的控件就是负责显示数据的ListView,@android:id/empty则是当ListView为空时负责显示提示信息的控件。当然这边你可以添加任意你需要显示的东西,如你可能需要一个工具条,几个按钮之类的,尽管添加,ListView只会影响到对应它所在的区域而不会影响其他的控件。

[c-sharp] view
plaincopyprint?

package com.king.listview;

import android.app.ListActivity;

import android.os.Bundle;

import android.widget.ArrayAdapter;

public class ListActivityDemo extends ListActivity {

final String[] data = new String[]{

"第一章","第二章","第三章","第四章","第五章"

};

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_list_demo);

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,

android.R.layout.simple_list_item_1, data);

setListAdapter(adapter);

}

}



如果修改java代码,让其显示空数据时,运行的效果如下:



我们可以通过重写ListActivity里的onListItemClick方法,来便捷的为ListView添加item click事件。

[c-sharp] view
plaincopyprint?

@Override

protected void onListItemClick(ListView l, View v, int position, long id) {

Toast.makeText(this, "您点击的是:" + data[position], Toast.LENGTH_LONG)

.show();

}

当用户点击ListView里面的Item时,效果如下:



综上,我们讨论了如何编写ListView中的自定义视图并优化其效率,如何使用ListActivity。在这边大家要熟悉android提供的一些预设的id和layout,如@android:id/list ,android.R.layout.simple_list_item_1等。因为这些布局很常用,利用它们可以减少很多编码工作,而且后面要学习的tabhost也是内置了一些预设置的layout或id来减少代码编写工作,如果不熟悉这些对未来扩展自定义UI将造成一定的难度。

到现在我们知道ListView,由于要显示数据,来源多种多样,所以使用了适配器模式。之前主要讲解了ArrayAdapter的使用方法,这边我们再分析下SimpleAdapter和SimpleCursorAdapter的使用方法。

SimpleAdapter主要是用于显示静态的数据。如果显示的数据一条只有一个信息我们可以通过使用ArrayAdapter<String>来实现,如果显示的是对象里的多种信息:一条显示一个人的名字,地址,联系电话等,我们可以通过继承ArrayAdapter,重写getView方法来提供视图绑定。但是还有一个更容易使用的,也是用来显示对象信息的适配器,SimpleAdapter。

SimpleAdapter也是将对象里的信息绑定到视图中,只不过这个对象不需要用户自定义,而是一个map对象,用户可以将需要显示的对象信息封装进一个map对象中,然后再通过配置对应的影射关系构建出视图。如下面我们构造一个数据源,用map封装信息:

[c-sharp] view
plaincopyprint?

//创建数据源.

List<Map<String,?>> data = new ArrayList<Map<String,?>>();

Map<String, String> map = new HashMap<String, String>();

map.put("name", "蔡志坤");

map.put("age", "25");

map.put("email", "ffczk86@gmail.com");

map.put("address", "厦门市");

data.add(map);

map = new HashMap<String, String>();

map.put("name", "李杰华");

map.put("age", "25");

map.put("email", "ffczk86@gmail.com");

map.put("address", "漳州市");

data.add(map);

将对象信息封装在map里就可以略去自定义的对象,减少类的数量。然后使用SimpleAdapter将List里面的map抽取出来绑定到对应的view中。

[c-sharp] view
plaincopyprint?

SimpleAdapter adapter = new SimpleAdapter(

this, //context

data, //要显示的数据

// list item view 资源文件的位置

R.layout.list_item_person,

//需要抽取的键值,相当于调用map.get("name") ... 获取数据

new String[]{"name" , "age", "email" , "address"},

//将对应抽取到的数据 绑定到对应的view id中,这边顺序得跟上面保持一致,一一对应

new int[]{R.id.person_name , R.id.person_age , R.id.person_email , R.id.person_address});

SimpleCursorAdapter

  sdk的解释是这样的:An easy adapter to map columns from a cursor to TextViews or ImageViews defined in an XML file. You can specify which columns you want, which views you want to display the columns, and the XML file that defines the appearance of these views。简单的说就是方便把从游标得到的数据进行列表显示,并可以把指定的列映射到对应的TextView中。

最终调用setListAdapter,设置绑定关系,最终代码如下(xml继续沿用上一篇的R.layout.list_item_person.xml):

[c-sharp] view
plaincopyprint?

package com.king.listview;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import android.app.ListActivity;

import android.os.Bundle;

import android.widget.SimpleAdapter;

public class SimpleAdapterDemo extends ListActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_list_demo);

//创建数据源.

List<Map<String,?>> data = new ArrayList<Map<String,?>>();

Map<String, String> map = new HashMap<String, String>();

map.put("name", "蔡志坤");

map.put("age", "25");

map.put("email", "ffczk86@gmail.com");

map.put("address", "厦门市");

data.add(map);

map = new HashMap<String, String>();

map.put("name", "李杰华");

map.put("age", "25");

map.put("email", "ffczk86@gmail.com");

map.put("address", "漳州市");

data.add(map);

SimpleAdapter adapter = new SimpleAdapter(

this, //context

data, //要显示的数据

// list item view 资源文件的位置

R.layout.list_item_person,

//需要抽取的键值,相当于调用map.get("name") ... 获取数据

new String[]{"name" , "age", "email" , "address"},

//将对应抽取到的数据 绑定到对应的view id中,这边顺序得跟上面保持一致,一一对应

new int[]{R.id.person_name , R.id.person_age , R.id.person_email , R.id.person_address});

setListAdapter(adapter);

}

}

最终效果如下:



接下去我们将数据源换成是由数据库查询出来的。创建一个DatabaseHelper类与数据库进行交互,然后查询数据库使用SimpleCursorAdapter适配器将查询的结果集显示到ListView中.

Listview动态加载数据

网络数据加载的时候,比如我有几百,甚至上千条数据,这些数据如果一次性全部加载到arraylist,然后再加载到Listview中。我们必然会去单独开线程来做,这样造成的结果就是会出现等待时间很长,用户体验非常不好。我的想法是动态加载数据,第一次加载十条,然后往下面滑动的时候再追加十条,再往下面滑动的时候再去追加,这样大大减少了用户等待的时间,同时给处理数据留下了时间。

[c-sharp] view
plaincopyprint?

import android.app.Activity;

import android.os.Bundle;

import android.os.Handler;

import android.os.Message;

import android.view.Gravity;

import android.view.View;

import android.view.ViewGroup;

import android.widget.AbsListView;

import android.widget.BaseAdapter;

import android.widget.LinearLayout;

import android.widget.ListView;

import android.widget.ProgressBar;

import android.widget.TextView;

import android.widget.Toast;

import android.widget.AbsListView.OnScrollListener;

import android.widget.LinearLayout.LayoutParams;

public class ListViewForLoading extends Activity implements OnScrollListener {

private listViewAdapter adapter = new listViewAdapter();

ListView listView;

LinearLayout loadingLayout;

private Thread mThread;

/**

* 设置布局显示属性

*/

private LayoutParams mLayoutParams = new LinearLayout.LayoutParams(

LinearLayout.LayoutParams.WRAP_CONTENT,

LinearLayout.LayoutParams.WRAP_CONTENT);

/**

* 设置布局显示目标最大化属性

*/

private LayoutParams FFlayoutParams = new LinearLayout.LayoutParams(

LinearLayout.LayoutParams.FILL_PARENT,

LinearLayout.LayoutParams.FILL_PARENT);

private ProgressBar progressBar;

/** Called when the activity is first created. */

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

init();

}

private void init() {

// TODO Auto-generated method stub

// 线性布局

LinearLayout layout = new LinearLayout(this);

// 设置布局 水平方向

layout.setOrientation(LinearLayout.HORIZONTAL);

// 进度条

progressBar = new ProgressBar(this);

// 进度条显示位置

progressBar.setPadding(0, 0, 15, 0);

// 把进度条加入到layout中

layout.addView(progressBar, mLayoutParams);

// 文本内容

TextView textView = new TextView(this);

textView.setText("加载中...");

textView.setGravity(Gravity.CENTER_VERTICAL);

// 把文本加入到layout中

layout.addView(textView, FFlayoutParams);

// 设置layout的重力方向,即对齐方式是

layout.setGravity(Gravity.CENTER);

// 设置ListView的页脚layout

loadingLayout = new LinearLayout(this);

loadingLayout.addView(layout, mLayoutParams);

loadingLayout.setGravity(Gravity.CENTER);

// 得到一个ListView用来显示条目

listView = (ListView) findViewById(R.id.tv);

// 添加到脚页显示

listView.addFooterView(loadingLayout);

// 给ListView添加适配器

listView.setAdapter(adapter);

// 给ListView注册滚动监听

listView.setOnScrollListener(this);

}

/**

* 要用用于生成显示数据

*

* @author huangbq

*

*/

class listViewAdapter extends BaseAdapter {

int count = 10;

public int getCount() {

return count;

}

public Object getItem(int pos) {

return pos;

}

public long getItemId(int pos) {

return pos;

}

public View getView(int pos, View v, ViewGroup p) {

TextView view;

if (v == null) {

view = new TextView(ListViewForLoading.this);

} else {

view = (TextView) v;

}

view.setText("ListItem " + pos);

view.setTextSize(20f);

view.setGravity(Gravity.CENTER);

view.setHeight(60);

return view;

}

}

@Override

public void onScroll(AbsListView view, int firstVisibleItem,

int visibleItemCount, int totalItemCount) {

// TODO Auto-generated method stub

if(firstVisibleItem+visibleItemCount==totalItemCount)

{

//开线程去下载网络数据

if (mThread == null || !mThread.isAlive()) {

mThread = new Thread() {

@Override

public void run() {

try {

//这里放你网络数据请求的方法,我在这里用线程休眠5秒方法来处理

Thread.sleep(5000);

} catch (InterruptedException e) {

e.printStackTrace();

}

Message message = new Message();

message.what = 1;

handler.sendMessage(message);

}

};

mThread.start();

}

}

}

@Override

public void onScrollStateChanged(AbsListView view, int scrollState) {

// TODO Auto-generated method stub

}

private Handler handler = new Handler() {

@Override

public void handleMessage(Message msg) {

// TODO Auto-generated method stub

switch (msg.what) {

case 1:

if (adapter.count <= 41) {

adapter.count += 10;

int currentPage = adapter.count / 10;

Toast.makeText(getApplicationContext(),"第" + currentPage + "页", Toast.LENGTH_LONG).show();

} else {

listView.removeFooterView(loadingLayout);

}

//重新刷新Listview的adapter里面数据

adapter.notifyDataSetChanged();

break;

default:

break;

}

}

};

}

运行结果如下图:





下面的程序是从电话簿中把联系人显示到类表中。先在通讯录中添加一个联系人作为数据库的数据。然后获得一个指向数据库的Cursor并且定义一个布局文件(当然也可以使用系统自带的)。

[c-sharp] view
plaincopyprint?

public class MyListView2 extends Activity {

private ListView listView;

//private List<String> data = new ArrayList<String>();

@Override

public void onCreate(Bundle savedInstanceState){

super.onCreate(savedInstanceState);

listView = new ListView(this);

Cursor cursor = getContentResolver().query(People.CONTENT_URI, null, null, null, null);

startManagingCursor(cursor);

ListAdapter listAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_expandable_list_item_1,

cursor,

new String[]{People.NAME},

new int[]{android.R.id.text1});

listView.setAdapter(listAdapter);

setContentView(listView);

}

}

Cursor cursor = getContentResolver().query(People.CONTENT_URI, null, null, null, null);先获得一个指向系统通讯录数据库的Cursor对象获得数据来源。

 startManagingCursor(cursor);我们将获得的Cursor对象交由Activity管理,这样Cursor的生命周期和Activity便能够自动同步,省去自己手动管理Cursor。

 SimpleCursorAdapter 构造函数前面3个参数和ArrayAdapter是一样的,最后两个参数:一个包含数据库的列的String型数组,一个包含布局文件中对应组件id的int型数组。其作用是自动的将String型数组所表示的每一列数据映射到布局文件对应id的组件上。上面的代码,将NAME列的数据一次映射到布局文件的id为text1的组件上。

注意:需要在AndroidManifest.xml中如权限:<uses-permission android:name="android.permission.READ_CONTACTS"></uses-permission>

运行后效果如下图:



SimpleAdapter

simpleAdapter的扩展性最好,可以定义各种各样的布局出来,可以放上ImageView(图片),还可以放上Button(按钮),CheckBox(复选框)等等。下面的代码都直接继承了ListActivity,ListActivity和普通的Activity没有太大的差别,不同就是对显示ListView做了许多优化,方面显示而已。

下面的程序是实现一个带有图片的类表。

首先需要定义好一个用来显示每一个列内容的xml

vlist.xml

[c-sharp] view
plaincopyprint?

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="horizontal" android:layout_width="fill_parent"

android:layout_height="fill_parent">

<ImageView android:id="@+id/img"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_margin="5px"/>

<LinearLayout android:orientation="vertical"

android:layout_width="wrap_content"

android:layout_height="wrap_content">

<TextView android:id="@+id/title"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:textColor="#FFFFFFFF"

android:textSize="22px" />

<TextView android:id="@+id/info"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:textColor="#FFFFFFFF"

android:textSize="13px" />

</LinearLayout>

</LinearLayout>

[c-sharp] view
plaincopyprint?

public class MyListView3 extends ListActivity {

// private List<String> data = new ArrayList<String>();

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

SimpleAdapter adapter = new SimpleAdapter(this,getData(),R.layout.vlist,

new String[]{"title","info","img"},

new int[]{R.id.title,R.id.info,R.id.img});

setListAdapter(adapter);

}

private List<Map<String, Object>> getData() {

List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();

Map<String, Object> map = new HashMap<String, Object>();

map.put("title", "G1");

map.put("info", "google 1");

map.put("img", R.drawable.i1);

list.add(map);

map = new HashMap<String, Object>();

map.put("title", "G2");

map.put("info", "google 2");

map.put("img", R.drawable.i2);

list.add(map);

map = new HashMap<String, Object>();

map.put("title", "G3");

map.put("info", "google 3");

map.put("img", R.drawable.i3);

list.add(map);

return list;

}

}

使用simpleAdapter的数据用一般都是HashMap构成的List,list的每一节对应ListView的每一行。HashMap的每个键值数据映射到布局文件中对应id的组件上。因为系统没有对应的布局文件可用,我们可以自己定义一个布局vlist.xml。下面做适配,new一个SimpleAdapter参数一次是:this,布局文件(vlist.xml),HashMap的 title 和 info,img。布局文件的组件id,title,info,img。布局文件的各组件分别映射到HashMap的各元素上,完成适配。

运行效果如下图:



有按钮的ListView

但是有时候,列表不光会用来做显示用,我们同样可以在在上面添加按钮。添加按钮首先要写一个有按钮的xml文件,然后自然会想到用上面的方法定义一个适配器,然后将数据映射到布局文件上。但是事实并非这样,因为按钮是无法映射的,即使你成功的用布局文件显示出了按钮也无法添加按钮的响应,这时就要研究一下ListView是如何现实的了,而且必须要重写一个类继承BaseAdapter。下面的示例将显示一个按钮和一个图片,两行字如果单击按钮将删除此按钮的所在行。并告诉你ListView究竟是如何工作的。效果如下:



[c-sharp] view
plaincopyprint?

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="horizontal"

android:layout_width="fill_parent"

android:layout_height="fill_parent">

<ImageView android:id="@+id/img"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_margin="5px"/>

<LinearLayout android:orientation="vertical"

android:layout_width="wrap_content"

android:layout_height="wrap_content">

<TextView android:id="@+id/title"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:textColor="#FFFFFFFF"

android:textSize="22px" />

<TextView android:id="@+id/info"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:textColor="#FFFFFFFF"

android:textSize="13px" />

</LinearLayout>

<Button android:id="@+id/view_btn"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="@string/s_view_btn"

android:layout_gravity="bottom|right" />

</LinearLayout>

[c-sharp] view
plaincopyprint?

public class MyListView4 extends ListActivity {

private List<Map<String, Object>> mData;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

mData = getData();

MyAdapter adapter = new MyAdapter(this);

setListAdapter(adapter);

}

private List<Map<String, Object>> getData() {

List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();

Map<String, Object> map = new HashMap<String, Object>();

map.put("title", "G1");

map.put("info", "google 1");

map.put("img", R.drawable.i1);

list.add(map);

map = new HashMap<String, Object>();

map.put("title", "G2");

map.put("info", "google 2");

map.put("img", R.drawable.i2);

list.add(map);

map = new HashMap<String, Object>();

map.put("title", "G3");

map.put("info", "google 3");

map.put("img", R.drawable.i3);

list.add(map);

return list;

}

// ListView 中某项被选中后的逻辑

@Override

protected void onListItemClick(ListView l, View v, int position, long id) {

Log.v("MyListView4-click", (String)mData.get(position).get("title"));

}

/**

* listview中点击按键弹出对话框

*/

public void showInfo(){

new AlertDialog.Builder(this)

.setTitle("我的listview")

.setMessage("介绍...")

.setPositiveButton("确定", new DialogInterface.OnClickListener() {

@Override

public void onClick(DialogInterface dialog, int which) {

}

})

.show();

}

public final class ViewHolder{

public ImageView img;

public TextView title;

public TextView info;

public Button viewBtn;

}

public class MyAdapter extends BaseAdapter{

private LayoutInflater mInflater;

public MyAdapter(Context context){

this.mInflater = LayoutInflater.from(context);

}

@Override

public int getCount() {

// TODO Auto-generated method stub

return mData.size();

}

@Override

public Object getItem(int arg0) {

// TODO Auto-generated method stub

return null;

}

@Override

public long getItemId(int arg0) {

// TODO Auto-generated method stub

return 0;

}

@Override

public View getView(int position, View convertView, ViewGroup parent) {

ViewHolder holder = null;

if (convertView == null) {

holder=new ViewHolder();

convertView = mInflater.inflate(R.layout.vlist2, null);

holder.img = (ImageView)convertView.findViewById(R.id.img);

holder.title = (TextView)convertView.findViewById(R.id.title);

holder.info = (TextView)convertView.findViewById(R.id.info);

holder.viewBtn = (Button)convertView.findViewById(R.id.view_btn);

convertView.setTag(holder);

}else {

holder = (ViewHolder)convertView.getTag();

}

holder.img.setBackgroundResource((Integer)mData.get(position).get("img"));

holder.title.setText((String)mData.get(position).get("title"));

holder.info.setText((String)mData.get(position).get("info"));

holder.viewBtn.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

showInfo();

}

});

return convertView;

}

}

}

  下面将对上述代码,做详细的解释,listView在开始绘制的时候,系统首先调用getCount()函数,根据他的返回值得到listView的长度(这也是为什么在开始的第一张图特别的标出列表长度),然后根据这个长度,调用getView()逐一绘制每一行。如果你的getCount()返回值是0的话,列表将不显示同样return 1,就只显示一行。

  系统显示列表时,首先实例化一个适配器(这里将实例化自定义的适配器)。当手动完成适配时,必须手动映射数据,这需要重写getView()方法。系统在绘制列表的每一行的时候将调用此方法。getView()有三个参数,position表示将显示的是第几行,covertView是从布局文件中inflate来的布局。我们用LayoutInflater的方法将定义好的vlist2.xml文件提取成View实例用来显示。然后将xml文件中的各个组件实例化(简单的findViewById()方法)。这样便可以将数据对应到各个组件上了。但是按钮为了响应点击事件,需要为它添加点击监听器,这样就能捕获点击事件。至此一个自定义的listView就完成了,现在让我们回过头从新审视这个过程。系统要绘制ListView了,他首先获得要绘制的这个列表的长度,然后开始绘制第一行,怎么绘制呢?调用getView()函数。在这个函数里面首先获得一个View(实际上是一个ViewGroup),然后再实例并设置各个组件,显示之。好了,绘制完这一行了。那 再绘制下一行,直到绘完为止。在实际的运行过程中会发现listView的每一行没有焦点了,这是因为Button抢夺了listView的焦点,只要布局文件中将Button设置为没有焦点就OK了。

运行效果如下图:



原文链接http://blog.csdn.net/comkingfly/article/details/6578907
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: