So I have created my first full custom component and would really like some feedback on it. It replaces a List that uses ItemRenderers. I hate the Flex List component, especially when using variableRowHeight and ItemRenderers. I could never get it to work right, and the size changing scrollbar drove me nuts, so here is my ItemScroller component. It is build fairly specific to the app I am building, but could be used elsewhere. Basically it takes a dataprovider and ItemRenderer(Just another component really) and adds instances of the itemrender to a scrolling canvas contained in a VBox. I added paging for really large datasets, and controls for that at the bottom. So it would be really great to hear some suggestion on making this ItemScroller better, and feel free to use it if you want
So here is the code <?xml version="1.0" encoding="utf-8"?> <mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="init()" horizontalScrollPolicy="off" horizontalGap="1" verticalGap="3" horizontalAlign="center"> <mx:Metadata> [Event(name="itemSelected", type="flash.events.Event")] </mx:Metadata> <mx:Script> <![CDATA[ import mx.collections.ListCollectionView; import mx.core.ScrollPolicy; import mx.core.UIComponent; import mx.core.Container; import mx.collections.ICollectionView; import mx.collections.XMLListCollection; import mx.events.CollectionEvent; import mx.collections.ArrayCollection; private var _dataProvider:ListCollectionView; private var _pageData:ArrayCollection = new ArrayCollection(); private var _pageSize:int = 10; [Bindable]private var _currPage:int = 1; [Bindable]private var _numPages:Number; [Inspectable] public var selectable:Boolean = false; public var selectedItem:Object; public var itemRenderer:Class; private var isInit:Boolean = false; private function init():void { isInit = true; if(_dataProvider != null){ setDP(); } } override public function validateSize(recursive:Boolean = false):void { super.validateSize(recursive); if (!initialized) return; if (itemCanvas.height < itemCanvas.measuredHeight) itemCanvas.verticalScrollPolicy = ScrollPolicy.ON; else itemCanvas.verticalScrollPolicy = ScrollPolicy.OFF; } public function set dataProvider(val:ListCollectionView):void { if(val is XMLListCollection){ //trace("set dp XMLListCollection"); _dataProvider = val as XMLListCollection; }else{ //trace("set dp ArrayCollection"); _dataProvider = val as ArrayCollection; } _dataProvider.addEventListener(CollectionEvent.COLLECTION_CHANGE, setDP); //_dataProvider.filterFunction = sortData; setDP(); } private function setDP(e:CollectionEvent = null):void { if(e != null){ //trace("setDP event "+e.kind); } trace("dp length "+_dataProvider.length); if(_dataProvider.length > 10){ controls.visible = true; controls.height = 20; _numPages = numberOfPages(); firstBtn.enabled = false; prevBtn.enabled = false; lastBtn.enabled = true; nextBtn.enabled = true; setPageData(); //_dataProvider.refresh(); }else{ controls.visible = false; controls.height = 0; _numPages = numberOfPages(); _currPage = 1; createList(_dataProvider); } } private function createList(val:ListCollectionView):void { if(isInit){ itemHolder.removeAllChildren(); //trace("item dp "+_dataProvider); for each( var item:Object in val){ var tmp:Container = new itemRenderer() as Container; itemHolder.addChild(tmp); tmp.data = item; if(selectable){ tmp.addEventListener(MouseEvent.CLICK, onItemClick); } } } } private function setPageData():void { var dataWindowCeiling:Number = _pageSize * _currPage; var dataWindowFloor:Number = dataWindowCeiling - _pageSize; var pa:Array = _dataProvider.toArray(); pa = pa.slice(dataWindowFloor,dataWindowCeiling); _pageData.source = pa; createList(_pageData); } private function controlClick(e:Event):void { //trace("control click"); itemCanvas.verticalScrollPosition = 0; if(e.currentTarget.label == "prev"){ if(_currPage > 1){ _currPage --; //_dataProvider.refresh(); //createList(); lastBtn.enabled = true; nextBtn.enabled = true; if(_currPage == 1){ firstBtn.enabled = false; prevBtn.enabled = false; } setPageData(); } }else if(e.currentTarget.label == "next"){ if(_currPage < _numPages){ _currPage ++; //_dataProvider.refresh(); //createList(); firstBtn.enabled = true; prevBtn.enabled = true; if(_currPage == _numPages){ lastBtn.enabled = false; nextBtn.enabled = false; } setPageData(); } }else if(e.currentTarget.label == "first"){ if(_currPage > 1){ _currPage = 1; //_dataProvider.refresh(); //createList(); firstBtn.enabled = false; prevBtn.enabled = false; lastBtn.enabled = true; nextBtn.enabled = true; setPageData(); } }else if(e.currentTarget.label == "last"){ if(_currPage < _numPages){ _currPage = _numPages; //_dataProvider.refresh(); //createList(); lastBtn.enabled = false; nextBtn.enabled = false; firstBtn.enabled = true; prevBtn.enabled = true; setPageData(); } } } public function numberOfPages() : Number { var result:Number = _dataProvider.length / _pageSize; result = Math.ceil( result ); return result; } private function onItemClick(e:MouseEvent):void { //trace(e.currentTarget.data.elementID); selectedItem = e.currentTarget.data; dispatchEvent(new Event("itemSelected")); } ]]> </mx:Script> <mx:Canvas id="itemCanvas" width="100%" height="100%" verticalPageScrollSize="10" horizontalScrollPolicy="off" borderStyle="solid"> <mx:VBox id="itemHolder" width="100%" height="100%" paddingBottom="4" paddingLeft="4" paddingRight="4" paddingTop="4"/> </mx:Canvas> <mx:HBox id="controls" verticalAlign="middle" visible="false" height="0" backgroundColor="#FFFFFF" backgroundAlpha="0.5" paddingLeft="10" paddingRight="10" borderStyle="solid"> <mx:LinkButton id="firstBtn" label="first" height="20" click="controlClick(event)" enabled="false"/> <mx:LinkButton id="prevBtn" label="prev" height="20" click="controlClick(event)" enabled="false"/> <mx:Label text="{_currPage} of {_numPages}" width="38"/> <mx:LinkButton id="nextBtn" label="next" height="20" click="controlClick(event)"/> <mx:LinkButton id="lastBtn" label="last" height="20" click="controlClick(event)"/> </mx:HBox> </mx:VBox> and here is the implementation <ItemScroller width="100%" height="100%" itemRenderer="{Component}" id="historyList" dataProvider="{ListCollectionView}" selectable="true" itemSelected="function(event);/> thanks Russ