ArrayList作为List的dataProvider:索引0超出范围0

时间:2012-03-03 18:47:40

标签: flex flex4 arraylist flex4.5 flex4.6

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

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>

1 个答案:

答案 0 :(得分:1)

原因

您正在添加新阵列,但是List会根据该阵列中的项目开始创建ItemRenderers。这需要一些时间并且异步发生。在此期间,您说“显示项目1”,但项目1的ItemRenderer尚不存在。它很快就会发生,但现在不行。这就是你得到indexoutofrange错误的原因。

解决方案

在调用该方法之前,您必须确保List已完成创建ItemRenderers。解决这种情况的最简单方法 - 尽管绝对不是最干净的 - 是通过使用臭名昭着的callLater()等到下一个渲染周期。

callLater(_list.ensureIndexIsVisible, [0]);

这主要是说:等待下一个渲染周期,然后使用参数ensureIndexIsVisible()_list上调用0

(旁注:如果你真的只想要索引0,那么整个事情就毫无意义了,因为我认为List无论如何都会在其数据提供者改变时滚动到顶部)

更清洁的解决方案

您可以在列表中收听RendererExistenceEvent#RENDERER_ADD事件。每当将新的ItemRenderer添加到列表中并且它在List,数据和ItemRenderer本身中保存对项目索引的引用时,将调度此方法。但是在你的情况下我们只需要'索引'。每当在索引0处添加ItemRenderer时,我们将滚动回到顶部:

_list.addEventListener(RendererExistenceEvent.RENDERER_ADD, onRendererAdded);

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

当添加第一个ItemRenderer时,这将立即滚动到顶部,并且不需要等到所有这些都准备就绪。