安卓讲课笔记(9):列表视图
上次课我们通过“设置基本信息”案例,讲解了单选按钮与复选框的常用属性以及用法。单选按钮与复选框最关键的方法是
isChecked()
,根据该方法返回值确定项目是否被选中。还可以利用setChecked()方法来设置单选按钮或复选框的选择状态。
今天我们来学习ListView,它是AdapterView的子类,要通过适配器作为梁桥来绑定数据源。有四种适配器可以使用:数组适配器(ArrayAdapter)、简单适配器(SimpleAdapter)、简单游标适配器(SimpleCursorAdapter)、基适配器(BaseAdapter)。简单游标适配器要等到我们学习SQLite数据库时再说,另外三种适配器,本次课我们会一一讲述如何使用。
使用列表控件需要四样东西:
列表控件、适配器、数据源、列表项模板
ListView的继承关系图
案例1、阅读古诗(采用数组适配器)
一、运行效果
二、涉及知识点
1、列表视图(ListView)
2、数组适配器(ArrayAdapter)
数组适配器构造方法有三个参数:上下文环境、列表项模板文件资源索引、数据源(数组或数组列表)
adapter = new ArrayAdapter<String>(context, resource, objects)
三、实现步骤
1、项目结构图
2、布局资源文件
(1)诗歌列表布局文件 activity_poem_list.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp" >
<ListView
android:id="@+id/lv_poem_title"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#aaaaaa"
android:dividerHeight="1dp" >
</ListView>
</LinearLayout>
(2)诗歌内容布局文件 activity_poem_content.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/content_background"
android:gravity="center"
android:orientation="vertical" >
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:textColor="#ff00ff"
android:textSize="30sp" />
<TextView
android:id="@+id/tv_author"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:textColor="#000000"
android:textSize="18sp" />
<TextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:textColor="#0000ff"
android:textSize="20sp" />
<Button
android:id="@+id/btn_back"
android:layout_width="150dp"
android:layout_height="wrap_content"
android:onClick="doBack"
android:text="@string/back"
android:textSize="20sp" />
</LinearLayout>
(3)列表项模板文件poem_title_list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tv_poem_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:gravity="center_vertical"
android:minHeight="50dp"
android:textSize="18sp"
android:textColor="#0000ff"/>
3、字符串资源文件strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">阅读古诗</string>
<string name="back">返回目录</string>
<string name="action_settings">设置</string>
<string-array name="titles">
<item>静夜思</item>
<item>登鹳雀楼</item>
<item>相思</item>
<item>听弹琴</item>
<item>登乐游原</item>
<item>朝发白帝城</item>
<item>巴山夜雨</item>
<item>渭城曲</item>
<item>春夜喜雨</item>
<item>离离原上草</item>
<item>示儿</item>
</string-array>
<string-array name="authors">
<item>唐·李白</item>
<item>唐·王之涣</item>
<item>唐·王维</item>
<item>唐·刘长卿</item>
<item>唐·李商隐</item>
<item>唐·李白</item>
<item>唐·杜牧</item>
<item>唐·王维</item>
<item>唐·杜甫</item>
<item>唐·白居易</item>
<item>宋·陆游</item>
</string-array>
<string-array name="contents">
<item>床前明月光,\n疑是地上霜。\n举头望明月,\n低头思故乡。</item>
<item>白日依山尽,\n黄河入海流。\n欲穷千里目,\n更上一层楼。</item>
<item>红豆生南国,\n春来发几枝。\n愿君多采撷,\n此物最相思。</item>
<item>泠泠七弦上,\n静听松风寒。\n古调虽自爱,\n今人多不弹。</item>
<item>向晚意不适,\n驱车登古原。\n夕阳无限好,\n只是近黄昏。</item>
<item>朝辞白帝彩云间,\n千里江陵一日还。\n两岸猿声啼不住,\n轻舟已过万重山。</item>
<item>君问归期未有期,\n巴山夜雨涨秋池。\n何当共剪西窗烛,\n却话巴山夜雨时。</item>
<item>渭城朝雨浥轻尘,\n客舍青青柳色新。\n劝君更尽一杯酒,\n西出阳关无故人。</item>
<item>好雨知时节,当春乃发生。\n随风潜入夜,润物细无声。\n野径云俱黑,江船火独明。\n晓看红湿处,花重锦官城。</item>
<item>离离原上草,一岁一枯荣。\n野火烧不尽,春风吹又生。\n远芳侵古道,晴翠接荒城。\n又送王孙去,萋萋满别情。</item>
<item>死去元知万事空,\n但悲不见九州同。\n王师北定中原日,\n家祭无忘告乃翁。 </item>
</string-array>
</resources>
4、Java源代码
(1)诗歌列表窗口(PoemListActivity)
/**
* 功能:阅读古诗
* 诗歌标题窗口
* 作者:华卫
* 日期:2015年9月28日
*/
package net.hw.read_poems;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
public class PoemListActivity extends Activity {
/**
* 古诗总数
*/
private static final int POEM_COUNT = 11;
/**
* 显示古诗标题的列表控件
*/
private ListView lvPoemTitle;
/**
* 数组适配器
*/
private ArrayAdapter<String> adapter;
/**
* 古诗标题数组
*/
private String[] strPoemTitles;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_poem_list);
// 通过资源索引获得控件示例
lvPoemTitle = (ListView) findViewById(R.id.lv_poem_title);
// 初始化古诗标题数组,作为数据源
String[] titles = getResources().getStringArray(R.array.titles);
String[] authors = getResources().getStringArray(R.array.authors);
strPoemTitles = new String[POEM_COUNT];
for (int i = 0; i < strPoemTitles.length; i++) {
strPoemTitles[i] = (i + 1) + "、" + authors[i] + "·" + titles[i];
}
// 创建数组适配器,作为数据源与列表控件联系的桥梁
/**
* 参数1:上下文环境<br>
* 参数2:列表项模板<br>
* 参数3:数据源(数组或数组列表)<br>
*/
adapter = new ArrayAdapter<String>(this, R.layout.poem_title_list_item, strPoemTitles);
// 给列表控件设置适配器
lvPoemTitle.setAdapter(adapter);
// 给列表控件注册监听器
lvPoemTitle.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// 创建意图
Intent intent = new Intent();
intent.setClass(PoemListActivity.this, PoemContentActivity.class);
intent.putExtra("poem_index", position);
// 按意图启动活动
startActivity(intent);
}
});
}
}
(2)诗歌内容窗口(PoemContentActivity)
/**
* 功能:显示古诗内容
* 作者:华卫
* 日期:2015年9月30日
*/
package net.hw.read_poems;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class PoemContentActivity extends Activity {
/**
* 标题标签
*/
private TextView tvTitle;
/**
* 作者标签
*/
private TextView tvAuthor;
/**
* 内容标签
*/
private TextView tvContent;
/**
* 标题数组
*/
private String[] titles;
/**
* 作者数组
*/
private String[] authors;
/**
* 内容数组
*/
private String[] contents;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 利用布局文件设置用户界面
setContentView(R.layout.activity_poem_content);
// 通过资源索引获得控件示例
tvTitle = (TextView) findViewById(R.id.tv_title);
tvAuthor = (TextView) findViewById(R.id.tv_author);
tvContent = (TextView) findViewById(R.id.tv_content);
// 获取古诗标题数组
titles = getResources().getStringArray(R.array.titles);
// 获取古诗作者数组
authors = getResources().getStringArray(R.array.authors);
// 获取古诗内容数组
contents = getResources().getStringArray(R.array.contents);
// 获取古诗列表窗口选择的古诗索引
int poemIndex = getIntent().getIntExtra("poem_index", 0);
// 设置标题标签、作者标签与内容标签的文本属性
tvTitle.setText(titles[poemIndex]);
tvAuthor.setText(authors[poemIndex]);
tvContent.setText(contents[poemIndex]);
}
/**
* 返回按钮单击事件处理方法
*
* @param view
*/
public void doBack(View view) {
finish();
}
}
四、课后作业
做一个简易音乐播放器,如下图所示:
案例2、显示用户列表(采用简单适配器)
一、运行效果
二、涉及知识点
1、列表视图(ListView)
2、简单适配器(SimpleAdapter)
简单适配器构造方法有五个参数:上下文环境、数据源(数组列表)、列表项模板文件资源索引、要显示的数据项、显示数据项的控件
3、数组列表(ArrayList)
4、哈希映射(HashMap)
三、实现步骤
1、项目结构图
2、布局资源文件
(1)主布局文件 activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp"
android:background="@drawable/background">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:id="@+id/tv_icon"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="20dp"
android:layout_weight="0.5"
android:text="@string/icon"
android:textSize="20sp" />
<TextView
android:id="@+id/tv_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/name"
android:textSize="20sp" />
<TextView
android:id="@+id/tv_phone"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:text="@string/phone"
android:textSize="20sp" />
</LinearLayout>
<ImageView
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:background="#aaaaaa"/>
<ListView
android:id="@+id/lv_user"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>
</LinearLayout>
(2)列表项模板文件user_list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal" >
<ImageView
android:id="@+id/iv_icon"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="20dp"
android:layout_weight="0.5"
android:background="@drawable/img1" />
<TextView
android:id="@+id/tv_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textColor="#0000ff"
android:textSize="20sp" />
<TextView
android:id="@+id/tv_phone"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.5"
android:textColor="#ff00ff"
android:textSize="20sp" />
</LinearLayout>
3、字符串资源文件strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">显示用户列表</string>
<string name="icon">图标</string>
<string name="name">姓名</string>
<string name="phone">电话</string>
<string name="action_settings">设置</string>
</resources>
4、主界面类MainActivity
/**
* 功能:显示用户列表
* 利用简单适配器
* 作者:华卫
* 日期:2015年9月30日
*/
package net.hw.display_user_list;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import android.R.anim;
import android.app.Activity;
import android.os.Bundle;
import android.widget.Adapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;
public class MainActivity extends Activity {
/**
* 用户列表控件
*/
private ListView lvUser;
/**
* 数据源
*/
private List<HashMap<String, Object>> users;
/**
* 简单适配器
*/
private SimpleAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 利用布局文件设置用户界面
setContentView(R.layout.activity_main);
// 通过资源索引获得控件示例
lvUser = (ListView) findViewById(R.id.lv_user);
// 初始化数据源
users = getUsers();
// 创建简单适配器
/**
* 参数1:上下文环境;<br>
* 参数2:数组列表(元素是Map对象);<br>
* 参数3:列表项的布局文件;<br>
* 参数4:Map对象的键名列表;<br>
* 参数5:列表项模板的控件列表<br>
*/
adapter = new SimpleAdapter(this, users, R.layout.user_list_item,
new String[] { "icon", "name", "phone" },
new int[] { R.id.iv_icon, R.id.tv_name, R.id.tv_phone });
// 列表控件设置适配器
lvUser.setAdapter(adapter);
}
/**
* 获取用户列表
*
* @return 用户列表
*/
private List<HashMap<String, Object>> getUsers() {
ArrayList<HashMap<String, Object>> users = new ArrayList<HashMap<String, Object>>();
HashMap<String, Object> user = null;
user = new HashMap<String, Object>();
user.put("icon", R.drawable.img1);
user.put("name", "李晓云");
user.put("phone", "13945451129");
users.add(user);
user = new HashMap<String, Object>();
user.put("icon", R.drawable.img2);
user.put("name", "王华玲");
user.put("phone", "15878768343");
users.add(user);
user = new HashMap<String, Object>();
user.put("icon", R.drawable.img3);
user.put("name", "张三丰");
user.put("phone", "13920206785");
users.add(user);
user = new HashMap<String, Object>();
user.put("icon", R.drawable.img4);
user.put("name", "郭靖");
user.put("phone", "15890901125");
users.add(user);
user = new HashMap<String, Object>();
user.put("icon", R.drawable.img5);
user.put("name", "杨过");
user.put("phone", "13945456712");
users.add(user);
user = new HashMap<String, Object>();
user.put("icon", R.drawable.img6);
user.put("name", "郑智化");
user.put("phone", "15894971124");
users.add(user);
user = new HashMap<String, Object>();
user.put("icon", R.drawable.img7);
user.put("name", "林青霞");
user.put("phone", "13545347890");
users.add(user);
return users;
}
}
四、课后作业
在显示用户列表程序里,实现功能:单击某个用户,即拨打该用户电话。
案例3:显示联系人(采用基适配器)
一、运行效果
二、涉及知识点
1、列表控件(ListView)
2、基适配器(BaseAdapter)
三、实现步骤
1、项目结构图
2、布局资源文件
(1)主布局activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity"
android:background="@drawable/background">
<ListView
android:id="@+id/lv_contact"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" >
</ListView>
</RelativeLayout>
(2)联系人列表项模板contact_list_item.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="5dp" >
<TextView
android:id="@+id/tv_contact_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
android:textColor="#0000ff"
android:text="@string/name" />
<TextView
android:id="@+id/tv_contact_phone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:layout_marginLeft="25dp"
android:textColor="#555555"
android:text="@string/phone" />
</LinearLayout>
3、字符串资源文件strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">显示联系人(采用基适配器)</string>
<string name="action_settings">设置</string>
<string name="name">姓名</string>
<string name="phone">电话</string>
</resources>
4、Java源代码
(1)联系人实体类Contact
/**
* 功能:联系人实体类
* 日期:2015年7月28日
*/
package net.hw.listview;
public class Contact {
private String name;
private String phone;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
(2)联系人适配器ContactAdapter
/**
* 功能:继承基适配器,创建联系人适配器
* 日期:2015年7月28日
*/
package net.hw.listview;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
public class ContactAdapter extends BaseAdapter {
/**
* 联系人数组
*/
private Contact[] contacts;
/**
* 上下文环境
*/
private Context context;
private final String TAG = "ContactAdapter";
/**
* 构造方法,传递联系人数组
*
* @param contacts
*/
public ContactAdapter(Context context, Contact[] contacts) {
this.context = context;
this.contacts = contacts;
}
@Override
public int getCount() {
return contacts.length;
}
@Override
public Object getItem(int position) {
return contacts[position];
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Log.i(TAG, "getView()");
if (convertView == null) {
// 获得布局填充器,用于将layout加载为View对象
LayoutInflater inflater = LayoutInflater.from(context);
// 将布局文件加载为视图对象
convertView = inflater.inflate(R.layout.contact_list_item, null);
}
Log.i(TAG, "getView(), convertView -> " + convertView.hashCode());
// 从加载的对象里检索各个标签控件
TextView tvName = (TextView) convertView
.findViewById(R.id.tv_contact_name);
TextView tvPhone = (TextView) convertView
.findViewById(R.id.tv_contact_phone);
Log.d(TAG, "getView(), convertView.tvName -> " + tvName.hashCode());
// 获取要显示的联系人数据
Contact contact = contacts[position];
// 设置标签控件要显示的数据
tvName.setText(contact.getName());
tvPhone.setText(contact.getPhone());
// 返回通过模板加载的view对象
return convertView;
}
}
说明:联系人适配器可以进行优化。
/**
* 联系人适配器
*/
package net.hw.listview;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
public class ContactAdapter extends BaseAdapter {
/**
* 上下文环境
*/
private Context context;
/**
* 联系人数组
*/
private Contact[] contacts;
/**
* 构造方法:传入上下文环境与联系人数据
*
* @param context
* @param contacts
*/
public ContactAdapter(Context context, Contact[] contacts) {
this.context = context;
this.contacts = contacts;
}
/**
* 获取数据项的总数
*/
@Override
public int getCount() {
return contacts.length;
}
@Override
public Object getItem(int position) {
return contacts[position];
}
@Override
public long getItemId(int position) {
return position;
}
/**
* 获取显示数据项的视图 优化convertView,该对象是要重复使用的,如果存在,就不应重复声明。
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 声明视图容器
ViewHolder holder;
if (convertView == null) { // 转换视图不存在时
// 获取布局填充器,用于将布局文件转换成视图
LayoutInflater inflater = LayoutInflater.from(context);
// 将联系人项目布局文件转换成视图对象
convertView = inflater.inflate(R.layout.contact_list_item, null);
// 创建视图容器对象(包含两个控件)
holder = new ViewHolder();
holder.tvName = (TextView) convertView
.findViewById(R.id.tv_contact_name);
holder.tvPhone = (TextView) convertView
.findViewById(R.id.tv_contact_phone);
// 将视图容器对象附加到转换视图
convertView.setTag(holder);
} else {
// 转换视图存在时,直接从它获取视图容器对象
holder = (ViewHolder) convertView.getTag();
}
// 获取联系人对象
Contact contact = contacts[position];
// 将该联系人信息填充两个控件的内容
holder.tvName.setText(contact.getName());
holder.tvPhone.setText(contact.getPhone());
// 返回转换视图对象
return convertView;
}
/**
* 视图容器,一个容器类, 包含两个控件
*/
private static class ViewHolder {
TextView tvName;
TextView tvPhone;
}
}
(3)主界面类MainActivity
/**
* 功能:利用基适配器来绑定数据到列表控件
* 日期:2015年7月27日
*/
package net.hw.listview;
import android.app.Activity;
import android.os.Bundle;
import android.widget.BaseAdapter;
import android.widget.ListView;
public class MainActivity extends Activity {
/**
* 联系人列表控件
*/
private ListView lvContact;
/**
* 联系人数组
*/
private Contact[] contacts;
/**
* 联系人适配器
*/
private ContactAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 利用布局文件设置用户界面
setContentView(R.layout.activity_main);
// 通过资源索引获得控件实例
lvContact = (ListView) findViewById(R.id.lv_contact);
// 创建数据源(对象数组)
contacts = new Contact[8];
contacts[0] = new Contact();
contacts[0].setName("凤飞飞");
contacts[0].setPhone("158121245");
contacts[1] = new Contact();
contacts[1].setName("费翔");
contacts[1].setPhone("153456235");
contacts[2] = new Contact();
contacts[2].setName("郑智化");
contacts[2].setPhone("151191245");
contacts[3] = new Contact();
contacts[3].setName("邓丽君");
contacts[3].setPhone("155565645");
contacts[4] = new Contact();
contacts[4].setName("童安格");
contacts[4].setPhone("139121200");
contacts[5] = new Contact();
contacts[5].setName("谭咏麟");
contacts[5].setPhone("159128945");
contacts[6] = new Contact();
contacts[6].setName("张学友");
contacts[6].setPhone("159232345");
contacts[7] = new Contact();
contacts[7].setName("李克勤");
contacts[7].setPhone("153125675");
// 创建联系人适配器
adapter = new ContactAdapter(this, contacts);
// 给列表控件设置适配器
lvContact.setAdapter(adapter);
}
}
课堂练习:采用基适配器实现下述列表。
四、课后作业
采用基适配器实现如下列表,并对列表项实现单击事件处理。
引申学习
1、下拉刷新列表(Android-PushToRefresh)
3、ExpandableListView(
http://note.youdao.com/noteshare?id=366f42241f28cb714758aee9c61c6479&sub=B3F5E161EFFD436EAFB2AFCBC49E9AAB
)
下面给出一些列表控件案例,供有兴趣的同学参看。
1、列表视图演示
2、列表:阅读古诗(新)