本节书摘来自异步社区《Android UI基础教程》一书中的第2章,第2.4节显示列表,作者 【美】Jason Ostrander,更多章节内容可以访问云栖社区“异步社区”公众号查看
2.4 显示列表
Android UI基础教程
用来开发应用最常见的视图类型之一就是ListView。这个视图呈现了一个垂直滚动的项目列表。每一行都会有一些文本但是通常也会包含其他视图,例如ImageView和按钮等(联系人应用就是很好的例子)。当你需要把数据列表呈现给用户的时候,使用ListView最合适。它是如此常见,以至于Android实际上提供了展示一个列表的内置活动。
2.4.1 ListActivity
一个ListActivity将绑定到一个包含有ListView的默认视图。不必要在活动的onCreate方法中调用setContentView,因为默认情况下ListActivity已经被设置为ListView了(当然你可以自定义一个视图,如果你选择这么做的话)。ListActivity类也包含了一些搜索和设置列表数据以及处理项目选择的方便方法。虽然使用ListActivity来展示列表并不是必须,但是当你想要把列表数据展示给用户时你应该考虑使用它。
Android 默认布局
ListActivity实际上把它的内容设置到了一个构建于Android OS上的特殊布局上。这个布局包含内容单一的ListView。在创建应用时,还有另一些内置的布局可以使用,许多这类布局都被包含在android.R.layout类中。下面是你可以和ListView一起使用的两个布局。 android.R.layout.simple_list_item_1用来让一个ListView每行显示一行文本。 android.R.layout.two_line_list_item用来让列表每行显示两行文本。
2.4.2 XML布局
显示列表很方便,但是有时候你需要展示的远不止一个列表。在这些情况下,你可以创建一个标准的布局文件并且使用列表视图来展示列表。列表视图和Android中的其他视图使用的定义方式相同:
`<ListView android:layout`_`width="match`_`parent"`
` ``android:layout`_`height="match`_`parent" android:id="@+id/list">`
`</ListView>`
列表视图有一些特殊的属性,可以在更复杂的布局中使用这些属性。首先是android:entries属性。当有一个静态的。不变的值的列表来填充列表视图时使用这个属性。你可以通过简单地引用资源来使用entries属性,而不需要编程来填充列表。还有一些改变外观和分隔行之间行为的属性。总的来说,你应该与默认值保持一致,不要与平台的外观和感觉相悖。
行布局
为列表行创建布局与为活动创建布局相同:你需要创建一个有布局容器以及一些视图的XML文件。每一行都会包含那个布局,这让你可以设置文本和图像的值。Android平台提供了一些默认的行布局。一般来说对于你想创建的列表视图这些已经够用了。然而,你同样可以为列表行创建自定义的布局。要创建自定义布局,只需创建一个新的布局文件并在把数据绑定到列表视图时使用它。
时间跟踪应用程序将需要为它的列表视图自定义布局。在res/文件夹中,新建一个名为time_row.xml的布局文件:
`<?xml version="1.0" encoding="utf-8"?>`
`<LinearLayout xmlns:android="http://schemas.android.com/`
`→ apk/res/android"`
` `` `` ``android:id="@+id/time`_`row" android:orientation="horizontal"`
` `` `` ``android:layout`_`width="match`_`parent"`
` `` `` ``→ android:layout`_`height="wrap`_`content"`
` `` `` ``android:gravity="center" android:paddingLeft="10dp"`
` `` `` `` android:paddingRight="10dp" android:paddingBottom="20dp"`
` `` `` ``android:paddingTop="20dp">`
` `` `` ``<TextView android:id="@+id/lap`_`name"`
` `` `` `` → android:layout`_`height="wrap`_`content"`
` `` `` `` android:text="Lap 1" android:layout`_`weight="1"`
` `` `` `` → android:layout`_`width="0dp" />`
` `` `` `` <TextView android:id="@+id/lap`_`time"`
` `` `` `` → android:layout`_`height="wrap`_`content"`
` `` `` `` android:text="00:00:00" android:layout`_`weight="1"`
` `` `` `` android:layout`_`width="0dp" android:gravity="right" />`
`</LinearLayout>`
这个文件使用一个简单的线性布局来展示两个并排的文本视图:
一个是会议的名称,另一个是会议的时间。线性布局使用padding来创建文本视图之间的空间。
2.4.3 把数据绑定到列表
如果你已经知道列表元素都是哪些,那么ListView的entries属性将会很管用。但是如果你希望动态生成列表,那么你需要建立一个列表适配器。ListAdapter基于一些内部的数据存储往ListView中填充数据。有的ListAdapter是用一个静态值的地图来填充列表(与使用XMLentries属性类似),从数组中载入行,从数据库中载入数据。ListAdapter是Adapter类的一个具体的例子。Adapter类被用于往UI的视图上绑定数据(在本书之后的内容中,你会了解更多这方面的知识)。
当你创建一个列表适用器之后,你要重写getView方法。这个方法在列表视图的每一行都会被系统调用。它会使用列表的位置、任何存在的该行的布局以及该行的父视图,并且它会返回行布局视图。对于时间跟踪应用来说,你将会使用先前创建的行布局。创建一个继承自ArrayAdapter<Long>的新类。然后重写getView方法来为列表视图的每一行加载自定义的time_row.xml布局。
`public class TimeListAdapter extends ArrayAdapter<Long> {`
` `` `` `` public TimeListAdapter(Context context, int textViewResourceId) {`
` `` `` `` `` `` `` ``super(context, textViewResourceId);`
` `` `` `` }`
` `` `` `` @Override`
` `` `` `` public View getView(int position, View convertView,`
` `` `` `` → ViewGroup parent) {`
` `` `` `` `` `` `` ``View view = convertView;`
` `` `` `` `` `` `` ``if (view == null) {`
` `` `` `` `` `` `` `` `` `` `` ``view = LayoutInflater.from(getContext()).inflate`
` `` `` `` `` `` `` `` `` `` `` ``→ (R.layout.time`_`row, null);`
` `` `` `` `` `` `` `` }`
` `` `` `` `` `` `` `` long time = getItem(position);`
` `` `` `` `` `` `` `` TextView name = (TextView) view.findViewById(R.id.lap`_`name);`
` `` `` `` `` `` `` `` String taskString = getContext().getResources().getString`
` `` `` `` `` `` `` `` → (R.string.task`_`name);`
` `` `` `` `` `` `` `` name.setText(String.format(taskString, position+1));`
` `` `` `` `` `` `` `` TextView lapTime = (TextView) view.findViewById`
` `` `` `` `` `` `` `` → (R.id.lap`_`time);`
` `` `` `` `` `` `` `` lapTime.setText(DateUtils.formatElapsedTime(time));`
` `` `` `` `` `` `` `` return view;`
` `` `` `` }`
`}`
这种方法为列表视图的每一行都扩展了一个自定义布局。扩展布局就是把XML布局转换成一组View对象(在第三章中你会了解更多这方面的知识)。当用户滚动列表时,系统将会调用这个方法来为列表创建行。那些已经看不见的行将会被回收。在为数据适配器分配内存时你应当当心,避免不必要的内存分配。不必要的垃圾回收事件是造成Android动画不流畅的主要原因之一。在本书的后面章节中你将会学会如何创建有效率的数据适配器。
提示: 若不必要,不要扩展新视图。在代码中,视图只会在它不存在的情况下才会进行扩展。这是阻止不必要的对象创建以及垃圾回收的一个优化方法。
2.4.4 Loader
往列表适配器中加载数据可能是一个相当乏味的过程:你需要异步处理事务,以避免在主进程上执行太多工作;你需要在数据改变时重新加载数据适配器以保持展示的数据为最新;你还需要在手机的朝向改变时保存数据,因为在这个过程中会销毁并重新创建活动。为了简化这个过程,Android 3 引入了一个名叫Loader的帮助类。Loader类让异步加载数据变得更加简单。
Loader类通过兼容包可以兼容所有Android版本。此包包括了诸如loader和fragmant之类的新Android API,从而你可以在老的Android版本中使用它们。在随后的章节中,你会学到更多关于loader和fragment的知识,但到目前为止,你只需记得你可以通过Loader类简单地把数据绑定到视图上。
Android活动代表用户的界面。所有与用户有关的交互都通过活动发生。作为一个开发者,在创建快速响应的应用时把用户放在第一位很重要。这可能像使用易读的文本视图一样简单,也可能像用户查询时把输入内容保存到数据域一样复杂。理解活动是创建可用的快速响应应用程序的关键。