0

我遇到了位于 HBox 内的画布的尺寸问题。似乎“_graphic”、“_border”和“_fill”画布(在 com.example.ThingRenderer.mxml 中)没有与渲染器内的所有其他测量值同时进行测量。但是,此问题仅在第一次通过时观察到。请参阅图像以获得视觉效果... 第一张图像显示了应用程序完成加载后的状态。Populate第二张图片代表点击按钮后屏幕的样子。第三张图片显示了当步进器增加时会发生什么。问题是,如何将数据填充到表格中后,第三张图像中的绘图不会被渲染?

渲染器测试.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;
        }
    }
}

应用程序完成加载后的初始外观

单击填充按钮后

步进器从 0.30 增加到 0.40 后

4

1 回答 1

2

这一切的发生是因为你不能在 updateDisplayList 中使用 width 和 height 属性,它们还没有更新。制作单独的组件(例如 ThingProgressBar)并将绘图逻辑放入其中,这将解决所有问题:

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 会覆盖渲染器的样式表。
于 2012-05-10T14:31:30.733 回答