我是Flex/ActionScript的新手。到目前为止,NET/Java一直是我的主要领域)。我试图建立一个Flex应用程序,它有一个列表,意味着看起来和行为像一个无限列表(项目-它可以是任何对象)。这个想法是,用户应该能够向上或向下滚动,并且永远不会到达任何方向的列表末尾。
一个例子是一个数字列表。向上滚动显示的是负数;向下滚动将显示积极的内容。现在,我的列表是一个简单的Flex Spark列表(使用Flex Hero)。它被绑定到一个数据提供者,该数据提供者是一个数组列表。
我最初的想法是监听滚动事件并根据需要添加/删除项目。然而,在Flex Hero的当前版本中,有一个错误,有时不会引发垂直滚动条的滚动事件(http://bugs.adobe.com/jira/browse/SDK-26533)。
所以,我正在使用上面链接中建议的解决方案(即监听列表滚动轮的viewport的propertyChanged事件)。事件虽然只给我当前的垂直scrollposition。
看起来默认的spark列表支持平滑滚动,并在列表滚动停止之前多次引发事件(这是一个很好的视觉效果)。
现在,我需要:
- 弄清楚它是向上还是向下滚动(我怎么做?)
-
找出哪些项目是可见的。我可以从
list.dataGroup.getItemIndicesInView()
-
根据需要添加/删除项目,以便用户可以永远向上和向下滚动,永远不会到达任何方向的列表结束
我试过下面的代码,但它不起作用。(代码注释).
有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();
}
不能使用List。您必须从头开始创建自己的自定义组件。我所知道的Flex中的所有组件都使用有限的dataProvider来显示信息。如果你想要无限的,你需要创建自己的组件来处理一个范围(或者什么都不处理),并适当地显示它并滚动它。请确保清除不再显示的任何项(或重用它们),因为这将导致严重的内存泄漏。
我认为你需要创建你的自定义列表控件,它循环arraycollection/arraylist项目的UI,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="<<" click="{ltClicked(event)}"/>
<mx:Button id="rt" label=">>" click="{rtClicked(event)}"/>
</mx:HBox>
</mx:VBox>
</mx:Application>
注意函数ltClicked和rtClicked正在修改List数据滚动是用来到达
方向的我认为更好的方法是扩展列表控件来获取方向和滚动位置并操作Array来添加或删除列表
可以试试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/