这一篇整理一些ListView的基本知识。
PartA翻译自:
(A)API Guide
使用Adapter建立(bind)Layout
当layout内容是动态的或者不是预先决定好的,可以使用AdapterView的子类动态完成layout的填充。
AdapterView的子类使用Adapter来bind data到它的layout里面。Adapter的角色是数据源和AdapterView layout的中间人——Adapter从数组、数据库等处取出数据然后把每个条目转换成可以加进AdapterView layout的一个view。
用数据填充Adapter
你可以通过给Adapter建立AdapterView的形式来填充ListView、GridView等AdatperView。这个操作会把外部数据建立成一个view,每一个view代表一个数据条目。
Android提供很多Adapter的子类来给AdapterView获取数据、建立view。两种最常见的adapter是:
ArrayAdapter
当你的数据源是array(数组)的时候使用这个adapter。默认地,ArrayAdapter通过对每个list item调用toString()并且把内容放进TextView中来为每个array item建立一个view。
例如,当你有一个字符串数组想要展示在ListView里面,用构造函数来初始化一个新的ArrayAdapter并且为每个string和string array指定一个latyout。
ArrayAdapteradapter = new ArrayAdapter (this, android.R.layout.simple_list_item_1, myStringArray);
构造函数的参数是:
- App Context
- 一个包含了为数组中的每个string准备了TextView的layout
- 字符串数组
然后只要在你的ListView中调用setAdapter():
ListView listView = (ListView) findViewById(R.id.listview);listView.setAdapter(adapter);
为了自定义每个item的外观,你可以为你的数组对象复写(override)的toString()方法。或者建立一个不是TextView的view比如ImageView,继承ArrayAdapter类并且复写getView()来反悔你想为每个item准备的view类型。
当你的数据来自的时候使用这个adapter。使用,你必须为Cursor中的每一行指定一个layout。例如,如果你想建立一个包含了人名和电话号码的表,你可以使用一个返回Cursor的查询(query),Corsor中一行对应一个人,列则对应人名和电话号码。然后你建立一个字符串数组来指定Cursor中的哪些列你想要放置到result的layout里面,建立一个integer数组来指定列应该被放置到的对应的view:
String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER};int[] toViews = {R.id.display_name, R.id.phone_number};
When you instantiate the , pass the layout to use for each result, the
containing the results, and these two arrays:
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.person_name_and_number, cursor, fromColumns, toViews, 0);ListView listView = getListView();listView.setAdapter(adapter);
-
The
.then creates a view for each row in the
using the provided layout by inserting each
fromColumns
item into the correspondingtoViews
view.
If, during the course of your application's life, you change the underlying data that is read by your adapter, you should call . This will notify the attached view that the data has been changed and it should refresh itself.
处理点击事件
你可以通过实现接口来处理点击AdapterView中每个条目的点击事件。比如:
// Create a message handling object as an anonymous class. private OnItemClickListener mMessageClickedHandler = new OnItemClickListener() { public void onItemClick(AdapterView parent, View v, int position, long id) { // Do something in response to the click } }; listView.setOnItemClickListener(mMessageClickedHandler);
(B)
1)ArrayAdapter
listview在findViewById获取布局之后,可以用setAdapter()方法添加元素,它的参数是ListAdapter,子类有很多,比如ArrayAdapter<T> 、CursorAdapter、BaseAdapter等等。
*三种Adapter的
1. String[]: ArrayAdapter 2. List<Map<String,?>>: SimpleAdapter 3. 数据库Cursor: SimpleCursorAdapter
ArrayAdapteradapter;//生成对象 adapter = new ArrayAdapter (this,R.layout.listcell);//这里的第二个参数中的列表必须是TextView array.add("Hello");//取决于前面的泛型 array.add("there");
然后介绍了自定义一个类作为泛型里的类型,ListCellData,里面有get set方法,然后在adapter里像下面这样初始化:
adapter.add(new ListCellData("小明","男","17"));
之后发现返回的是乱码,说是因为这里返回的是函数的toString()方法的返回值(构造函数没有返回值,但是每个类的父类Object有一个toString()方法可以重写)。
于是:
@overridepublic String toString(){ return this.getUserName(); //}
然后介绍了lv.setOnItemClickListener(this)绑定监听器的方法。之前为什么要定义ListCellAdapter?这里体现出来:
可以返回自定义的类型,然后去get类中的变量。这样的话比如一条答案,可以把他们所有的属性封装成一个数据类型,list.add的时候add要显示的内容。
这里的position似乎很智能,似乎之前的add是一个队列,跟这里position顺序都是一一对应的。
2)用BaseAdapter自定义复杂列表
初始化一个BaseAdapter对象,它复写了getView(), getItemID(), getItem(), getCount()。
getView()只要在需要呈现列表项的时候都会被执行。
在getView()里需要创建一个视图来返回。这里返回一个textview。
getView()什么时候执行?如果在getView()里println,会发现每当显示新的item的时候,getView就会执行一次,创建一个textView。当有几百个list item的时候,这样很耗内存。
所以,要用getView的第二个参数,convertView。
convertView的意思是,当listview里的item滑出屏幕之后,view就会变成一个convertView。所以可以在convertView不为空的时候(有view滑出屏幕了),不创建新的view,而是先用convertView(这样是不是实现了只需要创建屏幕上显示的那么多个view就行了呢)。下图中注意new TextView中传的参数就是所在的类的名字。
图:convertView的示例 下面,实现一个能够呈现出图片的列表。于是新建一个customer_list_cell.xml列表项。里面有ImageView、TextView等等需要自己布局。 然后,数据要建立一个独立的类。一般要有set get方法。用构造函数给这个类里的变量传值。 接下来要实现getView()。同样重用convertView。给内部的子对象重新赋值,否则会发现后面的item跟以前会重复。 然后是在自定义的LL里面findViewById,从以前创建的LL内部查找。这一点要好好体会一下。 好了,功能完成了,但是真实开发过程不建议用BaseAdapter的匿名内部类实现。要把类之间的耦合度降低,以便写大工程。 创建一个类继承自BaseAdapter。 传Context进来。 接下来介绍了ListActivity。如果为ListActivity创建一个布局xml文件,那么这个布局文件内部必须有一个listview并且它的ID必须指定为:android:id="@android:id/list"。