2012-05-09 29 views
0

我有一個大小問題與位於HBox內的畫布。看起來「_graphic」,「_border」和「_fill」畫布(在com.example.ThingRenderer.mxml中)與渲染器內的所有其他度量值不同時被測量。但是,只有在第一次傳遞時纔會看到此問題。請參閱圖像以獲得可視化效果...第1張圖顯示應用完成加載後的狀態。第二張圖像代表點擊Populate按鈕後屏幕的外觀。第三張圖顯示了步進器增加時發生的情況。問題是,一旦數據填充到表格中後,第三張圖像中的圖形如何不會呈現?如何在Flex 3中的渲染器內測量mx:Canvas?

RendererTest.mxml

<?xml version="1.0" encoding="utf-8"?> 
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" 
    layout="absolute" 
    creationComplete="handleCreationComplete(event)" 
> 
    <mx:Script> 
     <![CDATA[ 
      import com.example.Thing; 

      import mx.collections.ArrayCollection; 
      import mx.events.FlexEvent; 
      import mx.events.NumericStepperEvent; 

      private const _thingProvider:ArrayCollection = new ArrayCollection(); 
      private var _thing1:Thing; 

      protected function handleCreationComplete(event:FlexEvent):void { 
       _thing1 = new Thing("thingy", 0xff0000, 0.3); 
       _stepper.value = _thing1.ratio; 
      } 

      protected function handlePopulateClick(event:MouseEvent):void { 
       _thingProvider.addItem(_thing1); 
      } 

      protected function handleStepperChange(event:NumericStepperEvent):void { 
       _thing1.ratio = event.value; 
      } 

     ]]> 
    </mx:Script> 
    <mx:VBox> 
     <mx:Button label="Populate" click="handlePopulateClick(event)" /> 
     <mx:NumericStepper id="_stepper" minimum="0" maximum="1" stepSize="0.01" change="handleStepperChange(event)" /> 
     <mx:AdvancedDataGrid dataProvider="{_thingProvider}" variableRowHeight="true" width="100%" height="100%"> 
      <mx:columns> 
       <mx:AdvancedDataGridColumn headerText="Name" dataField="name" /> 
       <mx:AdvancedDataGridColumn headerText="Display" 
        width="150" sortable="false" 
        itemRenderer="com.example.ThingRenderer" 
       /> 
      </mx:columns> 
     </mx:AdvancedDataGrid> 
    </mx:VBox> 
</mx:Application> 

com.exampleThingRenderer.mxml

<?xml version="1.0" encoding="utf-8"?> 
<mx:Canvas 
    xmlns:mx="http://www.adobe.com/2006/mxml" 

    width="100%" 
    horizontalScrollPolicy="off" verticalScrollPolicy="off" 
> 
    <mx:Script> 
     <![CDATA[ 
      import mx.binding.utils.ChangeWatcher; 

      private var _thing:Thing; 
      private var _ratioWatcher:ChangeWatcher; 

      private var _doClearContent:Boolean; 
      private var _doDrawBorder:Boolean; 
      private var _doUpdateFill:Boolean; 

      override public function set data(value:Object):void { 
       if(value && value is Thing) { 
        _thing = Thing(value); 
        if(_ratioWatcher) { 
         _ratioWatcher.unwatch(); 
        } 
        _ratioWatcher = ChangeWatcher.watch(_thing, "ratio", handleRatioChanged); 

        _doClearContent = false; 
        _doDrawBorder = true; 
        _doUpdateFill = true; 
        _graphic.invalidateSize(); 
        _border.invalidateSize(); 
       } 
       else { 
        _doClearContent = true; 
        _doDrawBorder = false; 
        _doUpdateFill = false; 
       } 
       super.data = value; 
      } 

      private function handleRatioChanged(event:Event):void { 
       _doUpdateFill = true; 
       invalidateDisplayList(); 
      } 

      override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void { 
       if(_doClearContent) { 
        _container.visible = false; 
        _container.includeInLayout = false; 
        _doClearContent = false; 
       } 

       super.updateDisplayList(unscaledWidth, unscaledHeight); 

       if(_doDrawBorder) { 
        trace("_thingContainer.width="+_container.width, "_thingGraphic.width="+_graphic.width, "_thingBorder.width="+_border.width); 
        _border.graphics.clear(); 
        _border.graphics.moveTo(0, 0); 
        _border.graphics.lineStyle(1, _thing.color); 
        _border.graphics.lineTo(_border.width, 0); 
        _border.graphics.lineTo(_border.width, _border.height); 
        _border.graphics.lineTo(0, _border.height); 
        _border.graphics.lineTo(0, 0); 

        _doDrawBorder = false; 
       } 

       if(_doUpdateFill) { 
        _percentage.text = Math.round(_thing.ratio * 100.0) + "%"; 
        _fill.graphics.clear(); 
        _fill.graphics.beginFill(_thing.color); 
        _fill.graphics.drawRect(0, 0, _fill.width * _thing.ratio, _fill.height); 

        _doUpdateFill = false; 
       } 
      } 
     ]]> 
    </mx:Script> 

    <mx:HBox id="_container" width="100%" paddingLeft="5" paddingTop="5" paddingRight="5" paddingBottom="5"> 
     <mx:Label id="_percentage" width="45" /> 
     <mx:Canvas id="_graphic" width="100%" height="15"> 
      <mx:Canvas id="_border" x="0" y="0" width="100%" height="100%" /> 
      <mx:Canvas id="_fill" x="0" y="0" width="100%" height="100%" /> 
     </mx:Canvas> 
    </mx:HBox> 
</mx:Canvas> 

com.example.Thing.as

package com.example { 
    public class Thing { 
     [Bindable] public var name:String; 
     [Bindable] public var color:uint; 
     [Bindable] public var ratio:Number; 

     public function Thing(name:String, color:uint, ratio:Number) { 
      this.name = name; 
      this.color = color; 
      this.ratio = ratio; 
     } 
    } 
} 

Initial look when app has finished loading

After Populate button is clicked

After stepper is incremented from 0.30 to 0.40

+0

我玩了大約30分鐘,感到沮喪。我認爲_fill.width和__border.width回到0.我不知道爲什麼它會這樣做。 – Dom

+0

因爲列表組件(和子類)設置explicitWidth,而不是寬度(由於某種原因)。 –

回答

2

這一切是因爲你不能在使用的updateDisplayList width和height屬性,它們還未被更新。製作單獨的組件(例如ThingProgressBar),並把繪製logick裏面,這將解決一切:

package 
{ 
import mx.core.UIComponent; 

public class ThingProgressBar extends UIComponent 
{ 
    private var _ratio:Number; 
    public function get ratio():Number 
    { 
     return _ratio; 
    } 
    public function set ratio(value:Number):void 
    { 
     _ratio = value; 
     invalidateDisplayList(); 
    } 

    override protected function updateDisplayList(
        unscaledWidth:Number, unscaledHeight:Number):void 
    { 
     super.updateDisplayList(unscaledWidth, unscaledHeight); 
     graphics.clear(); 
     if (unscaledWidth > 0 && unscaledHeight > 0) 
     { 
      graphics.lineStyle(1, 0xFF0000); 
      graphics.drawRect(0, 0, unscaledWidth, unscaledHeight); 

      graphics.beginFill(0xFF0000); 
      graphics.drawRect(0, 0, unscaledWidth * ratio, unscaledHeight); 
      graphics.endFill(); 
     } 
    } 
} 
} 

所以您的渲染器可能是這樣的:

<mx:HBox 
xmlns:fx="http://ns.adobe.com/mxml/2009" 
xmlns:mx="library://ns.adobe.com/flex/mx" 
horizontalScrollPolicy="off" verticalScrollPolicy="off" xmlns:local="*" 
> 
<fx:Script> 
    <![CDATA[ 
     [Bindable] private var _thing:Thing; 

     override public function set data(value:Object):void 
     { 
      _thing = value as Thing; 
      super.data = value; 
     } 
    ]]> 
</fx:Script> 

<mx:HBox width="100%" 
      paddingLeft="5" paddingTop="5" 
      paddingRight="5" paddingBottom="5"> 
    <mx:Label text="{_thing.name}" width="45" /> 
    <local:ThingProgressBar width="100%" height="15" 
            ratio="{_thing.ratio}"/> 
</mx:HBox> 
</mx:HBox> 
  1. 我刪除觀察者。觀察者綁定被認爲是不好的做法,請使用mxml綁定或事件。
  2. 我刪除了兩個分開的邊框和填充畫布 - 他們可以合併在一起。
  3. 我用UIComponent而不是Canvas。除非你需要佈局,否則不要使用容器,它們很重。
  4. 我在渲染器中使用了HBox而不是Canvas,因爲我喜歡盒子更多:)但是如果自List需要自定義樣式覆蓋渲染器的樣式表,您無法避免在渲染器中使用第二個容器。
+0

我可以看到你說的是一個合法的解決方案,但在updateDisplayList函數中,包含mx:Canvas的_container(包含mx:Canvas的mx:HBox)的大小如何?這是令人困惑和誤導 - 一些組件變大,有些組件不變......任何想法爲什麼? – Andrey

+0

@Andrey,在BoxLayout或Canvas裏面看看,它們在更新期間使用兩對函數和屬性:getExplicitOrMeasuredWidth/Heights()和percentWidth/Height,而不是寬度/高度本身。當容器佈置子項時,它不會影響它的孫輩,而是在LayoutManager中排隊等待佈局,並且稍後會更新(甚至可能在下一幀中)。因此,當您嘗試以某種方式檢查updateDisplayList中孫的大小時,它將保持爲零或以前的值。 – moropus

+0

現在這一切都很有意義。謝謝你的回答! – Andrey