Search code examples
xmlapache-flexparsingxml-parsingflex4.5

Flex Horizontal List


I am trying to place my thumbnails into a horizontal list in Flex. What I have so far works fine, but I'd like stylize it to where the the row of thumbnails, when individually clicked, displays the larger image and other information.

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
           xmlns:s="library://ns.adobe.com/flex/spark" 
           xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"
            creationComplete="main()" backgroundColor="#FFFFFF">
<fx:Style source="AgileWidget.css"/>
<s:states>
    <s:State name="State1"/>
    <s:State name="thumbViewer"/>
</s:states>
<fx:Declarations>
</fx:Declarations>
<fx:Script>
    <![CDATA[
        import mx.collections.ArrayCollection;
        import mx.collections.ArrayList;
        import mx.controls.Alert;
        import mx.events.ListEvent;

        import scripts.Equipment;
        import scripts.EquipmentXmlLoad;

        private var equipAC:Array = new Array();
        private var equipXL:EquipmentXmlLoad;
        private var myEquipment:ArrayList;

        private function main():void{
            this.equipXL = new EquipmentXmlLoad("content/labEquipment.xml");
            equipXL.addEventListener("XmlLoadedCompleteEvent",xmlCompleted);
            this.equipCbo.addEventListener(ListEvent.CHANGE, cbChangeEvent);
        }
        private function xmlCompleted(e:Event):void{
            this.equipAC = this.equipXL.returnArray();
            myEquipment = new ArrayList(equipAC);
            this.equipCbo.dataProvider = myEquipment;
        }
        private function cbChangeEvent (evt:Event):void{
            var EquipmentClicked:Equipment=Equipment(evt.currentTarget.selectedItem);
            this.titleLbl.text = EquipmentClicked.title;
            this.descripLbl.text = EquipmentClicked.description;
            this.curImg.source = EquipmentClicked.imageThumbnailURL;
            this.lgImg.source = EquipmentClicked.imageURL;
            this.replaceLbl.text = EquipmentClicked.replacementCost;
            this.categoryLbl.text = EquipmentClicked.equipmentCategory;
            this.yrLbl.text = EquipmentClicked.yearOfPurchase;
            this.mtDayLbl.text = EquipmentClicked.maintenanceDay;
            this.mtCostLbl.text = EquipmentClicked.maintenanceCost;
            this.avgLifeLbl.text = EquipmentClicked.averageHourlyLife;
        }
        public function addListListener():void{
            myList.addEventListener(MouseEvent.CLICK, function():void
            {
                updateItemInfo(myList.selectedItem as Equipment);
            });
        }
        public function updateItemInfo(equipmentItem:Equipment):void
        {

        }
    ]]>
</fx:Script>
<s:List id="myList" itemRenderer="org.renderer.EquipmentItem" 
        width="400"
        height="200"
        horizontalCenter="0" verticalCenter="0">
    <s:layout>
        <s:HorizontalLayout />
    </s:layout>
</s:List>
    <s:ComboBox id="equipCbo" x="385" y="11" width="364" contentBackgroundColor="#FFFFFF"/>
    <s:Label id="titleLbl" x="521" y="294" text="Select Equipment"/>
    <s:Label id="descripLbl" x="339" y="403" width="465" height="89"/>
    <s:Image id="curImg" x="757" y="10" width="47" height="42"/>
    <s:Image id="lgImg" x="480" y="84" width="175" height="187"/>
    <s:Label id="replaceLbl" x="700" y="328" text="Replacement Cost"/>
    <s:Label id="categoryLbl" x="339" y="328" text="Category"/>
    <s:Label id="yrLbl" x="339" y="348" text="Year Purchased"/>
    <s:Label id="mtDayLbl" x="706" y="348" text="Maintainence Day"/>
    <s:Label id="mtCostLbl" x="700" y="368" text="Maintainence Cost"/>
    <s:Label id="avgLifeLbl" x="339" y="368" text="Average Life"/>
</s:Application>

I have two AS3 classes parsing the XML; here is the loader:

package scripts {
    import flash.display.*;
    import flash.events.*;
    import flash.net.*;

    public class EquipmentXmlLoad extends Sprite{
        private var docXML:XML;
        private var urlLoader:URLLoader;

        public function EquipmentXmlLoad( XMLFileName:String )  {
            this.urlLoader = new URLLoader;
            this.urlLoader.addEventListener( Event.COMPLETE, completeListener );
            this.urlLoader.load( new URLRequest( XMLFileName ) );
        }
        public function completeListener( e:Event ) : void {
            this.docXML = new XML( this.urlLoader.data );
            this.dispatchEvent( new Event( "XmlLoadedCompleteEvent" ) );
        }
        public function returnArray() : Array{
            var tempArray:Array = new Array();
            for( var i:int = 0; i < this.docXML.equipment.length(); i++ ){
                var tempEquip:Equipment = new Equipment();
                tempEquip.title = this.docXML.equipment[ i ].title;
                tempEquip.imageThumbnailURL = this.docXML.equipment[ i ].imageThumbnailURL;
                tempEquip.imageURL = this.docXML.equipment[ i ].imageURL;               
                tempEquip.description = this.docXML.equipment[ i ].description;
                tempEquip.replacementCost = this.docXML.equipment[ i ].replacementCost;
                tempEquip.equipmentCategory = this.docXML.equipment[ i ].equipmentCategory;             
                tempEquip.yearOfPurchase = this.docXML.equipment[ i ].yearOfPurchase;
                tempEquip.maintenanceDay = this.docXML.equipment[ i ].maintenanceDay;
                tempEquip.maintenanceCost = this.docXML.equipment[ i ].maintenanceCost;
                tempEquip.averageHourlyLife = this.docXML.equipment[ i ].averageHourlyLife;
                tempArray.push( tempEquip );
            }
            return tempArray;
        }       
    }
}

And here is the renderer:

<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
                xmlns:s="library://ns.adobe.com/flex/spark"
                xmlns:mx="library://ns.adobe.com/flex/mx"
            autoDrawBackground="true">
    <s:Image source="{data.imageThumbnailURL}" width="50" height="50" />
    <s:Label text="{data.title}" width="120" textAlign="center"/>
</s:ItemRenderer>

Solution

  • So, there are two aspects to your request

    First, to display the thumbnails, you will have to use a List component, with a custom item renderer. Setting up the list is the easiest part:

    <s:List id="myList" itemRenderer="org.renderer.EquipmentItem">
        <s:layout>
            <s:HorizontalLayout />
        </s:layout>
    </s:List>
    

    Here is what the item renderer, for an Equipment item could look like:

    <?xml version="1.0" encoding="utf-8"?>
    <s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
                    xmlns:s="library://ns.adobe.com/flex/spark"
                    xmlns:mx="library://ns.adobe.com/flex/mx"
                    autoDrawBackground="true">
    
        <s:Image source="{data.imageThumbnailURL}" />
    
    </s:ItemRenderer>
    

    This works by passing each object in the list's data provider to an instance of the item renderer, through the data property.

    This should solve the first part of your problem. Now, to get the clicked item and rendering it, you need to catch the event dispatched when the list's selected item is changed. There are basically two ways to do this:

    1) Dispatch a custom event with the list's selected item, when the selection changes:

    To do this, first set a change handler on the list:

    change="dispatchEvent(new EquipmentEvent(EquipmentEvent.ITEM_CLICKED, myList.selectedItem as Equipment))"
    

    Then, the custom event class could look like this:

    public class EquipmentEvent extends Event
    {
        static public const ITEM_CLICKED:String = "itemClicked";
    
        public var equipmentItem:Equipment;
    
        public function EquipmentEvent(type:String, item:Equipment, bubbles:Boolean = false, cancelable:Boolean = false)
        {
            super(type, bubbles, cancelable);
    
            this.equipmentItem = item;
        }
    }
    

    You can then listen for that event, directly on the view that contains the list.

    2) The second solution is to listen for click events dispatched by the list, and getting its selected item to display the information about it:

    public function addListListener():void
    {
        myList.addEventListener(MouseEvent.CLICK, function():void
        {
            updateItemInfo(myList.selectedItem as Equipment);
        });
    }
    
    public function updateItemInfo(equipmentItem:Equipement):void
    {
        // item info display logic goes here
        // all the info about the selected item is in the equipmentItem parameter
    }
    

    It's up to you to decide which method works best for your situation. The first one assumes that you mediate the view which contains the list (i.e. the list's selected item needs to be passed, from the view, to some other object), and the second one assumes that the list and the selected item details are handled by the same view (i.e. you just need to pass info between two sibling components, which are in the same view).

    Have a great day.