如何在Flex 3中的渲染器内测量mx:Canvas?

时间:2012-05-09 21:01:09

标签: flex flex3

我对位于HBox内部的画布存在尺寸问题。好像" _graphic"," _border"和" _fill"画布(在com.example.ThingRenderer.mxml中)不会与渲染器内的所有其他测量值同时进行测量。但是,只有在第一次传递时才会出现此问题。请参阅图像以获得可视化...第1张图显示了完成加载后应用的状态。第二个图像表示单击Populate按钮后屏幕的外观。第3张图显示了步进器增加时会发生什么。问题是,一旦将数据填充到表格中,第三张图像中的绘图是如何渲染的?

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

1 个答案:

答案 0 :(得分: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会覆盖渲染器的样式表。