如何保持列表不滚动dataProvider刷新/更新/更改?

时间:2011-06-30 20:45:40

标签: flex list selection dataprovider

我有一个简单的列表和后台刷新协议。

当列表向下滚动时,刷新会将其滚动回顶部。我想阻止它。

我试过捕捉COLLECTION_CHANGE事件和

validateNow();   //  try to get the component to reset to the new data

list.ensureIndexIsVisible(previousIndex);    //  actually, I search for the previous data id in the IList, but that's not important

这会失败,因为列表会在更改后重置(在DataGroup.commitProperties中)。

我讨厌使用Timer,ENTER_FRAME或callLater(),但我似乎无法找到方法。

我能看到的唯一其他选择是对List进行子类化,以便它可以捕获皮肤中的DataGroup正在抛出的dataProviderChanged事件。

有什么想法吗?

4 个答案:

答案 0 :(得分:4)

实际上,更好的解决方案是扩展DataGroup。你需要覆盖它。

此处的所有解决方案都会产生闪烁,因为滚动条会重置为0并且它会重新设置为之前的值。那看起来不对。这个解决方案在没有任何闪烁的情况下工作,最重要的是,您只需在代码中将DataGroup更改为FixedDataGroup即可正常运行,无需其他代码更改;)。

尽情享受。

public class FixedDataGroup extends spark.components.DataGroup
{
    private var _dataProviderChanged:Boolean;
    private var _lastScrollPosition:Number = 0;

    public function FixedDataGroup()
    {
        super();
    }

    override public function set dataProvider(value:IList):void
    {
        if ( this.dataProvider != null && value != this.dataProvider )
        {
            dataProvider.removeEventListener(CollectionEvent.COLLECTION_CHANGE, onDataProviderChanged);
        }
        super.dataProvider = value;

        if ( value != null )
        {
            value.addEventListener(CollectionEvent.COLLECTION_CHANGE, onDataProviderChanged);
        }
    }

    override protected function commitProperties():void
    {
        var lastScrollPosition:Number = _lastScrollPosition;

        super.commitProperties();

        if ( _dataProviderChanged )
        {
            verticalScrollPosition = lastScrollPosition;
        }
    }

    private function onDataProviderChanged(e:CollectionEvent):void
    {
        _dataProviderChanged = true;
        invalidateProperties();
    }

    override public function set verticalScrollPosition(value:Number):void
    {
        super.verticalScrollPosition = value;
        _lastScrollPosition = value;
    }


}

答案 1 :(得分:2)

之前我遇到过这个问题,并通过使用带有匹配器的数据代理模式解决了这个问题。通过仅更新已更改的对象并仅更新现有对象的属性,为支持列表的集合编写匹配器。目标是在数据源刷新时避免创建新对象。

当您拥有列表的新数据(刷新后)时,循环遍历新数据对象列表,将这些对象的属性复制到支持列表的集合中的对象中。通常,您将根据id匹配对象。新列表中旧对象中不存在的任何对象都会被添加。您的滚动位置通常不会改变,通常会保留任何选择。

这是一个例子。

for each(newObject:Object in newArrayValues){
  var found:Boolean = false;
  for each(oldObject:Object in oldArrayValues){
    if(oldObject.id == newObject.id){
      found = true;
      oldObject.myAttribute = newObject.myAttribute;
      oldObject.myAttribute2 = newObject.myAttribute2;
    }
  }
  if(!found){
    oldArrayValues.addItem(newObject);
  }
}

答案 2 :(得分:2)

我会尝试解释我的方法......如果你仍然不确定让我知道,我也会给你源代码。

1)创建一个变量来存储视口的当前滚动位置。 2)在滚动条上为Event.CHANGE和MouseEvent.MOUSE_WHEEL添加事件监听器,并用当前滚动位置更新在步骤1中创建的变量; 3)在视口中为FlexEvent.UpdateComplete添加事件侦听器,并将滚动位置设置为存储的变量。

简而言之,我们所做的是每当用户与其交互时将滚动位置存储在变量中,并且当我们的视口更新时(由于数据提供者更改),我们只需设置我们先前存储在滚动位置中的滚动位置。变量

答案 3 :(得分:0)

我针对这个问题的解决方案针对特定情况,但它具有非常简单的优点,因此您可以从中绘制符合您需求的内容。由于我不确切地知道你要解决的问题,我会给你一个关于我的描述:

我有一个从服务器逐步加载数据的List。当用户向下滚动并将下一批项目添加到数据提供者时,滚动位置将跳回到开头。

解决方法就像停止COLLECTION_CHANGE事件的传播一样简单,以便List不会捕获它。

myDataProvider.addEventListener(
    CollectionEvent.COLLECTION_CHANGE, preventRefresh
);

private function preventRefresh(event:CollectionEvent):void {
    event.stopImmediatePropagation();
}

您必须知道这有效地阻止了重新绘制List组件,因此不会显示任何添加的项目。这对我来说不是问题,因为项目将被添加到List的末尾(在视口之外),当用户滚动时,List将自动被重绘并且将显示新项目。也许在您的情况下,如果需要,您可以强制重绘。

当所有项目都已加载后,我可以删除事件监听器并返回List组件的正常行为。