1
votes

I have a Spark List (spark.components.List) backed by an ArrayCollection for its dataProvider. The List has a vertical scrollbar when there's too many rows to display. What I want is when a new row is added to the List for it to scroll to the bottom to show that new row.

I've tried calling List's ensureIndexIsVisible from a listener on the ArrayCollection. This doesn't work because the List hasn't yet fully rendered the new row. It will either scroll to the second from the last row, or throw the exception:

Error: invalidIndex
 at spark.layouts.supportClasses::LinearLayoutVector/start()[E:\dev\4.0.0\frameworks\projects\spark\src\spark\layouts\supportClasses\LinearLayoutVector.as:893]
 at spark.layouts.supportClasses::LinearLayoutVector/getBounds()[E:\dev\4.0.0\frameworks\projects\spark\src\spark\layouts\supportClasses\LinearLayoutVector.as:1117]
 at spark.layouts::VerticalLayout/getElementBounds()[E:\dev\4.0.0\frameworks\projects\spark\src\spark\layouts\VerticalLayout.as:852]
 at spark.layouts.supportClasses::LayoutBase/http://www.adobe.com/2006/flex/mx/internal::getScrollPositionDeltaToElementHelper()[E:\dev\4.0.0\frameworks\projects\spark\src\spark\layouts\supportClasses\LayoutBase.as:1367]
 at spark.layouts.supportClasses::LayoutBase/getScrollPositionDeltaToElement()[E:\dev\4.0.0\frameworks\projects\spark\src\spark\layouts\supportClasses\LayoutBase.as:1348]
 at spark.components::List/ensureIndexIsVisible()[E:\dev\4.0.0\frameworks\projects\spark\src\spark\components\List.as:2105]

I've made sure that my listener is added to the ArrayCollection after setting List's dataProvider. The hope is that I'd be calling ensureIndexIsVisible after the List got a chance to process the new row. My guess is that the List doesn't render the row until some redraw event that occurs later (after I've called ensureIndexIsVisible).

I've also tried specifying a VerticalLayout and setting its verticalScrollPosition to an overly large number (like 99999). This has the same problem - it scrolls to the second from the last row.

So, is there a way to scroll to a newly added row in a Spark List? The only I could find on the Internet is this poorly formatted flexcoders thread.

6
Did you try to call ensureIndexIsVisible() in a callLater? Like callLater(function():void { ensureIndexIsVisible(lastIndex) });Maxim Kachurovskiy
Yes, I tried that and it didn't work, apparently callLater is not being called later enough. I did use setTimeout and it does work but that's very hacky.Steve Kuo

6 Answers

4
votes

Instead of extending List in order to override updateDisplayList, I added a listener to the FlexEvent.UPDATE_COMPLETE on my List component and it works just fine for me.

1
votes

My solution was to extend List and override updateDisplayList, which works:

protected override function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
    super.updateDisplayList(unscaledWidth, unscaledHeight);
    // call ensureIndexIsVisible here
}

See discussion on Adobe's forums.

1
votes

I found that when ensureIndexIsVisible is run directly after an insert to a list dataProvider it does not accurately pull the position of the last item in the list. This looks to be because the binding has not had a chance to update the list. The fastest solution for me was to run a callLater on ensureIndexIsVisible.

_acGuestBookMessages.addItemAt(obj, _acGuestBookMessages.length);
var arr:Array = [_acGuestBookMessages.length-1];
callLater(lstGuestBookMessages.ensureIndexIsVisible, arr);
1
votes

Want to scroll to bottom of an updating spark list and ensureIndexIsVisible is too much undocumented trouble? I got this to work by dealing with the physical height of dataGroup property on List...

function newMessage(...)
{
    messages.addItemAt(msg, messages.length);               
    messages.refresh();
    scrollToBottom();
}

function scrollToBottom():void
{
    messagesList.addEventListener("updateComplete", scrollUpdate);
}

private function scrollUpdate(e:Event):void
{
    messagesList.removeEventListener("updateComplete", scrollUpdate);
    messagesList.dataGroup.verticalScrollPosition = messagesList.dataGroup.contentHeight - messagesList.dataGroup.height;
}
0
votes
if(itemsList && itemsList.scroller && 
   itemsList.scroller.verticalScrollBar && 
   itemsList.scroller.verticalScrollBar.visible){

       itemsList.scroller.verticalScrollBar.value = 
          itemsList.scroller.verticalScrollBar.maximum;

}
0
votes

Was having the same problem, this works for me -

if(chatCollection.length > 0)
{
    chatList.validateNow();
    chatList.ensureIndexIsVisible(chatCollection.length - 1);
}