3.2.2 界面组件的开发

界面组件都是android.app.Activity类的子类型。实现界面组件需要派生Activity类或其子类,并在配置文件中描述该组件的信息,比如:


//子类com.duguhome.test.SimpleActivity

public class SimpleActivity extends Activity{

@Override

Protected void onCreate(Bundle savedInstanceState){

…#此处初始化数据

}

}

//在配置文件中添加组件信息

<application…>

<activity android:name="SimpleActivity">

…#此处可以添加Intent filter信息

</activity>

</application>


当然,这只是一个最基本的实现片段。在实际开发中,界面组件是Android中最复杂的组件,有大量需要妥善处理的问题。

❑构造界面

作为应用与用户交互的窗口,界面组件的第一要务当属利用控件构造交互界面。在Android中,界面样式和内容是通过应用资源文件进行描述的。系统会提前将资源文件进行预编译,同时生成与应用中的资源相对应的R类。R类相当于应用资源的目录列表,通过它可以方便地定位到界面所需的资源。在R类的帮助下,通过Activity.setContentView方法,可以将相关资源信息设置到界面的内容区域中。

关于界面构造的具体方法及实现细节,本书会在第7章和第8章进行详细的介绍和剖析。

❑处理交互事件

界面是用来与用户进行交互的,不可避免地要处理点击、按键和滑动等交互事件。Android的界面交互事件处理,大体上有两类。一类是当前界面的全局事件,可以通过重载Activity中特定的方法来实现,比如重载Activity.onKeyDown等函数可以捕获并处理按键事件。

另一类则是和具体控件相关的交互事件。Android的控件采用了观察者模式,可以通过添加监听者处理相关事件。比如,调用控件对象的View.setOnClickListener方法可以监听该控件被点击的事件。

小贴士 Android的界面框架并没有采取MVC的架构。因此在开发的时候,需要特别注意逻辑代码和界面的分离,以避免界面设计的频繁变更影响到逻辑的实现。下面给出一些设计上的经验。

1)代码中尽量使用抽象的控件对象,不需要将类型转换成对应的子控件类型。因为具体的子控件随时都可能变换,这种改变最好就在资源文件中修改,而不要涉及代码。比如,界面中有一个按钮,开发中只用了它的点击事件,那么,只需要把该按钮转换成View对象而不是Button对象。如果在今后的设计中,按钮变成了标签控件(TextView)或者是图像控件(ImageView),都不需要改变代码便可以支持。

2)能用资源表示的逻辑就不要用代码来控制。比如,在Android中可以用资源文件来控制控件在不同状态下的背景图片,这种可以通过资源文件完成的工作就不要放在代码中来做。一旦界面需要变更,就不用再修改代码,只需要调整资源文件即可。

3)降低界面的复杂性。Android的界面通过XML格式的资源文件来表示。与代码相比,XML能表示的逻辑较为简单。因此在进行界面设计时,尽量采用XML逻辑能够表示的界面设计,不要轻易使用大量的代码来控制界面。

4)在控件观察者对象中不要放置具体的实现代码。界面元素会经常变更,具体的逻辑需要在单独的类或函数中实现,而不要放在某个观察者对象中。

❑构造菜单、对话框等附加的交互资源

手机界面的显示空间比较狭小,很多时候需要对话框(Dialog)、菜单(Menu)、弹出式窗口(Popup Window)等弹出式交互界面作辅助。

在Android中,频繁构造上述辅助交互界面是一个较为耗时的操作,在一些配置较低的移动设备上会阻塞主进程,产生明显的停滞感。因此,Android为这类对象提供了延迟构造框架,以减少辅助交互界面构造的次数。比如,在用户首次查看界面的选项菜单时,界面组件会调用Activity.onCreateOptionsMenu函数创建该菜单对象,构造出来的对象被缓存下来,等待下次展示。每次将菜单展示给用户前,界面组件还会调用Activity.onPrepareOptionsMenu函数,开发者可以在此函数中调整菜单项的状态。

在尽量减少额外开销的前提下,这样的设计模式避免了反复构建和销毁资源,提升了用户体验。

❑管理界面组件中的数据

Android是一个多任务的操作系统,当同时运行的任务过多时,就需要自动结束部分应用和组件,以保证系统有充足的内存空间来执行新的任务。

Android采取的是进程托管的策略,身处后台的应用进程在内存紧张时会被终止,直到该应用切换至前台时,再重新构造运行。对于开发者而言,需要依照界面组件的生命周期模型,妥善维护好相关的状态。在组件被销毁时序列化保存相关的信息,当应用被重新构造时精准地恢复成销毁前的状态,以保证用户体验的一致性。

界面组件的生命周期管理是界面组件开发中的重要工作,本书的第5章会进行详细的介绍。

❑配置界面组件的任务模型

Android界面组件在运行时,会通过任务进行组织。同一个任务中的界面组件,会按照栈模型线性排列。这种模式理解起来非常简单,但并不能满足所有类别的应用需求。

比如,当一个界面组件需要占用大量资源的时候,就不应该有太多的实例同时存在于任务中,而是要尽可能多地进行复用,以降低系统的开销。因此,Android提供了多种组件任务模型,来调整栈中元素的次序,或者是将单个任务拆分成多个任务,甚至将任务放到不同的进程中去,以提升执行效率。

Android开发者需要合理地配置界面组件的任务模型,通过配置文件中的launchMode、clearTaskOnLaunch、process等参数进行设置。在使用其他界面组件时,也需要通过Intent的标志位(flags)来控制目标组件的任务模型。关于任务模型的更多内容,将在第5章展开讨论。

❑适应环境配置变化

在移动设备的使用过程中,有很多配置信息会随着设备和环境等因素的变更而有所变化,比如,硬键盘消隐、屏幕朝向、语言环境等。

当这些配置信息产生变化的时候,正在与用户进行交互的界面组件需要根据这些变化及时做出调整,为用户提供最合适的交互方式。比如,用户将手机朝向由纵向转为横向,界面应该及时变换为横向的布局以方便用户操作(如图3-4所示);当用户将硬键盘滑出时,界面需要及时地将软键盘(虚拟键盘)消隐,为用户提供广阔的可视界面。

在默认情况下,当配置信息发生变更时,Android会简单地销毁当前交互的界面组件对象,并根据新的配置信息重新构造该组件对象。这种简单的推倒重建策略具有很强的适应性,能够统一地处理各类复杂的场景。但同时,该策略不够精细,往往会付出较多的时间开销。比如浏览器界面。如果每次屏幕朝向有所改变就简单地销毁重建,就会导致当前页面反复刷新,不但浪费了流量,更破坏了用户体验。因此,开发者需要仔细衡量配置信息变化对每个界面组件的影响。如果不期望该组件随着某个配置信息进行销毁重建,可以通过activity配置项configChanges来标明[1],并在Activity.onConfigurationChanged函数中更精细地处理相关配置的变化事件。

3.2.2 界面组件的开发 - 图1

图 3-4 屏幕朝向不同的界面组件显示效果

[1]所有的可配置项,参见SDK:http://developer.android.com/guide/topics/manifest/activity-element.html#config。