7.2.2 界面组件和窗口

界面组件是Android中最重要的交互单元,每个界面组件都有一个android.view.Window对象。每个Window对象都会负责构造和管理一棵控件树,它会为该控件树构造对应的ViewRoot对象建立与窗口管理服务的双向通信。

Window类是一个抽象类,在不同的设备上会有各自的子类,用于定义其中控件树的结构和形态。比如,在手机设备中,Window的实现类为PhoneWindow,它定义的控件树结构如图7-6所示。该控件树的根控件为DecorView,它派生自帧容器控件android.view.FrameLayout(帧容器控件的特点是:其所有子控件都会层叠着对齐到父控件的左上角),开发者可以通过Window.getDecorView来获得该控件对象。

PhoneWindow定义的交互界面,主要由标题区域和内容区域构成。其中,标题区域的格式比较固定,通常是由标题文字、进度条和组件图标等元素构成,开发者可以通过Activity.setTitle来改变标题的内容。而内容区域是整个界面中最重要的交互区域,开发者可以通过组件功能进行自定义,并通过Activity.setContentView函数进行设定。

但很多应用并不适合采用这样的基本界面框架,Android提供了很多接口帮助开发者突破基本的界面样式。通过Activity.requestWindowFeature函数开发者可以设定窗口参数,改变界面的基本形态。比如,如果需要将标题栏隐藏,可以通过设定Window.FEATURE_NO_TITLE来实现:


/在Activity.onCreate函数中调用/

requestWindowFeature(Window.FEATURE_NO_TITLE);


Activity. requestWindowFeature仅接收一个类型参数,如果开发者需要为该样式指定一些附加数据,则需要使用Window.setFeatureInt函数。比如说,如果开发者不是想消隐标题栏,而是自定义窗口标题栏的样式,则可以利用Window.setFeatureInt来设定自定义标题栏的样式信息:


/在Activity.onCreate函数中调用/

//标记一下,表明标题栏将自定义

requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);

setContentView(R.layout.content_view);

//设置自定义标题栏的样式R.layout.custom_title

getWindow().setFeatureInt(

Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title);


值得注意的是,Activity.requestWindowFeature函数需要在Activity.setContentView之前调用,而Window.setFeatureInt函数要在之后调用。原因是,对setContentView的调用将会导致整个界面控件树的生成。因此,对控件树样式的设定需要在该构造流程之前,而对控件树中具体控件对象的变更,则需要在整棵控件树构造完成之后进行。对Activity.requestWindowFeature函数和Window.setFeatureInt函数的所有调用,都需要遵循这个编程模式,否则函数调用可能会失效甚至是出错。

7.2.2 界面组件和窗口 - 图1

图 7-6 界面组件的控件结构

除了标题区域和内容区域,在整个PhoneWindow中,还有一个辅助交互的区域,称为选择菜单区域(Option Menu)。选择菜单区域中包含了若干个菜单项,用户点击不同的菜单项时会触发相应的操作。大部分情况下,可选菜单区域为不可见状态,当用户点击菜单键或调用Activity.openOptionsMenu函数时,可选菜单会被构造并展现,而当用户再次点击菜单键或调用Activity.closeOptionsMenu函数时,可选菜单会被隐藏并关闭。

对于整个控件树而言,选择菜单是DecorView的一个子控件,在可见状态时,它会添加到DecorView中,并层叠显示在主区域上,与用户进行交互。而在消隐状态时,会从DecorView中移除,从而使主区域能够显示并与用户进行交互。

在Android中,动态构造控件对象是一个耗时的过程。所以,如果反复对可选菜单进行构造和销毁,就会降低界面响应速度,影响交互体验。为此,Android为可选菜单部署了缓存策略。在可选菜单第一次展示之前,界面组件的Activity.onCreateOptionsMenu函数会被调用,系统可以派生出该函数的构造菜单。可选菜单一旦被构造,在整个组件生命周期内就不会被销毁,用户再次使用可选菜单时,也不再调用onCreateOptionsMenu函数进行构造,而是直接从缓存中读出可选菜单的对象加入到控件树中。如果可选菜单项需要动态地进行变更,则需要重载Activity.onPrepareOptionsMenu函数。在可选菜单每次呈现前,系统都会调用该函数,开发者可以在其中动态变更菜单项的可见性和可用性等。