Flex / AS3:一个通用的单击并拖动缩放示例,除了一个问题外还可以工作

时间:2012-07-23 20:09:22

标签: actionscript-3 flex mxml

我采用了本网站的代码,

http://blog.ninjacaptain.com/2010/03/flex-chart-zoom-window/

请注意,网络博客中提到的有关显示数据提示的错误似乎已得到修复(至少从4.5.1 SDK开始)。我能看到数据提示很好。

除一个问题外,代码运行良好。我已经添加了下面的完整代码,因此您只需复制并粘贴为新的Flex应用程序并运行它。

问题就在于用户在没有拖动的情况下单击一次,这会出现以下错误(请确保在应用程序首次运行时单击它):

TypeError: Error #1009: Cannot access a property or method of a null object reference. at mx.charts.chartClasses::CartesianDataCanvas/localToData()[E:\dev\4.5.1\frameworks\projects\charts\src\mx\charts\chartClasses\CartesianDataCanvas.as:580]

有没有办法捕获用户点击时没有像这样拖动并调用某个函数来处理它?或者,任何方法来避免这种错误?感谢您的任何意见/建议。

代码是:

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
           xmlns:s="library://ns.adobe.com/flex/spark" 
           xmlns:mx="library://ns.adobe.com/flex/mx"
           initialize="init()" width="600" height="520">
<fx:Script>
    <![CDATA[
        [Bindable]
        private var profits:Array;
        private var dragStart:Point;
        private var dragEnd:Point;
        private var zooming:Boolean;

        // initializes the data provider with random data
        private function init():void{
            profits = new Array({month: 0, profit: 15});
            for(var i:int=1; i<40; i++)
                profits.push({month: i, profit: Math.round(Math.random()*25-10)+profits[i-1].profit});
        }

        // sets the start point of the zoom window
        private function startDraw(e:MouseEvent):void{
            zooming = true;
            dragStart = new Point(series1.mouseX, series1.mouseY);
        }

        // draws the zoom window as your mouse moves
        private function showDraw(e:MouseEvent):void{
            if(zooming){
                dragEnd = new Point(series1.mouseX, series1.mouseY);

                // finds the top-left and bottom-right ponits of the zoom window
                var TL:Point = new Point();  // top-left point
                var BR:Point = new Point();  // bottom-right point
                if(dragStart.x < dragEnd.x){
                    TL.x = dragStart.x;
                    BR.x = dragEnd.x;
                }
                else{
                    TL.x = dragEnd.x;
                    BR.x = dragStart.x;
                }
                if(dragStart.y < dragEnd.y){
                    TL.y = dragStart.y;
                    BR.y = dragEnd.y;
                }
                else{
                    TL.y = dragEnd.y;
                    BR.y = dragStart.y;
                }

                // prevents the zoom window from going off the canvas
                if(TL.x < 0) TL.x = 0;
                if(BR.x > chartCanvas.width-1) BR.x = chartCanvas.width-1;
                if(TL.y < 0) TL.y = 0;
                if(BR.y > chartCanvas.height-1) BR.y = chartCanvas.height-1;

                // draw the actual zoom window
                chartCanvas.graphics.clear();
                chartCanvas.graphics.lineStyle(1, 0x000000, 0.25);
                chartCanvas.graphics.beginFill(0xd4e3f0,0.5);
                chartCanvas.graphics.drawRect(TL.x, TL.y, BR.x-TL.x, BR.y-TL.y);
                chartCanvas.graphics.endFill();
            }   
        }

        // clears the drawing canvas and sets the new max/mins
        private function finishDraw(e:MouseEvent):void{
            zooming = false;
            chartCanvas.graphics.clear();

            // converts the drag coordinates into axis data points
            var chartValStart:Array = chartCanvas.localToData(dragStart);
            var chartValEnd:Array = chartCanvas.localToData(dragEnd);

            // sets the new maximum and minimum for both axes
            haxis.minimum = (chartValStart[0] < chartValEnd[0]) ? chartValStart[0] : chartValEnd[0];
            haxis.maximum = (chartValStart[0] < chartValEnd[0]) ? chartValEnd[0] : chartValStart[0];
            vaxis.minimum = (chartValStart[1] < chartValEnd[1]) ? chartValStart[1] : chartValEnd[1];
            vaxis.maximum = (chartValStart[1] < chartValEnd[1]) ? chartValEnd[1] : chartValStart[1];
        }

        // resets the axis max/mins
        private function resetZoom():void{
            haxis.minimum = NaN; 
            haxis.maximum = NaN; 
            vaxis.minimum = NaN; 
            vaxis.maximum = NaN;
        }   
]]>
</fx:Script>

<s:VGroup>

    <mx:Panel title="Line Chart">
        <mx:LineChart id="chart1" 
                      mouseDown="startDraw(event)" 
                      mouseMove="showDraw(event)" 
                      mouseUp="finishDraw(event)" 
                      width="510">

            <!-- zoom window is drawn here -->
            <mx:annotationElements>
                <mx:CartesianDataCanvas id="chartCanvas"/>
            </mx:annotationElements>

            <mx:horizontalAxis>
                <mx:LinearAxis id="haxis"/>
            </mx:horizontalAxis>

            <mx:verticalAxis>
                <mx:LinearAxis id="vaxis"/>
            </mx:verticalAxis>

            <mx:series>
                <mx:LineSeries filterData="false" id="series1" xField="month" yField="profit" 
                               displayName="Profit" dataProvider="{profits}"/>
            </mx:series>

        </mx:LineChart>
    </mx:Panel>

    <mx:Button label="Reset Zoom" click="resetZoom()" />

</s:VGroup>

</s:Application>

更新:

这是解决方案,以防对其他人有用。我添加了一个if语句来检查null dragStart和dragEnd值,如下面的答案所述。此外,我已经删除了线条序列上默认弹奏位置的阴影,因此如果用户选择的缩放区域太小,则不会出现警告。

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
           xmlns:s="library://ns.adobe.com/flex/spark" 
           xmlns:mx="library://ns.adobe.com/flex/mx"
           initialize="init()" width="600" height="520">
<fx:Script>
    <![CDATA[
        [Bindable]
        private var profits:Array;
        private var dragStart:Point;
        private var dragEnd:Point;
        private var zooming:Boolean;

        // initializes the data provider with random data
        private function init():void{
            profits = new Array({month: 0, profit: 15});
            for(var i:int=1; i<40; i++)
                profits.push({month: i, profit: Math.round(Math.random()*25-10)+profits[i-1].profit});
        }

        // sets the start point of the zoom window
        private function startDraw(e:MouseEvent):void{
            zooming = true;
            dragStart = new Point(series1.mouseX, series1.mouseY);
        }

        // draws the zoom window as your mouse moves
        private function showDraw(e:MouseEvent):void{
            if(zooming){
                dragEnd = new Point(series1.mouseX, series1.mouseY);

                // finds the top-left and bottom-right ponits of the zoom window
                var TL:Point = new Point();  // top-left point
                var BR:Point = new Point();  // bottom-right point
                if(dragStart.x < dragEnd.x){
                    TL.x = dragStart.x;
                    BR.x = dragEnd.x;
                }
                else{
                    TL.x = dragEnd.x;
                    BR.x = dragStart.x;
                }
                if(dragStart.y < dragEnd.y){
                    TL.y = dragStart.y;
                    BR.y = dragEnd.y;
                }
                else{
                    TL.y = dragEnd.y;
                    BR.y = dragStart.y;
                }

                // prevents the zoom window from going off the canvas
                if(TL.x < 0) TL.x = 0;
                if(BR.x > chartCanvas.width-1) BR.x = chartCanvas.width-1;
                if(TL.y < 0) TL.y = 0;
                if(BR.y > chartCanvas.height-1) BR.y = chartCanvas.height-1;

                // draw the actual zoom window
                chartCanvas.graphics.clear();
                chartCanvas.graphics.lineStyle(1, 0x000000, 0.25);
                chartCanvas.graphics.beginFill(0xd4e3f0,0.5);
                chartCanvas.graphics.drawRect(TL.x, TL.y, BR.x-TL.x, BR.y-TL.y);
                chartCanvas.graphics.endFill();
            }   
        }

        // clears the drawing canvas and sets the new max/mins
        private function finishDraw(e:MouseEvent):void{
            zooming = false;
            chartCanvas.graphics.clear();
           if (dragStart && dragEnd) {  // Solution to original posted quesion
            // converts the drag coordinates into axis data points
            var chartValStart:Array = chartCanvas.localToData(dragStart);
            var chartValEnd:Array = chartCanvas.localToData(dragEnd);

            // sets the new maximum and minimum for both axes
            haxis.minimum = (chartValStart[0] < chartValEnd[0]) ? chartValStart[0] : chartValEnd[0];
            haxis.maximum = (chartValStart[0] < chartValEnd[0]) ? chartValEnd[0] : chartValStart[0];
            vaxis.minimum = (chartValStart[1] < chartValEnd[1]) ? chartValStart[1] : chartValEnd[1];
            vaxis.maximum = (chartValStart[1] < chartValEnd[1]) ? chartValEnd[1] : chartValStart[1];
                       }
                       // reset values for next time
                       dragStart=null;
                       dragEnd=null;
        }

        // resets the axis max/mins
        private function resetZoom():void{
            haxis.minimum = NaN; 
            haxis.maximum = NaN; 
            vaxis.minimum = NaN; 
            vaxis.maximum = NaN;
        }   
]]>
</fx:Script>

<s:VGroup>

    <mx:Panel title="Line Chart">
        <mx:LineChart id="chart1" 
                      mouseDown="startDraw(event)" 
                      mouseMove="showDraw(event)" 
                      mouseUp="finishDraw(event)" 
                      width="510">

            <!-- zoom window is drawn here -->
            <mx:annotationElements>
                <mx:CartesianDataCanvas id="chartCanvas"/>
            </mx:annotationElements>

            <mx:horizontalAxis>
                <mx:LinearAxis id="haxis"/>
            </mx:horizontalAxis>

            <mx:verticalAxis>
                <mx:LinearAxis id="vaxis"/>
            </mx:verticalAxis>

            <mx:series>
                <mx:LineSeries filterData="false" id="series1" xField="month" yField="profit" 
                               displayName="Profit" dataProvider="{profits}"/>
            </mx:series>

            <mx:seriesFilters>
                <fx:Array/>
            </mx:seriesFilters>

        </mx:LineChart>
    </mx:Panel>

    <mx:Button label="Reset Zoom" click="resetZoom()" />

</s:VGroup>

</s:Application>

1 个答案:

答案 0 :(得分:1)

您收到此错误,因为如果用户只是单击鼠标,则永远不会设置变量dragEnd。最简单的方法是检查null函数中的finishDraw值:

private function finishDraw(e:MouseEvent):void
{
  zooming = false;
  chartCanvas.clear();

  if(dragStart && dragEnd)
  {
    //your stuff here
    //...

  }

  //reset values for next time
  dragStart=null;
  dragEnd=null;
}

这应该避免任何进一步的 1009错误。请注意,如果我拖动一个微小的缩放窗口然后释放鼠标,我会收到一些警告,因为Flash对DisplayObject的大小有限制,因此您还应该验证缩放窗口的尺寸。

希望这有帮助!