Android之listview && adapter
今天我们讲的也是非常重要的一个控件listview—最常用也是最难的
一个ListView通常有两个职责。
(1)将数据填充到布局。
(2)处理用户的选择点击等操作。
第一点很好理解,ListView就是实现这个功能的。第二点也不难做到,在后面的学习中读者会发现,这非常简单。
一个ListView的创建需要3个元素。
(1)ListView中的每一列的View。
(2)填入View的数据或者图片等。
(3)连接数据与ListView的适配器。
也就是说,要使用ListView,首先要了解什么是适配器。适配器是一个连接数据和AdapterView(ListView就是一个典型的AdapterView,后面还会学习其他的)的桥梁,通过它能有效地实现数据与AdapterView的分离设置,使AdapterView与数据的绑定更加简便,修改更加方便
Android中提供了很多的Adapter,表4-5列出了常用的几个。
表4-5 常用适配器
Adapter |
含义 |
ArrayAdapter<T> |
用来绑定一个数组,支持泛型操作 |
SimpleAdapter |
用来绑定在xml中定义的控件对应的数据 |
SimpleCursorAdapter |
用来绑定游标得到的数据 |
BaseAdapter |
通用的基础适配器 |
其实适配器还有很多,要注意的是,各种Adapter只不过是转换的方式和能力不一样而已。
ListView使用SimpleAdapter
很多时候需要在列表中展示一些除了文字以外的东西,比如图片等。这时候可以使用SimpleAdapter。SimpleAdapter的使用也非常简单,同时它的功能也非常强大。可以通过它自定义ListView中的item的内容,比如图片、多选框等。
使用simpleAdapter的数据一般都是用HashMap构成的列表,列表的每一节对应ListView的每一行。通过SimpleAdapter的构造函数,将HashMap的每个键的数据映射到布局文件中对应控件上。这个布局文件一般根据自己的需要来自己定义。梳理一下使用SimpleAdapter的步骤。
(1)根据需要定义ListView每行所实现的布局。
(2)定义一个HashMap构成的列表,将数据以键值对的方式存放在里面。
(3)构造SimpleAdapter对象。
(4)将LsitView绑定到SimpleAdapter上。
BaseAdapter
在ListView的使用中,有时候还需要在里面加入按钮等控件,实现单独的操作。也就是说,这个ListView不再只是展示数据,也不仅仅是这一行要来处理用户的操作,而是里面的控件要获得用户的焦点。读者可以试试用SimpleAdapter添加一个按钮到ListView的条目中,会发现可以添加,但是却无法获得焦点,点击操作被ListView的Item所覆盖。这时候最方便的方法就是使用灵活的适配器BaseAdapter了。
当系统开始绘制ListView的时候,首先调用getCount()方法。得到它的返回值,即ListView的长度。然后系统调用getView()方法,根据这个长度逐一绘制ListView的每一行。也就是说,如果让getCount()返回1,那么只显示一行。而getItem()和getItemId()则在需要处理和取得Adapter中的数据时调用。那么getView如何使用呢?如果有10000行数据,就绘制10000次?这肯定会极大的消耗资源,导致ListView滑动非常的慢,那应该怎么做呢?
下面就通过微信的例子说明吧!
下面是布局文件
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" > 6 7 <ListView 8 android:id="@+id/listView1" 9 android:layout_width="match_parent" 10 android:layout_height="match_parent" > 11 </ListView> 12 13 </LinearLayout>
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="wrap_content" 5 android:paddingLeft="@dimen/activity_horizontal_margin" 6 android:paddingRight="@dimen/activity_horizontal_margin" 7 android:paddingTop="@dimen/activity_horizontal_margin" 8 android:paddingBottom="@dimen/activity_horizontal_margin" 9 android:orientation="horizontal" > 10 11 <ImageView 12 android:id="@+id/face" 13 android:layout_width="60dp" 14 android:layout_height="60dp" 15 android:src="@drawable/houzi" /> 16 17 18 <LinearLayout 19 android:layout_weight="1" 20 android:layout_width="wrap_content" 21 android:layout_height="wrap_content" 22 android:layout_margin="5dp" 23 android:orientation="vertical" > 24 25 <LinearLayout 26 android:layout_weight="1" 27 android:layout_width="match_parent" 28 android:layout_height="wrap_content" 29 android:layout_marginBottom="3dp" 30 android:orientation="horizontal" > 31 32 <TextView 33 android:layout_weight="1" 34 android:id="@+id/name" 35 android:layout_width="wrap_content" 36 android:layout_height="wrap_content" 37 android:textSize="20sp" 38 android:textColor="#7F7F7F"/> 39 40 <TextView 41 android:id="@+id/date" 42 android:layout_width="wrap_content" 43 android:layout_height="wrap_content" 44 android:textSize="15sp" 45 android:textColor="#A9A9A9"/> 46 </LinearLayout> 47 <LinearLayout 48 android:layout_weight="1" 49 android:layout_width="match_parent" 50 android:layout_height="wrap_content" 51 android:layout_marginBottom="3dp" 52 android:orientation="horizontal" > 53 <TextView 54 android:layout_weight="1" 55 android:id="@+id/content" 56 android:layout_width="wrap_content" 57 android:layout_height="wrap_content" 58 android:textSize="15sp" 59 android:textColor="#A9A9A9"/> 60 <TextView 61 android:id="@+id/noRead" 62 android:layout_width="wrap_content" 63 android:layout_height="wrap_content" 64 android:textSize="15sp" 65 android:text="" 66 android:textColor="#FF0000"/> 67 </LinearLayout> 68 </LinearLayout> 69 70 71 72 </LinearLayout>
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" 6 tools:context="com.main.weixin.MainActivity" > 7 8 <!-- 头部 --> 9 <LinearLayout 10 android:id="@+id/linearLayout1" 11 android:layout_width="match_parent" 12 android:layout_height="wrap_content" 13 > 14 15 <include layout="@layout/head"/> 16 17 </LinearLayout> 18 19 20 <!-- 中间 --> 21 <LinearLayout 22 android:layout_weight="1" 23 android:layout_width="match_parent" 24 android:layout_height="match_parent"> 25 26 <include layout = "@layout/listview_layout"></include> 27 28 </LinearLayout> 29 30 31 32 <!-- 尾部 --> 33 <LinearLayout 34 android:layout_width="match_parent" 35 android:layout_height="wrap_content" 36 > 37 38 <include layout="@layout/bottom"/> 39 40 </LinearLayout> 41 </LinearLayout>
下面是activity主要文件
1 package com.weixin.entity; 2 3 public class Messages { 4 5 private String face; 6 private String name; 7 private String Date; 8 private String content; 9 private Integer noRead; 10 11 public String getFace() { 12 return face; 13 } 14 public void setFace(String face) { 15 this.face = face; 16 } 17 public String getName() { 18 return name; 19 } 20 public void setName(String name) { 21 this.name = name; 22 } 23 public String getDate() { 24 return Date; 25 } 26 public void setDate(String date) { 27 Date = date; 28 } 29 public String getContent() { 30 return content; 31 } 32 public void setContent(String content) { 33 this.content = content; 34 } 35 public Integer getNoRead() { 36 return noRead; 37 } 38 public void setNoRead(Integer noRead) { 39 this.noRead = noRead; 40 } 41 42 43 44 }
1 package com.main.weixin; 2 3 import java.util.ArrayList; 4 import java.util.HashMap; 5 import java.util.List; 6 import java.util.Map; 7 8 import com.database.weixin.MyDatabaseOpenHelper; 9 import com.weixin.entity.Messages; 10 11 import android.app.Activity; 12 import android.os.Bundle; 13 import android.util.Log; 14 import android.view.LayoutInflater; 15 import android.view.Menu; 16 import android.view.MenuItem; 17 import android.view.View; 18 import android.view.ViewGroup; 19 import android.view.Window; 20 import android.widget.AdapterView; 21 import android.widget.AdapterView.OnItemClickListener; 22 import android.widget.BaseAdapter; 23 import android.widget.ListView; 24 import android.widget.SimpleAdapter; 25 import android.widget.TextView; 26 27 28 public class MainActivity extends Activity { 29 30 private SimpleAdapter sa; 31 private BaseAdapter ba; 32 private ListView lv; 33 private List<Messages> listMessage = new ArrayList<Messages>(); 34 private List<Map<String,Object>> listMessage2 = new ArrayList<Map<String,Object>>(); 35 36 @Override 37 protected void onCreate(Bundle savedInstanceState) { 38 super.onCreate(savedInstanceState); 39 requestWindowFeature(Window.FEATURE_NO_TITLE); 40 setContentView(R.layout.activity_main); 41 42 43 //模拟读取数据库或者互联网 44 for(int i=0 ; i<100 ; i++){ 45 Messages message = new Messages(); 46 message.setFace("houzi"); 47 message.setName("孙行者"+i); 48 message.setDate("昨天"); 49 message.setContent("有空吗?今晚一起吃饭"); 50 message.setNoRead(i); 51 listMessage.add(message);//方法一 52 53 54 /* //定义一个界面与数据混合体,一个Item代表一行记录 55 Map<String,Object> item = new HashMap<String,Object>(); 56 //一行多个空件 57 item.put("face", R.drawable.houzi); 58 item.put("name", "孙行者"+i); 59 item.put("date", "昨天"); 60 item.put("content", "有空吗?今晚一起吃饭"); 61 listMessage2.add(item);*/ 62 63 } 64 65 lv = (ListView) findViewById(R.id.listView1); 66 67 /* sa = new SimpleAdapter(this, 68 listMessage2, //data 不仅是数据,而是数据与界面高耦合的混合体 69 R.layout.middle1, 70 new String[]{"face","name","date","content"}, 71 new int[]{R.id.face,R.id.name,R.id.date,R.id.content}); 72 73 lv.setAdapter(sa); 74 75 //添加事件 76 lv.setOnItemClickListener(new OnItemClickListener(){ 77 78 @Override 79 public void onItemClick(AdapterView<?> parent, View view, 80 int position, long id) { 81 82 Map<String,Object> item =listMessage2.get(position); 83 item.put("name", "行者孙"+position); 84 sa.notifyDataSetChanged(); 85 } 86 87 }); */ 88 89 90 //简单理解为VC绑在一起 91 ba = new BaseAdapter(){ 92 93 //返回记录数目 94 @Override 95 public int getCount() { 96 97 return listMessage.size(); 98 99 } 100 101 //每一个item项,返回一次界面 102 @Override 103 public View getView(int position, View convertView, ViewGroup parent) { 104 View view ; 105 106 //布局不变,数据变 107 108 109 //如果缓存为空,我们生成新的布局作为1个item 110 if(convertView == null){ 111 Log.i("info:", "没有缓存,重新生成"+position); 112 LayoutInflater inflater = MainActivity.this.getLayoutInflater(); 113 114 115 //因为getView()返回的对象,adapter会自动赋给ListView 116 view = inflater.inflate(R.layout.middle2, null); 117 118 }else{ 119 Log.i("info:", "有缓存,不需要重新生成"+position); 120 view = convertView; 121 122 } 123 124 Messages m = listMessage.get(position); 125 Integer noRead = m.getNoRead(); 126 127 TextView tv_name = (TextView) view.findViewById(R.id.name); 128 tv_name.setText(m.getName()); 129 130 TextView tv_date = (TextView) view.findViewById(R.id.date); 131 tv_date.setText(m.getDate()); 132 133 TextView tv_content = (TextView) view.findViewById(R.id.content); 134 tv_content.setText(m.getContent()); 135 136 System.out.println(noRead); 137 138 if( noRead !=0){ 139 TextView tv_noRead = (TextView) view.findViewById(R.id.noRead); 140 //这里的setText()方法接收的是一个字符串类型,而你给了一个Integer类型给它,所以报错了,这里需要把Integer类型转为String类型即可 141 tv_noRead.setText(noRead.toString()); 142 143 } 144 145 return view ; 146 147 } 148 149 @Override 150 public Object getItem(int position) { 151 return null; 152 } 153 154 @Override 155 public long getItemId(int position) { 156 return 0; 157 } 158 159 160 }; 161 lv.setAdapter(ba); 162 163 //添加事件 164 lv.setOnItemClickListener(new OnItemClickListener(){ 165 166 @Override 167 public void onItemClick(AdapterView<?> parent, View view, 168 int position, long id) { 169 Messages message = listMessage.get(position); 170 if(message.getNoRead()!=0){ 171 172 message.setName("行者孙"+position); 173 message.setNoRead(0); 174 175 view.findViewById(R.id.noRead).setVisibility(View.INVISIBLE); 176 177 }else{ 178 message.setName("孙行者"+position); 179 } 180 181 ba.notifyDataSetChanged();// 182 } 183 184 }); 185 } 186 187 188 @Override 189 public boolean onCreateOptionsMenu(Menu menu) { 190 getMenuInflater().inflate(R.menu.main, menu); 191 return true; 192 } 193 194 @Override 195 public boolean onOptionsItemSelected(MenuItem item) { 196 int id = item.getItemId(); 197 if (id == R.id.action_settings) { 198 return true; 199 } 200 return super.onOptionsItemSelected(item); 201 } 202 }
效果如下:
convertView相当于一个缓存,开始为0,当有条目变为不可见,它缓存了它的数据,后面再出来的条目只需要更新数据就可以了,这样大大节省了系统资料的开销。