Flex 3 - show事件处理程序中的失效会破坏生命周期

时间:2011-10-20 00:38:21

标签: flex

只是想知道是否有人遇到过这个并且有一个很好的解决方案。

以下是如何重现:

创建一个标签导航器(或视图堆栈,无论如何)并添加几个标签。

在您的标签上添加一个show事件处理程序。在事件处理程序内部,在选项卡的其中一个子项上调用invalidateProperties()和invalidateDisplayList()。在childs commitProperties()和updateDisplayList()上设置一个断点。您会注意到updateDisplayList()在commitProperties()之前被调用,这会导致不正确的行为。

从show处理程序内部设置DataGrid的dataprovider时,我注意到了这个问题。设置dataProvider会导致网格使属性和displayList无效,updateDisplayList()将首先被调用,然后是commitProperties(),这将导致网格不更新行。

问题的根源似乎是从LayoutManagers validateDisplayList()循环中调度show事件,因此从show处理程序中使子对象无效会导致立即调用updateDisplayList()。

我知道我可以在show处理程序或其他几个hacky解决方案中使用callLater()但我更愿意修复问题的根源,因为我不想每次有人使用时都找到/修复此问题显示事件和坏事发生。

我正在考虑更改调度show事件并在dispatchEvent()上使用callLater()的UIComponent.setVisible(),这样show事件就不会在验证周期中调度,除非有人有更好的想法。

<mx:Script>
    <![CDATA[
        import mx.controls.Label;

        private var tabLabel:Label;
        private function onCreationComplete():void
        {
            var ifactory:IFactory = TestLabel;
            tabLabel = Label(ifactory.newInstance());
            tab1.addChild(tabLabel);
        }


        private function onTab1Show():void
        {
            tabLabel.invalidateProperties();
            tabLabel.invalidateDisplayList();
        }

    ]]>
</mx:Script>

<mx:Component id="TestLabel">
    <mx:Label text="Test">
        <mx:Script>
            <![CDATA[

                override protected function commitProperties():void
                {
                    super.commitProperties();   
                }
                override protected function updateDisplayList(w:Number, h:Number):void
                {
                    super.updateDisplayList(w, h);                  
                }

            ]]>
        </mx:Script>
    </mx:Label>
</mx:Component>

<mx:TabNavigator height="200" width="200" creationComplete="onCreationComplete()">
    <mx:Canvas id="tab1" height="100%" width="100%" label="Tab 1" show="onTab1Show()" />
    <mx:Canvas height="100%" width="100%" label="Tab 2" />
</mx:TabNavigator>

1 个答案:

答案 0 :(得分:0)

我决定更好地解决这个问题是对LayoutManager的一个小调整。如果任何先前阶段无效,那么在知道对象验证时应该足够聪明。

这个问题可以通过几种方式表现出来。从本质上使measure()或updateDisplayList()方法中的对象无效可能导致此问题。例如,如果从measure()函数中调度事件,并且事件的处理程序碰巧使对象大小和属性无效,那么measure()函数可能会在commitProperties()可能破坏组件之前运行。

show事件是从UIComponent的updateDisplayList()中调度的,这就是它失败的原因。

我最后稍微修改了validateSize()和validateDisplayList()。添加了一些代码来检查先前阶段的无效标志,并在必要时重新使对象失效。如果由于上述条件导致对象重新失效,还会添加一个标志以强制LayoutManager立即运行另一个循环。

正如仅供参考,修改SDK通常不是一个好主意,但可以通过将SDK文件复制到项目中匹配的文件夹结构并对项目进行清理来逐个项目地完成。将使用项目中的文件而不是SDK中的文件。

private function validateSize():void
{
    // trace("--- LayoutManager: validateSize --->");

    //SDK Mod - Storage for items to be re-invalidated.
    var reInvalidate:Array = [];

    var obj:ILayoutManagerClient = ILayoutManagerClient(invalidateSizeQueue.removeLargest());
    while (obj)
    {
        // trace("LayoutManager calling validateSize() on " + Object(obj));


        //SDK Mod - Check if we need to record this item due to invalid dependencies.
        if (obj is UIComponent)
        {
            if (UIComponent(obj).mx_internal::invalidatePropertiesFlag == true)
            {
                //Record the invalid item.
                reInvalidate.push(obj);
                //Set flag so LayoutManager immediately runs another cycle
                recycleImmediately = true;
            }
        }

        obj.validateSize();
        if (!obj.updateCompletePendingFlag)
        {
            updateCompleteQueue.addObject(obj, obj.nestLevel);
            obj.updateCompletePendingFlag = true;
        }

        // trace("LayoutManager validateSize: " + Object(obj) + " " + IFlexDisplayObject(obj).measuredWidth + " " + IFlexDisplayObject(obj).measuredHeight);

        obj = ILayoutManagerClient(invalidateSizeQueue.removeLargest());
    }

    //Re-invalidate any items with invalid dependencies.
    while (reInvalidate.length > 0)
        invalidateSize(ILayoutManagerClient(reInvalidate.shift()));

    if (invalidateSizeQueue.isEmpty())
    {
        // trace("Measurement Queue is empty");

        invalidateSizeFlag = false;
    }

    // trace("<--- LayoutManager: validateSize ---");
}
private function validateDisplayList():void
{

    // trace("--- LayoutManager: validateDisplayList --->");

    //SDK Mod - Storage for items to be re-invalidated.
    var reInvalidate:Array = [];

    var obj:ILayoutManagerClient = ILayoutManagerClient(invalidateDisplayListQueue.removeSmallest());
    while (obj)
    {
        // trace("LayoutManager calling validateDisplayList on " + Object(obj) + " " + DisplayObject(obj).width + " " + DisplayObject(obj).height);



        //SDK Mod - Check if we need to record this item due to invalid dependencies.
        if (obj is UIComponent)
        {
            if (UIComponent(obj).mx_internal::invalidatePropertiesFlag == true ||
                UIComponent(obj).mx_internal::invalidateSizeFlag == true)
            {
                //Record the invalid item.
                reInvalidate.push(obj);
                //Set flag so LayoutManager immediately runs another cycle
                recycleImmediately = true;
            }
        }

        obj.validateDisplayList();
        if (!obj.updateCompletePendingFlag)
        {
            updateCompleteQueue.addObject(obj, obj.nestLevel);
            obj.updateCompletePendingFlag = true;
        }

        // trace("LayoutManager return from validateDisplayList on " + Object(obj) + " " + DisplayObject(obj).width + " " + DisplayObject(obj).height);

        // Once we start, don't stop.
        obj = ILayoutManagerClient(invalidateDisplayListQueue.removeSmallest());
    }

    //Re-invalidate any items with invalid dependencies.
    while (reInvalidate.length > 0)
        invalidateDisplayList(ILayoutManagerClient(reInvalidate.shift()));

    if (invalidateDisplayListQueue.isEmpty())
    {
        // trace("Layout Queue is empty");

        invalidateDisplayListFlag = false;
    }

    // trace("<--- LayoutManager: validateDisplayList ---");
}

然后在doPhasedInstantiation()里面 。 。

if (invalidatePropertiesFlag ||
        invalidateSizeFlag ||
        invalidateDisplayListFlag)
    {
        //SDK Mod - Check if we re-invalidated any items durring the cycle. 
        if (recycleImmediately == false)
        {
            //No items were re-invalidated, default behavior.
            callLaterObject.callLater(doPhasedInstantiation);
        }
        else
        {
            //We re-invalidated items durring the current cycle, run another cycle immediately and bail out.
            recycleImmediately = false;
            doPhasedInstantiation();
            return;
        }
    }