Flex3里处理延误的实例化(I)
延误的实例化是 Flex
里的一个技巧,IContainer的子控件没有被创建,直到用户真正地需要看到它们。例如,如果你知道你的组件仅仅有前一半会显示,你可以控制添加到通过MXML的组件的子控件的创建,仅显示你知道会首先看到的那些。然后,当它们从上往下移动时,你可以看到其它的了。
Flex 组件最为熟悉的一个使用延误实例化的范例就是ViewStack和它的子类, Accordion和
TabNavigator。在大部分情况下,ViewStack上的creatioPolicy会是“自动”的,这就意味着ViewStack的直接子控件会被马上创建。但是在大部分案例中,孙子控件是在特定的ViewStack面板被显示时创建的。通常情况下,这个过程很轻松,但是最近,我两次遇到”gotchas“:一个是当ViewStack包含了需要在其孙子控件上设定属性的Container子类,一次是当ViewStack的子控件由Repeater创建,而那些子控件包含了一个基于List的组件。
本周,我将讨论第一种情况,我将在下周讨论Repeater案例。对于这个问题的解决方案很简单。因此如果你时间比较紧,不想费劲去了解“为什么“。那现在就
进入解决方案。
子类化Container不是那么难的-你只需要把Canvas, Box, VBox,
等作为你的AS类的子类或MXML的根标签使用。为简洁起见,我将参照一个假定拓展的VBox叫做
VBoxWithComboBoxes。现在,comboboxes需要大量的设置。首先,它们需要一个dataProvider,这样你可以有几个选择。用数据绑定的方法就比较简单。但是,决定在combobox里选定内容的值很少与dataProvider里的条目精确映射,所以通常你不得不遍历dataProvider以寻找满足你的标准的条目,然后据此设定selecteditem或selectedindex。
这就导致了一个问题,因为一旦决定选定内容的值改变或dataProvider改变,你就需要进行此操作。没有深入组件的生命周期,因此我们不能确保当这些值的设值函被调用时,ComboBox的确存在。所以大部分开发人员在设值函数里调用invalidateProperties(),
设置一个标志(我们在commitProperties()可看到,覆盖),然后进行改变。通常,这个执行都会进行,因为commitProperties()常常在createChildren()之后被调用。
但是如何你像我一样,认为createChildren()结束时,所有的子控件已经被创建,你就可能错了。因为当利用延误的实例化创建一个容器时,子控件很有可能尚
未 被创建。因此,这个挑战,子控件是否被创建了,就变得很关键。
我之前的研究显示,子控件肯定initialize()结尾时被创建。因此我的第一个想法就是,检查在commitProperties()里的初始化。这只有一个问题,就是,当我调用在设值函数里的commitProperties()时,被设定的验证标志已经在初始化完成时被清除,因此commitProperties()不会被调用。所以,我需要找到更有保证性的方法,确保在子控件没有被创建之前,我不会读取子控件。
为此,我不得不深入Container和ViewStack代码,我想,我最好做下总结。
a.
在UIComponent(是Container的基类),initialize() 调用createChildren();
b.
Container覆盖createChildren(),调用一个新的函数createComponentsFromDescriptors()。这需要一个标志告知是否要递归子控件。当在Container内部调用时,这个标志通常是正确的。在函数结束时,它设定一个标量,processedDescriptors。该变量作为UICompoent里的一个设值函数。除了设定潜在的局部变量,这个设值函数产生一个事件,通知每个人容器被初始化。
c. ViewStack
覆盖createComponentsFromDescriptors,检查creationPolicy,然后针对creationPolicy,
设定递归标志。在此覆盖结 束
,processedDescriptors变量也是正确的,但是记住,它仅仅是ViewStack的processedDescriptors变量(尽管,在第一个面板上的组件上,它也是正确的)。这就意味着如果我们的VBoxWithComboBoxes
是ViewStack的子控件,但是不是在第一个面板上,它的子控件就很可能不会被创建(我们将在下周讨论特例),它的processedDescriptors
变量也会是错误的,直到ViewStack告知它处理其子控件描述符。
d.
ViewStack还有一个函数,instantiateSelectedChild(),
从ViewStack的其它地方被调用。当selectedIndex设值函数设定可能选择未被实例化的子控件的条件时,这个函数在commitProperties()。该函数然后调用createComponentFromDescriptors(),递归标志设定为正确,标志告诉我们的VBoxWithComboBoxes
创建子控件,然后processedDescriptors实例变量在创建的新VBoxWithComboBoxes上被设定为正确。
这个研究清楚地显示,我寻找的变量就是processedDescriptors。但是,我还是不确定覆盖什么以说服commitProperties()
运行。我的第一个选择是initialize(),
因此它更为简单的方法签名意味着我在调用super.initialize()时,不太可能把事情搞砸。然而,似乎编译器对于“不赞成”在Container子类里覆盖initialize(),因此我不再覆盖createComponentsFromDescriptors(),
仅仅是向invalidateProperties()增加了一个调用。所以,随着我的commitProperties覆盖,解决这个问题的代码就是:
override public function createComponentsFromDescriptors(recurse:Boolean=true):void
{
super.createComponentsFromDescriptors(recurse);
/* 这里确保现在所有的组件已被创建
我们可以付属性给它们.
*/
invalidateProperties();
}
override protected function commitProperties():void {
if (processedDescriptors) {
if (_comboSelectedPropertyChanged ||
_comboDataProviderPropertyChanged) {
childCombo.dataProvider=_comboDataProviderProperty;
//在dataProvider初始化标志中逻辑寻找基于项属性的被选定项
_comboSelectedPropertyChanged =false;
_comboDataProviderPropertyChanged=false;
}
}
super.commitProperties();
}
浙公网安备 33010602011771号