ListView中最重要的就是adapter,他是listview和待显示数据之间的桥梁,他是用来对相应的UI填充数据的,其实很多UI空间都需要adapter,常见的listview,gallery等等。引用网上随处的可见的一个图来表示一下
这就是适配器的作用,常用的adapter有简单的arraydapter,有一点扩展性的simpleadapter,带游标的simplecursoradapter,和扩展性灵活性很强的basedapter。下面一一解说:
(1)ArrayAdapter:最简单的adpter
实现这个adapter只要一行代码,一个字符串数组即可。其实它也可以支持图片,但是你要重写getView方法来返回你要的view,不过我感觉这样还不如直接去用simpleadapter了。
1 public class ListViewTestActivity extends Activity {
2 public ListView listview;
3 public String[] str = {"Java", "C", "c++", "PHP", "C#"};
4 /** Called when the activity is first created. */
5 @Override
6 public void onCreate(Bundle savedInstanceState) {
7 super.onCreate(savedInstanceState);
8 setContentView(R.layout.main);
9 listview= (ListView)findViewById(R.id.list);
10 ArrayAdapter<String> aa = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, str);
11 listview.setAdapter(aa);
12 listview.setOnItemClickListener(new OnItemClickListener() {
13 @Override
14 public void onItemClick(AdapterView<?> parent, View view, int position, long id ) {
15 // TODO Auto-generated method stub
16 Toast.makeText(ListViewTestActivity.this, (String)listview.getItemAtPosition(position), Toast.LENGTH_SHORT).show();
17 }
18 });
19 }
20 }
(2)SimpleAdapter:使用自定义的layout来填充list的每一行,通过一个map<string.object>将对应的组件和资源相对应。
1 public class ListViewActivity extends Activity {
2 public ListView listview;
3 /** Called when the activity is first created. */
4 @Override
5 public void onCreate(Bundle savedInstanceState) {
6 super.onCreate(savedInstanceState);
7 setContentView(R.layout.main);
8 listview= (ListView)findViewById(R.id.list);
9 ArrayList<HashMap<String,Object>> data = new ArrayList<HashMap<String,Object>>();
10 HashMap<String,Object> hm = null;
11 for(int i=0;i<5;i++) {
12 hm = new HashMap<String,Object>();
13 hm.put("data", "text1------"+i);
14 hm.put("image", R.drawable.ic_launcher);
15 data.add(hm);
16 }
17 SimpleAdapter sa = new SimpleAdapter(this, data,R.layout.list_layout, new String[]{"data", "image"}, new int[]{R.id.list_text1, R.id.list_image});
18 listview.setAdapter(sa);
19 listview.setOnItemClickListener(new OnItemClickListener() {
20 @Override
21 public void onItemClick(AdapterView<?> parent, View view, int position, long id ) {
22 // TODO Auto-generated method stub
23 Toast.makeText(ListViewTestActivity.this, (String)((HashMap<String, Object>)listview.getItemAtPosition(position)).get("data"), Toast.LENGTH_SHORT).show();
24 }
25 });
26 }
27 }
1 list_layout.xml
2 <?xml version="1.0" encoding="utf-8"?>
3 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
4 android:layout_width="match_parent"
5 android:layout_height="match_parent"
6 android:orientation="horizontal" >
7 <ImageView
8 android:id="@+id/list_image"
9 android:layout_width="wrap_content"
10 android:layout_height="wrap_content" />
11 <TextView
12 android:id="@+id/list_text1"
13 android:textSize="13pt"
14 android:layout_width="fill_parent"
15 android:layout_height="wrap_content" />
16 </LinearLayout>
(3)接着说下SimpleCursorAdapter:通过一个游标来控制数据的获取。这里哪一个获取联系人的例子来展示一下!
1 public class CursorAdapterTestActivity extends Activity {
2 public ListView myList;
3 /** Called when the activity is first created. */
4 @SuppressWarnings("deprecation")
5 @Override
6 public void onCreate(Bundle savedInstanceState) {
7 super.onCreate(savedInstanceState);
8 setContentView(R.layout.main);
9 myList = (ListView) findViewById(R.id.list);
10 Cursor cursor = getContentResolver().query(People.CONTENT_URI, null, null, null, null);
11 startManagingCursor(cursor);
12 ListAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1,
13 cursor, new String[] { People.NAME }, new int[] { android.R.id.text1 });
14 myList.setAdapter(adapter);
15 }
16 }
千万别忘了在manifest里加上对联系人的读取权限!
(4)下面来说功能最强的adapter---baseadapter,只有使用baseadapter才能做到listview的优化。
有时候,列表不光会用来做显示用,我们同样可以在在上面添加按钮。添加按钮首先要写一个有按钮的xml文件,然后自然会想到用上面的方法定义一个适配器,然后将数据映射到布局文件上。但是事实并非这样,因为按钮是无法映射的,即使你成功的用布局文件显示出了按钮也无法添加按钮的响应,这时必须要重写一个类继承BaseAdapter。
当系统开始绘制ListView的时候,首先调用getCount()方法。得到它的返回值,即ListView的长度。然后系统调用getView()方法,根据这个长度逐一绘制ListView的每一行。也就是说,如果让getCount()返回1,那么只显示一行。而getItem()和getItemId()则在需要处理和取得Adapter中的数据时调用。那么getView如何使用呢?如果有10000行数据,就绘制10000次?这肯定会极大的消耗资源,导致ListView滑动非常的慢,那应该怎么做呢?通过一个例子来讲解如何在使用BaseAdapter的时候优化ListView的显示。例子中将上一节中的ImageView换成Button,并且处理Button的点击事件,其中对ListView的显示做了优化。
1 public class ArrayListDemoActivity extends Activity {
2 // 定义一个String数组用来显示ListView的内容
3 private ListView lv;
4 ArrayList<HashMap<String, Object>> listItem;
5 /** Called when the activity is first created. */
6 @Override
7 public void onCreate(Bundle savedInstanceState) {
8 super.onCreate(savedInstanceState);
9 setContentView(R.layout.main);
10 listItem = getListDate();
11 lv = (ListView) findViewById(R.id.mylist);
12 MyAdapter mAdapter = new MyAdapter(this);// 得到一个MyAdapter对象
13 lv.setAdapter(mAdapter);// 为ListView绑定Adapter
14 /* 为ListView添加点击事件 */
15 lv.setOnItemClickListener(new OnItemClickListener() {
16 @Override
17 public void onItemClick(AdapterView<?> arg0, View view, int position, long id) {
18 Toast.makeText(ArrayListDemoActivity.this, "你点击了ListView条目" + position, Toast.LENGTH_SHORT).show();
19 }
20 });
21 }
22 /* 添加一个得到数据的方法,方便使用 */
23 private ArrayList<HashMap<String, Object>> getListDate() {
24 ArrayList<HashMap<String, Object>> listItem = new ArrayList<HashMap<String, Object>>();
25 /* 为动态数组添加数据 */
26 for (int i = 0; i < 50; i++) {
27 HashMap<String, Object> map = new HashMap<String, Object>();
28 map.put("ItemTitle", "第" + i + "行");
29 map.put("ItemText", "这是第" + i + "行");
30 listItem.add(map);
31 }
32 return listItem;
33 }
34 /**
35 * @author vtianyun
36 * @date 2012-4-4
37 * 自定义Adapter的实现
38 */
39 private class MyAdapter extends BaseAdapter {
40 private LayoutInflater mInflater;// 得到一个LayoutInfalter对象用来导入布局
41 /* 构造方法*/
42 public MyAdapter(Context context) {
43 this.mInflater = LayoutInflater.from(context);
44 }
45 @Override
46 public int getCount() {
47 return listItem.size();// 返回数组的长度
48 }
49 @Override
50 public Object getItem(int position) {
51 return null;
52 }
53 @Override
54 public long getItemId(int position) {
55 return 0;
56 }
57 @Override
58 public View getView(final int position, View convertView, ViewGroup parent) {
59 ViewHolder holder;
60 // 观察convertView随ListView滚动情况
61 System.out.println("getView = " + position + ", convertView = " + convertView);
62 if (convertView == null) {
63 convertView = mInflater.inflate(R.layout.list, null);
64 holder = new ViewHolder(); // 得到各个控件的对象
65 holder.title = (TextView) convertView.findViewById(R.id.ItemTitle);
66 holder.text = (TextView) convertView.findViewById(R.id.ItemText);
67 holder.bt = (Button) convertView.findViewById(R.id.listBtn);
68 convertView.setTag(holder);// 绑定ViewHolder对象
69 } else {
70 holder = (ViewHolder) convertView.getTag();// 取出ViewHolder对象
71 }
72 // 设置TextView显示的内容,即我们存放在动态数组中的数据
73 holder.title.setText(listItem.get(position).get("ItemTitle").toString());
74 holder.text.setText(listItem.get(position).get("ItemText").toString());
75 // 为Button添加点击事件
76 holder.bt.setOnClickListener(new OnClickListener() {
77 @Override
78 public void onClick(View v) {
79 Toast.makeText(ArrayListDemoActivity.this, "你点击了按钮" + position, Toast.LENGTH_SHORT).show(); // 打印Button的点击信息
80 }
81 });
82 return convertView;
83 }
84 }
85 /**
86 * @author vtianyun
87 * @date 2012-4-4
88 * 存放控件,当下次再调用时,不用再去findViewById了,这个比较费时
89 */
90 public final class ViewHolder {
91 public TextView title;
92 public TextView text;
93 public Button bt;
94 }
95 }
这里用一个ViewHolder对象来持有这个Layout里的三个组件的对象,这样在滚动listview的时候,就不用再去find了,因为这个操作是很费时的,这样做后,流畅度大大提高,特别是在比较复杂的list里。
还需要注意的是,Button会抢夺ListView的焦点,需要将Button设置为没有焦点。设置非常简单,只需要在xml的Button标签下加入一行:android:focusable=“false”代码就可以了。
这里还用到了一个View.setTag()知识,这表示给view额外的添加一个数据,和原来的view捆绑在一起,等到用的时候可以直接getTag拿出来,网上有个例子教程说的就是给很多button绑上一个整型的tag标记,然后在onClickListener里就可以通过这个tag标记来判断该响应哪个按钮的操作了!
通过查看logcat可以发现,内存被重复使用了,这样既减小了内存开销,在重用的同时,通过getTag获取对象,大大提高了效率!