2011-10-20 21 views
0

只是想知道是否有人遇到過這個問題,並且有很好的解決方法。Flex 3 - 顯示事件處理程序失效破壞生命週期

下面是如何重現:

創建標籤導航器(或viewstack,等等),並添加一對夫婦的標籤。

在您的選項卡上添加一個顯示事件處理程序。在事件處理程序內部,在選項卡的其中一個子項中調用invalidateProperties()和invalidateDisplayList()。在孩子的commitProperties()和updateDisplayList()上放置一個斷點。您會注意到updateDisplayList()在commitProperties()之前被調用,導致不正確的行爲。

從show handler中設置DataGrid的dataprovider時,我發現這個問題。設置dataProvider將導致網格使屬性和displayList無效,updateDisplayList()將首先被調用,然後commitProperties()將導致網格不更新行。

看來問題的根源在於顯示事件從LayoutManagers validateDisplayList()循環內調度,因此在show handler中使子對象失效導致其updateDisplayList()立即被調用。

我知道我可以在show handler或其他一些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> 
+0

是的,它是不尋常的,但它確實有效。此外,考慮到這個錯誤會影響任何支持show事件的組件的孩子,這是一個巨大的問題,我不知道有什麼方法可以在不改變SDK的情況下在全局範圍內修復它。 –

+0

向我們展示一些代碼來重現,我會看看。 – JeffryHouser

+0

你是否想讓我把它的斷點給你呢?您必須分解commitProperties()和updateDisplayList()才能看到問題。我不認爲這種情況下的代碼是必要的,因爲這個問題很容易重現,並且可以使用任何支持show事件的組件來完成。只需添加一個子節點和一個顯示處理程序,即可使顯示處理程序中的子節點失效,並觀察它在子節點上調用commitProperties和updateDisplayList的順序。 –

回答

0

我決定更好的解決這個問題,是對LayoutManager的一個小調整。如果以前的任何一個階段都是無效的,那麼在驗證對象時應該足夠聰明。

這個問題可以通過幾種方式表現出來。從measure()或updateDisplayList()方法實質上使對象失效可能會導致此問題。例如,如果事件從measure()函數內發出,並且事件的處理程序發生使對象大小和屬性無效的情況,則measure()函數可能會在commitProperties()潛在地破壞組件之前運行。

show事件從UIComponent的updateDisplayList()中調度,這就是爲什麼它會受到此失敗的影響。

我最終修改了validateSize()和validateDisplayList()。增加了一些代碼來檢查以前階段的無效標誌,並在必要時重新使對象無效。還添加了一個標誌,以強制LayoutManager在由於上述情況而導致對象重新失效時立即運行另一個週期。

就像一個FYI,修改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; 
     } 
    }