项目2-4 随手记列表界面设计
学习目标
■ 掌握ListView组件的使用方法,了解ListView组件事件监听处理机制。
■ 掌握SimpleAdapter简单适配器的使用方法。
■ 掌握BaseAdapter自定义适配器的使用方法,了解ListView组件的优化方法。
■ 学会运用ListView组件进行随手记列表界面设计。
项目描述
运用 ListView 组件与 BaseAdapter 自定义适配器的方法,设计一个随手记列表界面,要求在随手记列表界面上能显示图片和文字。
知识储备
2.4.1 ListView组件
在Android中,ListView被称为列表视图,是Android中最常用的一种视图组件。它是以垂直列表的形式显示所有列表项的数据。例如,显示电话簿中联系人的信息、系统功能设置或状态等。
1.ListView组件在XML文件中的基本语法
格式代码如下:
<ListView
属性列表
/>
2.ListView组件常用的XML属性
ListView组件常用的XML属性如表2-14所示。
表2-14 ListView组件常用的XML属性
续表
3.ListView组件显示列表项的方法
ListView 组件显示列表项的数据方式是采用 MVC 模式将前端显示和后端数据进行分离,即ListView组件在装载数据时并不是直接使用ListView.add或者类似的方法添加数据,而是需要指定一个Adapter作为适配器来显示列表中元素的布局方式。
Adapter常用实现类分为以下几种。
ArrayAdapter:通常用于将数组或List集合的多个值包装成多个列表项。
SimpleAdapter:用于将List集合的多个对象包装成多个列表项。
SimpleCursorAdapter:只是用于包装Cursor提供的数据。
BaseAdapter:自定义的适配器。
通过Adapter为ListView指定要显示的列表项,可分为如下步骤。
① 创建 Adapter 对象。对于纯文字的列表项,通常使用 ArrayAdapter 对象。而创建ArrayAdapter对象通常可以有两种情况,一种是通过数组资源文件创建,另一种是通过在Java文件中使用的字符串数组创建。
在创建ArrayAdapter时,必须指定Android系统自带的用于显示选项的TextView组件ID,该参数决定每个列表项的外观形式。这与在项目2-3中的 Spinner 列表选项框中介绍的创建ArrayAdapter 对象基本相同。其不同之处是在创建该对象时,要指定列表项的外观形式。为ListView指定的外观形式通常有以下几个属性值。
simple_list_item_1:每一个列表项都是普通的TextView。
simple_list_item_2:每一个列表项都是普通的TextView(字体略大)。
simple_list_item_checked:每一个列表项都是一个已勾选的列表项。
simple_list_item_multiple_choice:每一个列表项都是带多选框的文本。
simple_list_item_single_choice:每一个列表项都是带单选按钮的文本。
② 将创建的适配器对象与ListView相关联,具体代码如下:
ListView.setAdapter(adapter);
下面分别介绍在Android中创建ListView的方法。
第一种:通过数组资源文件创建ListView。
【例2-6】在 Android Studio中创建新项目,名称为 ListViewDemo,通过布局文件添加一个 ListView 组件,并通过数组资源为其设置列表项,列表项中的数据为电话簿中联系人姓名。
首先修改新建项目List View Demo中res/layout目录下的布局文件activity_main_xml,将默认添加的Text View组件删除,并添加一个List View组件。添加List View组件布局代码如下:
<ListView
android:id="@+id/listView_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:entries="@array/name"
android:divider="@color/colorAccent"
android:dividerHeight="3dp"
/>
然后,在res/values目录中创建一个定义数组资源的XML文件arrays.xml,并在该文件中添加名称为name的字符串数组。关键代码如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="name">
<item>张艳</item>
<item>李力</item>
<item>王丹</item>
</string-array>
</resources>
最后运行本实例,观察运行结果。
注意
数组资源文件中的列表项也可放置在strings.xml文件里。
第二种:通过ArrayAdapter创建ListView。
【例2-7】修改项目ListViewDemo下的布局文件activity_main_xml中的ListView组件属性,并通过 ArrayAdapter 适配器使用数组提供列表项,列表项中的数据为电话簿中联系人姓名和电话,列表项可设置为单选模式。
首先,修改 ListViewDemo 项目的 res/layout 目录下的布局文件 activity_main_xml 中的ListView组件属性。ListView组件的布局代码如下:
<ListView
android:id="@+id/listView_tel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:divider="@color/colorAccent"
android:dividerHeight="3dp" />
然后,在MainActivity.java中的onCreate()方法中为ListView创建并关联适配器。先定义存储姓名和电话的字符串数组,然后获取布局文件中ListView组件,创建适配器,并将字符串数组装载到适配器,最后将适配器与ListView组件相关联。关键代码如下:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//定义一个数组,并将字符串数组初始化
String [] arr = {"张艳 133123456","李力 185234567","王丹 130122334"};
//获取布局文件中的ListView
ListView listview = (ListView)findViewById(R.id.listView_tel);
//定义数据源适配器
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this,andro
id.R.layout.simple_list_item_single_choice,arr);
//为ListView设置Adapter
listview.setAdapter(arrayAdapter);
}
最后运行本实例,观察运行结果。
第三种:通过SimpleAdapter创建ListView。
SimpleAdapter适配器的主要功能是将List集合的数据转换为ListView可以支持的数据。而要实现这种转换,首先需要定义一个数据显示的布局文件,该布局文件用于定义 ListView每一行所需要显示的所有组件。在需要转换的List集合中保存的是多条Map集合数据,这些Map集合保存的是具体要显示的信息,即该布局文件中的每个组件的ID就是保存在Map集合的key,而每个组件的显示内容,则是由Map保存的value决定的。SimpleAdapter构造方法如下:
SimpleAdapter(Context context,List<Map<String ,?>>,int resource,String[] from,int[] to)
下面通过【例2-8】说明使用SimpleAdapter创建ListView的方法。
【例2-8】在Android Studio中创建新项目,名称为SimpleAdapterDemo。然后,修改项目下的布局文件activity_main_xml,添加第1个ListView组件。通过定义SimpleAdapter适配器,可提供数据源的列表项,列表项数据为电话簿中的头像、联系人姓名、城市和电话。
首先修改SimpleAdapterDemo项目res/layout目录下的布局文件activity_main_xml,将默认添加的TextView组件文本属性修改为“电话簿”,并添加第1个ListView组件。布局代码如下:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="电话簿"
android:textSize="30dp"
android:layout_gravity="center"/>
<ListView
android:id="@+id/personListView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:divider="@color/colorAccent"
android:dividerHeight="3dp" />
创建ListView中列表选项布局文件list_person_layout.xml的方法:右击【res】→【layout】目录,选择快捷菜单【New】→【XML】→【Layout XML File】,输入list_person_layout,单击【Finish】按钮。
其次,在列表选项布局文件list_person_layout.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">
<ImageView
android:paddingRight="20dp"
android:layout_width="50dp"
android:layout_height="30dp"
android:src="@drawable/contact_pic"
android:id="@+id/img"/>
<TextView
android:id="@+id/txtName"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/txtCity"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/txtPhone"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content" />
</LinearLayout>
然后在MainActivity.java程序中创建适配器需要的数据集合对象getData()方法,再在onCreate()方法中获取布局文件中的ListView组件,定义数据源适配器,将适配器与ListView组件相关联。其关键代码如下:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取布局文件中的ListView
listview = (ListView)findViewById(R.id.personListView);
//定义数据源适配器
SimpleAdapter adapter = new SimpleAdapter(this, getData(),R.layout.list_ -
person_layout, new String[] { "img", "name", "City", "Phone" }, new int[]-
{ R.id.img,R.id.txtName, R.id.txtCity, R.id.txtPhone });
//适配器与ListView相关联
listview.setAdapter(adapter);
}
//创建适配器需要的数据集合对象
private List<Map<String, Object>> getData() {
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
Map<String, Object> map = new HashMap<String, Object>();
map.put("img", R.drawable.contact_pic);
map.put("name", " 张艳 ");
map.put("City", " 长沙 ");
map.put("Phone", " 133123456 ");
list.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.contact_pic);
map.put("name", " 李力 ");
map.put("City", " 株洲 ");
map.put("Phone", " 185123456");
list.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.contact_pic);
map.put("name", " 王丹 ");
map.put("City", " 武汉 ");
map.put("Phone", " 130123456 ");
list.add(map);
map = new HashMap<String, Object>();
map.put("img", R.drawable.contact_pic);
map.put("name", " 刘明艳 ");
map.put("City", " 上海 ");
map.put("Phone", " 1389990456 ");
list.add(map);
return list;
}
最后运行本实例,观察运行结果。
第四种:通过ListView+BaseAdapter实现复杂列表选项。
此方法将在随手记列表界面设计项目中介绍。
4.ListView组件监听器
ListView组件最重要的监听器是实施对用户选择了某个选项的监听。设定该监听器的方法为public void onItemClick(AdapterView<?> parent, View view, int position, long id)。它的功能是监听ListView选项被选中的事件。例如,当用户单击ListView的列表项时,为获得该选项的值,需要为ListView添加OnItemClickListener监听器。其代码如下:
//为ListView添加OnItemClickListener监听器
listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//获取选项的值
String result = parent.getItemAtPosition(position).toString();
//显示提示的消息
Toast.makeText(MainActivity.this,result,Toast.LENGTH_SHORT).show();
}
});
2.4.2 BaseAdapter自定义适配器
在【例2-7】和【例2-8】中已经分别讲解了ArrayAdapter数组适配器和SimpleAdapter简单适配器的使用方法,现在将要介绍BaseAdapter自定义适配器的使用。BaseAdapter自定义适配器与其他Adapter有些不同,其他的Adapter可以直接在其构造方法中进行数据的设置,如下:
SimpleAdapter adapter = new SimpleAdapter(this, getData(), R.layout.list_item, new String[]{"img","title","info",new int[]{R.id.img, R.id.title, R.id.info}});
但在BaseAdapter自定义适配器中就需要创建一个继承自BaseAdapter的类,并要重载getCount、getItem、getItemId和getView方法,其中getView是用来刷新它所在的ListView。例如,创建MyAdapter类,让它继承自BaseAdapter的类,关键代码如下:
Public class MyAdapter extends BaseAdapter {
private Context context;
public MyAdapter(Context context) {
this.context = context;
}
@Override
public int getCount() {
//此适配器中所代表的数据集中的条目数
return 0;
}
@Override
public Object getItem(int position) {
//获取数据集中与指定索引对应的数据项
return null;
}
@Override
public long getItemId(int position) {
//获取在列表中与指定索引对应的行ID
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//获取一个在数据集中指定索引的视图来显示数据
return null;
}
}
其中,在对getView方法重写时,当列表项数据量很大时,如果每次都重新创建View,设置资源,会严重影响手机性能,因此,在重载getView方法时,要进行ListView优化。
ListView优化的第1种方法是利用缓存convertView的方式,判断缓存中是否存在View,如果不存在则要创建View,如果已存在就不要再创建View。利用重用缓存convertView传递给getView()方法来避免填充不必要的视图,如下:
If (convertView == null){
View view = LayoutInfalter.from(getContext()).inflate(resourceID,null);
}else{
View view = convertView;
}
ListView 优化的第2种方法是通过 convertView+ViewHolder 来实现,其中 ViewHolder是一个静态类。当判断convertView为空时,就会根据已设计好的ListView的Item布局XML来为convertView赋值,并生成一个ViewHolder来绑定convertView里面的各个View组件(也就是XML布局里面的组件),再用convertView的setTag将ViewHolder设置到Tag中,以便系统第2次绘制ListView时从Tag中取出。当convertView不为空时,就直接用convertView的getTag()来获得一个ViewHolder。ViewHolder模式通过getView()方法在返回的视图的标签(Tag)中存储一个数据结构,这个数据结构包含了指向要绑定数据的视图的引用,从而避免每次调用getView()的时候调用findViewById()。代码如下:
//先定义ViewHolder静态类
static class ViewHolder {
public ImageView img;
public TextView noteTime;
public TextView noteContent;
}
//重写getView
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
If (convertView == null) {
holder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(context);
convertView = inflater.inflate(R.layout.list_note_layout, null);
holder.img = (ImageView) findViewById(R.id.img)
holder.noteTime = (TextView) findViewById(R.id.noteTime);
holder.noteContent = (TextView) findViewById(R.id.noteContent);
convertView.setTag(holder);
}else {
holder = (ViewHolder)convertView.getTag();
holder.img.setImageResource(R.drawable.ic_launcher);
holder.noteTime.setText("5月1日");
holder.noteContent.setText("劳动节");
}
return convertView;
}
项目实施
1.项目分析
运用 ListView 组件与 BaseAdapter 自定义适配器的方法,设计一个随手记列表界面,要求在随手记列表界面上能显示图片和文字,效果如图2-15所示。
图2-15 随手记列表界面
随手记列表界面设计特点:
① 整个界面由标题和垂直列表的数据项组成。
② 每个数据项由一个图片、两个文本框组成。
分析结果:
① 整个界面可采用相对布局或线性布局技术。
② 用一个文本框组件表示标题。
③ 用一个 ListView 组件实现垂直列表显示功能。
④ ListView 组件中 item 项的布局采用线性布局技术,并由图片和文本框组成。
2.项目实现
(1)新建随手记项目。
启动 Android Studio,在 Android Studio 起始页选择【Start a new Android Studio project】,或在Android Studio主页菜单栏上选择【File】→【New】→【New Project】,新建 Android 工程。在 New Project 页面上输入应用程序的名称(NoteTest)、公司域名(com.zzhn.zheng)和存储路径,单击【Next】按钮。然后,选择工程的类型以及支持的最低版本,单击【Next】按钮。之后选择是否创建Activity以及创建Activity的类型,选择【Empty Activity】,单击【Finish】按钮。
(2)设计布局文件。
首先在【res】→【layout】文件夹中,打开布局的【activity_main.xml】文件,使用相对布局技术,添加ListView组件,设置ListView、TextView组件属性。
操作方法:展开【res】→【layout】文件夹,双击【activity_main.xml】文件,打开右侧布局文件text编辑窗口,在编辑窗口中输入activity_main.xml文件代码,如下:
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="随手记列表"
android:textSize="30sp"
android:id="@+id/tv1"
android:gravity="center"/>
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/tv1"
android:id="@+id/listview"
android:divider="@color/colorAccent"
android:listSelector="#ece10d"
android:fadeScrollbars="true"
android:dividerHeight="3dp" />
创建ListView中item列表选项布局文件list_note_layout.xml。
操作方法:右击【res】→【layout】目录,选择快捷菜单【New】→【XML】→【Layout XML File】,输入list_note_layout,单击【Finish】按钮。在列表选项布局文件list_note_layout.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:orientation="horizontal">
<ImageView
android:paddingRight="20dp"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/note_pic"
android:id="@+id/img"/>
<TextView
android:layout_width="0dp"
android:layout_weight="0.5"
android:layout_height="wrap_content"
android:id="@+id/noteTime"/>
<TextView
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:id="@+id/noteContent"/>
</LinearLayout>
(3)编写Java程序代码。
首先创建MyAdapter类,让它继承自BaseAdapter的类,并通过convertView+ViewHolder实现对ListView的优化。程序关键代码如下:
public class MyAdapter extends BaseAdapter {
private Context context;
private List<Map<String, Object>> list ;
public MyAdapter(Context context, List<Map<String, Object>> list) {
this.context = context;
this.list = list;
}
@Override
public int getCount() {
//在此适配器中所代表的数据集中的条目数
return list.size();
}
@Override
public Object getItem(int position) {
//获取数据集中与指定索引对应的数据项
return list.get(position);
}
@Override
public long getItemId(int position) {
//获取在列表中与指定索引对应的行id
return position;
}
//ViewHolder静态类
static class ViewHolder
{
public ImageView img;
public TextView noteTime;
public TextView noteContent;
}
//获取一个在数据集中指定索引的视图来显示数据
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
//如果缓存convertView为空,则需要创建View
if(convertView == null){
holder = new ViewHolder();
//根据自定义的Item布局加载布局
LayoutInflater inflater = LayoutInflater.from(context);
convertView = inflater.inflate(R.layout.list_note_layout, null);
holder.img = (ImageView)convertView.findViewById(R.id.img);
holder.noteTime = (TextView)convertView.findViewById(R.id.noteTime);
holder.noteContent = (TextView)convertView.findViewById(R.id.noteContent);
//将设置好的布局保存到缓存中,并将其设置在Tag里,以便后面方便取出Tag
convertView.setTag(holder);
}else {
holder = (ViewHolder)convertView.getTag();
}
holder.img.setBackgroundResource((Integer)list.get(position).get("img"));
holder.noteTime.setText((String)list.get(position).get("noteTime"));
holder.noteContent.setText((String)list.get(position).get("noteContent"));
return convertView;
}
}
然后,在 MainActivity.java 中编写创建适配器需要的数据集合对象 getData()方法,在onCreate()方法中将获取的数据设置到 list 中。创建自定义适配器,将自定义适配器绑定到数据源,并与ListView组件相关联。创建适配器需要的数据集合对象的代码如下:
private List<Map<String, Object>> getData()
{
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
Map<String, Object> map;
for(int i=0;i<10;i++)
{
map = new HashMap<String, Object>();
map.put("img", R.drawable.note_pic);
map.put("noteTime", "4月25日");
map.put("noteContent", "班级活动内容……");
list.add(map);
}
return list;
}
最后,设置数据源、ListView与自定义适配器相关联的代码如下:
listView = (ListView)findViewById(R.id.listview);
//获取将要绑定的数据并设置到list中
list = getData();
MyAdapter adapter = new MyAdapter(this,list);
listView.setAdapter(adapter);
(4)调试运行。
① 单击工具栏上的 AVD Manager 图标,打开虚拟设备对话框,在虚拟设备对话框中单击启动虚拟设备的命令按钮,打开Android Studio模拟器。
② 单击工具栏上的“三角形”运行按钮,运行本项目。
项目总结
通过本项目的学习,读者应学会运用 ListView+BaseAdapter 实现复杂列表选项的设计方法。
① 创建一个添加了ListView组件的布局文件,并设置相应属性。
② 创建ListView组件中item列表选项布局文件。
③ 创建一个类,让它继承自 BaseAdapter 的类,并通过 convertView+ViewHolder 实现对ListView优化。
④ 创建数据源,并实现数据源、BaseAdapter适配器和ListView组件三者之间的相互关联。
项目训练——用BaseAdapter创建ListView实现联系人列表界面
要求将【例2-8】中用 SimpleAdapter 创建 ListView 实现联系人列表的界面修改成用BaseAdapter创建ListView实现联系人列表的界面,列表选项数据为电话簿中的头像、联系人姓名、城市和电话。
练习题
2-4-1 如何实现在ListView组件上为每个列表项设置带单选按钮或多选按钮的样式?
2-4-2 说明ListView优化的方法和作用。
2-4-3 说明SimpleAdapter适配器的设计方法和作用。
2-4-4 如何监控ListView组件中item选项的变化?