系统设计
基于OPhone的RSS Reader将以Google Reader的功能为参考,并充分考虑到手机屏幕的限制。RSS Reader将实现以下几个Activity:
1. MainActivity:负责列出当前订阅的所有Item,根据用户设置显示未读项或全部项;
2. ReadActivity:当用户点击MainActivity的某一个Item时,显示Item的详细内容;
3. SubsActivity:负责列出当前的所有订阅,并可以添加或删除;
4. PrefActivity:显示选项设置,可以修改或取消。
Activity的切换是根据用户点击或选择菜单时发起的。在编写代码之前,我们先绘制这4个Activity的草图。祭出UI设计的利器——Microsoft Excel!没错,利用Excel强大的表格编辑功能,我们能很容易地作出UI界面。设计的4个Activity界面如下:
我们先来实现第一个用户界面:MainActivity。它用于显示当前所有的Item,显然,ListView符合我们的要求。当我们需要一个充满整个屏幕的ListView时,使用ListActivity更加简单,因为ListActivity已经封装了对ListView的操作,我们可以通过ListActivity直接显示Item列表。
新建OPhone工程,命名为RssReader,然后,修改SDK自动为我们生成的MainActivity,将继承关系由Activity改为ListActivity:
有一个id为list的ListView,这样,ListActivity才能正确关联该ListView,因此,我们修改res/layout/main.xml,放入一个id为list的ListView:
ListActivity要求被显示的View中必须最外层的LinearLayout是最常用的线性布局,指定其layout_width和layout_height属性均为fill_parent,表示充满整个屏幕,该LinearLayout包含一个id为list的ListView和一个id为empty的TextView。当ListView中没有可以显示的列表项时,这个TextView将自动显示,会提示用户“No data display”,当ListView中至少有一个列表项时,这个TextView将自动隐藏。这些功能都是ListActivity为我们自动实现的,无需再手动编写逻辑。
运行RssReader,屏幕显示“No data display”,因为我们还没有向ListView中添加任何列表项。
下面,我们将向ListView中添加一些列表项。在OPhone系统中,如果要在UI中显示数据,需要使用Adapter来绑定数据,这样可以将UI的显示逻辑和具体的数据分离。例如,ListView需要一个数组来表示列表项,就可以使用最常用的ArrayAdapter,将任意数组绑定到ListView中。为了让ListView显示一个字符串数组表示的列表项,我们只需要在onCreate()方法中添加一句:
自带的android.R.layout.simple_list_item_1。通过查看源码可以看到,simple_list_item_1非常简单,仅包含一个TextView,用于显示单行文字:
ArrayAdapter需要一个int参数指定用于显示每个列表项的layout的id,由于OPhone系统本身已经内置了一些常用的layout,对于ListView的列表项使用的layout,可以直接指定OPhone我们可以在此基础上修改,以便实现定制外观的列表项。运行应用程序,列表项显示效果如下:
小提示:当使用非String泛型的ArrayAdapter时,例如自定义的JavaBean,ArrayAdapter会自动调用toString()方法,所以,确保正确覆写了toString()方法,就能正确显示列表项。
当用户点击某一个列表项时,可以通过向ListView注册OnItemClickListener来接收事件。由于我们使用的ListActivity已经自动向其包含的ListView注册了OnItemClickListener,我们只需要覆写ListActivity的onListItemClick()方法即可。
现在,我们已经用硬编码的方式显示出了RSS的Item项。如果我们把硬编码的数组替换成从网络读取的RSS列表项,就可以完成一个最基本的RSS Reader了。
深入ListView
趁着对ListActivity的熟悉,我们继续编写管理订阅的SubsActivity。SubsActivity需要列出所有的订阅,自然是继承ListActivity比较方便了。此外,删除菜单需要用户选择一个订阅,然后才能删除选中的订阅,因此,和MainActivity对ListView的布局和操作又有所不同。
我们先根据MainActivity写出SubsActivity的框架:
注意到ListView的列表项使用的layout是android.R.layout.simple_list_item_single_choice,该Layout包含一个CheckedTextView,可以显示一个单选按钮和一个字符串。仿照MainActivity的布局,编写subs.xml:
不要忘了向AndroidManifest.xml文件中添加SubsActivity的声明。SubsActivity运行结果如下:
点击列表项,发现单选按钮并未选中,因为ListActivity还没有为我们把单选逻辑封装进去,所以,需要再添加一点代码。首先,为SubsActivity添加一个成员变量,记录当前选中的列表项的索引:
然后,覆写onListItemClick()方法:
因为我们为ArrayAdapter指定了一个包含CheckedTextView的layout,因此,在onListItemClick()方法中传入的View参数就是一个CheckedTextView,将其强制转型,并设置其checked状态就可以了。
现在,点击某个列表项,其对应的单选按钮就被选中了:
不过,当用户在带有摇杆的真实手机上操作时,直接点击触摸屏是可以选中单选按钮的,使用摇杆上下选择时则不行(测试时可以用鼠标滚轮模拟),因为我们还没有编写代码响应ListView的OnItemSelected事件。在onCreate()方法中追加代码如下:
现在,通过鼠标滚轮模拟手机摇杆操作,单选框会自动被选中:
使用Intent
现在,我们已经编写了两个Activity:MainActivity和SubsActivity。当用户启动RSS Reader时,显示MainActivity,当用户希望管理订阅时,我们如何从MainActivity中启动SubsActivity呢?在OPhone系统中,各种组件不是直接调用,而是通过Intent联系起来的。例如,MainActivity想要启动SubsActivity,就可以实例化一个Intent,然后通过startActivity()方法告诉OPhone系统执行这个Intent就可以了:
通过指定Activity的class来启动Intent称之为显式启动一个Intent,此外,在OPhone系统中还可以隐式地启动一个Intent。什么时候我们需要一个隐式的Intent呢?例如,应用程序需要调用OPhone系统的浏览器为用户显示一个URL时,应用程序很难知道浏览器的Activity的类名(事实上,从应用程序之间的耦合关系上讲,最好不要知道浏览器的Activity的任何信息),这时,通过构造一个隐式的Intent,让OPhone系统自己去寻找合适的浏览器为用户打开这个URL:
除了调用系统浏览器外,拨打电话、发送短信等系统操作都可以通过隐式的Intent实现,OPhone系统的这种设计可以让不同的应用程序通过Mashup的方式组合,而用户几乎感觉不到在不同的应用程序间切换。