如何在Flex(英雄)中实现无限列表

时间:2011-04-12 05:59:22

标签: flash flex flex4 blackberry-playbook flashlite

我是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. 根据需要添加/删除项目,以便用户可以永久地向上和向下滚动,从不到达任何方向的列表末尾。

  4. 我尝试过以下代码但不起作用。 (代码中的评论)。

    那里有任何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();
            }
    

3 个答案:

答案 0 :(得分:2)

您无法使用列表。您必须从头开始为此创建自己的自定义组件。我在Flex中知道的所有组件都使用有限的dataProvider来显示信息。如果你想要无限,你需要创建自己的组件来处理一个范围(或者无论如何)并适当地显示它并滚动它。请务必清除任何不再显示的项目(或重复使用它们),因为这会造成严重的内存泄漏。

答案 1 :(得分:0)

我认为你需要创建自定义List控件,它为UI传递arraycollection / arraylist项, ArrayCollection / Array / ArrayList只应用于保存数据,如衬里列表项,而列表Control应以循环方式显示项目

EDITED 我试图回答你的问题修改列表和滚动方向的数据 希望这会有所帮助

<?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控件以获取方向和滚动位置 并操纵数组以添加或删除列表中的项目

答案 2 :(得分:0)

可以尝试InfiniteScrollList class ::

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/infinitely-scrolling-lists-in-flex-applications/