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获取对象,大大提高了效率!