0

有人请知道,为什么我会收到运行时错误:

RangeError: Error #1125: The index 0 is out of range 0.
    ........
    at Popup/update()[Popup.mxml:80]
    at PopupTest/showPopup()[PopupTest.mxml:45]
    at PopupTest/___btn_click()[PopupTest.mxml:52]

调用函数时:

private function showPopup(event:MouseEvent):void {
    _popup.update(new Array('Pass' , 
        '6♠', '6♣', '6♦', '6♥', '6 x', 
        '7♠', '7♣', '7♦', '7♥', '7 x', 
        '8♠', '8♣', '8♦', '8♥', '8 x', 
        '9♠', '9♣', '9♦', '9♥', '9 x', 
        '10♠', '10♣', '10♦', '10♥', '10 x'), true, 80);
}

好像我的 _list 根本没有条目(但为什么?我确实分配了 _data.source=args),因此_list.ensureIndexIsVisible(0)调用将在第 80 行失败:

<?xml version="1.0" encoding="utf-8"?>
<s:Panel xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx"
    width="220" height="200"
    initialize="init(event)">   

    <fx:Script>
        <![CDATA[
            import mx.collections.ArrayList;
            import mx.events.FlexEvent;
            import mx.utils.ObjectUtil;

            private static const FORCE:uint = 20;

            [Bindable]
            private var _data:ArrayList = new ArrayList();

            private var _timer:Timer = new Timer(1000, 120);

            private function init(event:FlexEvent):void {
                _timer.addEventListener(TimerEvent.TIMER, timerUpdated);
                _timer.addEventListener(TimerEvent.TIMER_COMPLETE, timerCompleted);
            }

            public function close():void {
                _timer.reset();
                _data.source = null;
                visible = false;
            }

            private function timerUpdated(event:TimerEvent=null):void {
                var seconds:int = _timer.repeatCount - _timer.currentCount;
                title = 'Your turn! (' + seconds + ')';
                // show panel for cards too
                if (seconds < FORCE)
                    visible = true;
            }

            private function timerCompleted(event:TimerEvent=null):void {
                title = 'Your turn!';
                close();
            }

            public function update(args:Array, bidding:Boolean, seconds:int):void {
                if (seconds <= 0) {
                    close();
                    return;
                }

                // nothing has changed
                if (ObjectUtil.compare(_data.source, args, 0) == 0)
                    return;
                _data.source = args;

                if (args == null || args.length == 0) {
                    close();
                    return;
                }

                if (seconds < FORCE || bidding)
                    visible = true;

                _timer.reset();

                title = 'Your turn! (' + seconds + ')';
                _list.ensureIndexIsVisible(0); // the line 80
                _timer.repeatCount = seconds;
                _timer.start();
            }
        ]]>
    </fx:Script>

    <s:VGroup paddingLeft="10" paddingTop="10" paddingRight="10" paddingBottom="10" gap="10" width="100%" height="100%">
        <s:List id="_list" dataProvider="{_data}" width="100%" height="100%" fontSize="24" itemRenderer="RedBlack" />
    </s:VGroup>
</s:Panel>
4

1 回答 1

1

the reason

You are adding the new array allright, but then the List starts creating ItemRenderers based on the items that are in that array. This takes some time and happens asynchronously. In the meantime you're saying "show me item 1", but the ItemRenderer for item 1 doesn't exist yet. It will very soon, but not right now. That's why you get an indexoutofrange error.

the solution

You have to be sure the List is done creating ItemRenderers before you call that method. The easiest way to solve this situation - though definitely not the cleanest - is to just wait until the next render cycle by using the infamous callLater().

callLater(_list.ensureIndexIsVisible, [0]);

This essentially saying: wait for the next render cycle and then call ensureIndexIsVisible() on _list with parameter 0.

(On a side note: if you really only want index 0 this whole thing is rather pointless, because I think a List scrolls back to the top when its dataprovider is changed anyway)

a cleaner solution

You can listen on the List for the RendererExistenceEvent#RENDERER_ADD event. This will be dispatched whenever a new ItemRenderer was added to the list and it holds a reference to the item's index in the List, the data and the ItemRenderer itself. However in your case we only need the 'index'. Whenever an ItemRenderer is added at index 0 we'll scroll back to the top:

_list.addEventListener(RendererExistenceEvent.RENDERER_ADD, onRendererAdded);

private function onRendererAdded(event:RendererExistenceEvent):void {
    if (event.index == 0) myList.ensureIndexIsVisible(0);
}

This will immediately scroll to the top when the first ItemRenderer is added and doesn't need to wait until all of them are ready.

于 2012-03-03T21:51:51.923 回答