ArayList как dataProvider для списка: индекс 0 находится вне диапазона 0 - PullRequest
0 голосов
/ 03 марта 2012

Кто-нибудь, пожалуйста, есть идея, почему я получаю ошибку времени выполнения:

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 Ответ

1 голос
/ 04 марта 2012

причина

Вы добавляете новый массив в порядке, но затем List начинает создавать ItemRenderers на основе элементов, которые находятся в этом массиве.Это занимает некоторое время и происходит асинхронно.В то же время вы говорите «покажи мне пункт 1», но ItemRenderer для элемента 1 еще не существует.Это будет очень скоро, но не сейчас.Вот почему вы получаете ошибку indexoutofrange.

решение

Вы должны быть уверены, что List завершил создание ItemRenderers, прежде чем вызывать этот метод.Самый простой способ разрешить эту ситуацию - хотя, безусловно, не самый чистый - это просто подождать до следующего цикла рендеринга, используя печально известный callLater () .

callLater(_list.ensureIndexIsVisible, [0]);

По сути, это говорит:дождитесь следующего цикла рендеринга и затем вызовите ensureIndexIsVisible() on _list с параметром 0.

(С другой стороны: если вы действительно хотите только индекс 0, то все это довольно бессмысленно, потому что ядумаю, что список прокручивается обратно наверх, когда его поставщик данных все равно изменяется)

более чистое решение

Вы можете прослушать список для RendererExistenceEvent # RENDERER_ADD событие.Это будет отправлено всякий раз, когда новый ItemRenderer был добавлен в список, и он содержит ссылку на индекс элемента в Списке, данные и сам ItemRenderer.Однако в вашем случае нам нужен только «индекс».Всякий раз, когда ItemRenderer добавляется с индексом 0, мы прокручиваем обратно к вершине:

_list.addEventListener(RendererExistenceEvent.RENDERER_ADD, onRendererAdded);

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

Это сразу же прокручивается к вершине, когда добавляется первый ItemRenderer, и не нужно ждать, пока всеготовы.

...