Как реализовать бесконечный список в Flex (герой)

Я новичок в Flex / ActionScript (до сих пор моей основной игровой площадкой была .NET / Java). Я пытаюсь создать приложение Flex, у которого есть список, который должен выглядеть и вести себя как бесконечный список (элементов - который может быть любым). Идея состоит в том, что пользователь должен иметь возможность прокручивать вверх или вниз и никогда не доходить до конца списка в любом направлении.

Примером может служить список чисел. Прокрутка вверх покажет отрицательные числа; прокрутка вниз покажет положительные. Теперь мой список представляет собой простой список Flex Spark (с использованием Flex Hero). Он привязан к поставщику данных, который является ArrayList.

Моя первоначальная идея заключалась в том, чтобы прослушивать событие прокрутки и добавлять / удалять элементы по мере необходимости. Однако в текущей сборке Flex Hero есть ошибка, которая иногда не вызывает события прокрутки для вертикальных полос прокрутки (http://bugs.adobe.com/jira/browse/SDK-26533).

Итак, я использую обходной путь, предложенный в приведенной выше ссылке (например, прослушивание события propertyChanged окна просмотра прокрутки списка. Событие, однако, дает мне только текущее значение verticalScrollPosition.

И похоже, что список искр по умолчанию поддерживает плавную прокрутку и вызывает событие много раз, прежде чем прокрутка списка остановится (это хороший визуальный эффект).

Теперь мне нужно:

  1. Выясните, прокручивается ли он вверх или вниз (как мне это сделать?)
  2. Выясните, какие элементы видны. Я могу получить это из:

    list.dataGroup.getItemIndicesInView()

  3. Добавляйте / удаляйте элементы по мере необходимости, чтобы пользователь мог бесконечно прокручивать вверх и вниз, никогда не доходя до конца списка ни в одном направлении.

Я пробовал следующий код, но он не работает. (комментарии в коде).

Есть какие-нибудь эксперты по Flex? Пожалуйста помоги.


        import mx.collections.ArrayList;
        import mx.core.INavigatorContent;
        import mx.events.FlexEvent;
        import mx.events.PropertyChangeEvent;

        var listData:ArrayList;
        var firstItemInView:int = 0;
        var lastItemInView:int = 0;
        var count = 0;

                    //gets the currently visible item indices (first and last)
        private function getVisibleIndices():Vector.<int> { 
            var ind:Vector.<int> = new Vector.<int>(2, true);
            ind[0] = firstItemInView;
            ind[1] = lastItemInView;
            return ind;
        }

        protected function view_creationCompleteHandler(event:FlexEvent):void {

                            //create an initialise list data
            listData = new ArrayList();
            for(var i:int = 0; i < 6; i++){
                listData.addItemAt(i, i);
            }
            infiniteList.dataProvider = listData;

            updateIndices();

            infiniteList.scroller.viewport.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, infiniteList_VerticalScroll_PropertyChanged);              
        }

                    //get the indices visible from the list's data group
        private function getNewIndices():Vector.<int> {
            var indices:Vector.<int> = new Vector.<int>(2, true);
            var indicesInView:Vector.<int> = infiniteList.dataGroup.getItemIndicesInView();
            if (indicesInView.length > 0){
                indices[0] = indicesInView[0];
            } 
            if (indicesInView.length > 1){
                indices[1] = indicesInView[indicesInView.length - 1];
            }
            return indices;
        }

        private function updateIndices():void {
            var indices:Vector.<int> = getNewIndices();
            if (indices.length > 0){
                firstItemInView = indices[0];
                if (indices.length > 1){
                    lastItemInView = indices[1];
                }
            }
        }

        protected function leftCalendar_VerticalScroll_PropertyChanged(event:PropertyChangeEvent):void {


            switch (event.property){
                case "verticalScrollPosition":

                    var indices:Vector.<int> = getNewIndices();
                    var oldIndices:Vector.<int> = getVisibleIndices();

                    var newNum:Number;


                    if (indices[1] - indices[0] == 2 && (oldIndices[0] != indices[0] && oldIndices[1] != indices[1])){
                        //a new item is in view. did we scroll up or down?
                        if (oldIndices[0] < indices[0]){
                            count++;
                            trace(count + " up : old[" + oldIndices[0] + "," + oldIndices[1] + "], new[" + indices[0] + "," + indices[1] + "]");
                            //newNum = Number(listData.getItemAt(listData.length - 1)) + 1;
                            //trace("new number to add: " + newNum);
                            //trace("todo remove: " + listData.getItemAt(0));
                            fixItems({ addAt : "top", removeAt : "bottom", newValue : newNum});

                        } else {
                            trace("down : old[" + oldIndices[0] + "," + oldIndices[1] + "], new[" + indices[0] + "," + indices[1] + "]");                               
                            fixItems({ addAt : "bottom", removeAt : "top", newValue : newNum});
                        }

                        //update indices:
                        updateIndices();
                        var newOnes = getVisibleIndices(); //seems to be getting the new ones, but the next occurance of this event handler doesn't pick up the new values! why?
                        trace(count + " current[" + newOnes[0] + ", " + newOnes[1] + "]");
                    }

                    break;
            }
        }

        protected function fixItems(data:Object):void {
            var item:Object;

            //add a new item
            if (data.addAt == "top"){
                listData.addItemAt(data.newValue, 0);
            } else {
                listData.addItem(data.newValue);
            }

            //remove one of the existing ones
            if (data.removeAt == "top"){
                item = listData.getItemAt(0);
                trace("removing " + item);
                listData.removeItemAt(0);
            } else {
                item = listData.getItemAt(listData.length - 1);
                trace("removing " + item);
                listData.removeItemAt(listData.length - 1);
            }
            updateIndices();
        }


person Krishna    schedule 12.04.2011    source источник


Ответы (3)


Вы не можете использовать список. Для этого вам придется создать свой собственный компонент с нуля. Все известные мне компоненты Flex используют конечный dataProvider для отображения информации. Если вы хотите бесконечность, вам нужно создать свой собственный компонент, который может обрабатывать диапазон (или вообще ничего), отображать его соответствующим образом и прокручивать его. Обязательно очистите все элементы, которые больше не отображаются (или используйте их повторно), потому что это будет серьезной утечкой памяти.

person J_A_X    schedule 12.04.2011
comment
^^^ что он сказал. Вам наверняка потребуется реализовать свою собственную логику прокрутки. - person drkstr; 14.04.2011
comment
Спасибо, я тоже пришел к такому же выводу. Самая большая проблема (до сих пор) заключалась в том, чтобы убрать предметы, которые не были в поле зрения. - person Krishna; 16.04.2011
comment
Вам следует изучить жизненный цикл компонента. Для этого есть много ресурсов в Интернете. Вы также можете ознакомиться с серией статей о Flex за неделю. - person J_A_X; 17.04.2011

Я думаю, вам нужно создать свой настраиваемый элемент управления List, который распространяет элемент arraycollection / arraylist для пользовательского интерфейса, ArrayCollection / Array / ArrayList следует использовать только для хранения данных, таких как список элементов лайнера, а элемент управления списком должен отображать элементы по кругу

ИЗМЕНИТЬ Я пытаюсь ответить на ваш вопрос, изменяя данные в списке и направление прокрутки, надеюсь, это поможет

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
    <mx:Script>
        <![CDATA[
            import mx.controls.Alert;
            import mx.events.ScrollEvent;

            [Bindable]
            private var arr:Array = new Array(0,1,2,3,4,5);

            private var lastScrollPostion:int =0;
            private var isScrollingUp:Boolean = false;
            private function ltClicked(event:MouseEvent):void
            {
                arr.unshift(arr[0]-1);
                lst.dataProvider = arr;
            }
            private function rtClicked(event:MouseEvent):void
            {
                arr.push(arr[arr.length-1] +1); 
                lst.dataProvider = arr;
            }

            private function scrolled(event:ScrollEvent):void
            {
                if (lastScrollPostion < event.position)
                {
                    isScrollingUp = false;
                }
                else
                {
                    isScrollingUp = true;
                }
                lastScrollPostion = event.position;
                Alert.show("isScrollingUp : "+isScrollingUp.toString());
            }

        ]]>
    </mx:Script>
    <mx:VBox>
        <mx:List 
            id="lst" 
            width="100%" 
            dataProvider="{arr}"
            scroll="{scrolled(event)}"
            />
        <mx:HBox>
            <mx:Button id="lt" label="&lt;&lt;" click="{ltClicked(event)}"/>
            <mx:Button id="rt" label="&gt;&gt;" click="{rtClicked(event)}"/>
        </mx:HBox>

    </mx:VBox>


</mx:Application>

ПРИМЕЧАНИЕ Функции ltClicked и rtClicked изменяют данные списка, а прокрутка используется для перехода к направлению.

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

person Imran    schedule 12.04.2011
comment
Не уверен, что это сработает. Как бы вы отобразили список целых чисел от бесконечности до бесконечности, используя список? - person Krishna; 12.04.2011
comment
Для целых чисел должно быть рассчитано значение, рассчитано и обновлено при нажатии пользователем. и новое значение - push / unshift в массиве - person Imran; 12.04.2011
comment
Спасибо, но обновление должно происходить при прокрутке, а не при нажатии пользователем. Также поможет реализация / пример. Я не борюсь с концепцией / идеей / логикой. Мне нужна реализация и фактические API, поскольку я новичок в платформе Flex, но я сделал много .NET / Silverlight. - person Krishna; 12.04.2011
comment
Еще раз спасибо, но я использую Flex 4.5 Spark List, а не список mx. Список искр дает мне эффект плавной анимированной прокрутки. (Я создаю приложение для планшетного устройства - Blackberry Playbook, поэтому требуется эффект прокрутки). Я использую очень похожую на вашу логику, но работает не так, как я хочу. - person Krishna; 12.04.2011
comment
хорошо, вы пытаетесь реализовать список типа списка контактов, если да, вы можете использовать положение прокрутки для перемещения прокрутки вверх и вниз - person Imran; 12.04.2011
comment
Мне пришлось принять ответ другого парня. Ваша идея была правильной, но я не могу использовать этот код, учитывая, что мой вопрос касался использования элементов управления Flex Spark, а не старых mx. - person Krishna; 18.04.2011

Можно попробовать класс InfiniteScrollList ::

package components
{

import model.InfiniteListModel;
import model.LoadingVO;

import mx.core.ClassFactory;
import mx.events.PropertyChangeEvent;

import spark.components.IconItemRenderer;
import spark.components.List;

import views.itemRenderer.LoadingItemRenderer;

public class InfiniteScrollList extends List
{
    override protected function createChildren():void
    {
        super.createChildren();
        scroller.viewport.addEventListener( PropertyChangeEvent.PROPERTY_CHANGE, propertyChangeHandler );
        itemRendererFunction = itemRendererFunctionImpl;
    }   

    protected function propertyChangeHandler( event : PropertyChangeEvent ) : void
    {
        //trace( event.property, event.oldValue, event.newValue );

        if ( event.property == "verticalScrollPosition" ) 
        {
            if ( event.newValue == ( event.currentTarget.measuredHeight - event.currentTarget.height )) 
            {
                fetchNextPage();
            }
        }
    }

    protected function fetchNextPage() : void
    {
        if ( dataProvider is InfiniteListModel )
            InfiniteListModel( dataProvider ).getNextPage();
    }

    private function itemRendererFunctionImpl(item:Object):ClassFactory 
    {
        var cla:Class = IconItemRenderer;
        if ( item is LoadingVO )
            cla = LoadingItemRenderer;
        return new ClassFactory(cla);
    }
}
}

InfiniteListModel класс:

package model
{
    import flash.events.Event;
import flash.utils.setTimeout;

import mx.collections.ArrayCollection;
import mx.rpc.AsyncToken;
import mx.rpc.Responder;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.remoting.RemoteObject;

public class InfiniteListModel extends ArrayCollection
{
    private var _remoteObject : RemoteObject;

    protected var _loading : Boolean = false;

    public function get remoteObject():RemoteObject
    {
        return _remoteObject;
    }

    public function set remoteObject(value:RemoteObject):void
    {
        _remoteObject = value;
        if ( _remoteObject )
            getNextPage();
    }

    public function InfiniteListModel(source:Array=null)
    {
        super(source);
        addItem( new LoadingVO() );
    }

    public function getNextPage() : void
    {
        if ( !_loading)
        {
            _loading = true;

            trace( "fetching data starting at " + (this.length-1).toString() );
            var token : AsyncToken = remoteObject.getData( this.length-1 );
            var responder : Responder = new Responder( resultHandler, faultHandler );
            token.addResponder( responder );
        }
    }

    protected function resultHandler(event:ResultEvent):void
    {
        this.disableAutoUpdate();

        if ( this.getItemAt( this.length-1 ) is LoadingVO )
            this.removeItemAt( this.length-1 );

        for each ( var item : * in event.result )
        {
            addItem( item );
        }
        addItem( new LoadingVO() );
        this.enableAutoUpdate();

        _loading = false;
    }

    protected function faultHandler(event:FaultEvent):void
    {
        trace( event.fault.toString() );
    }
}
}

Для получения дополнительной информации обратитесь к следующему: http://www.tricedesigns.com/2011/10/26/infinito-scrolling-lists-in-flex-applications/

person michael    schedule 22.07.2014