3 August 2009
All
Welcome to Part 5 of this tutorial series. In this part, you will create, style, and implement a custom clock component for the wagon assembly line application that is pictured in Figure 1. You will also implement the ActionScript Timer class to animate the clock and ensure that updates to the inventory quantities in the DataGrid control are properly reflected in the colored bins.
Note: Parts 1 through 6 focus on teaching Flex development to SAP developers. Part 7 teaches Flex developers how to create a Web Dynpro application. Parts 8 and 9 show both SAP and Flex developers how to integrate the Flex application as a Rich Island into the Web Dynpro application.
Figure 2 outlines the Flex framework controls and containers that you will use in this application. In this tutorial, you convert the clock display that you created in an earlier tutorial into a custom component by moving the Canvas container layout (the smaller image at the bottom right of Figure 2) from the main application file to a separate file.
You used the following variables in previous tutorials:
_wagonInventory: The inventory data that will populate the colored bins._wagonProductionRate: The speed at which a complete wagon is assembled._wagonYellowWarningAmount: The minimum number of wagons that can be created, based on the available inventory, when a bin turns yellow._inventoryNeededForYellow: The number of parts needed in a bin to make the wagons for the _wagonYellowWarningAmount._wagonRedWarningAmount: The minimum number of wagons that can be created, based on available inventory, when you want the bin to turn red and the assembly line to stop._inventoryNeededForRed: The number of parts needed in the bin to make the wagons for the _wagonRedWarningAmount._binPosition: The index of the data in the _wagonInventory ArrayCollection that populates the bin.This tutorial introduces the following variables:
_productionTimer: An instance of the Timer class that will generate timed events and animate the clock and bin displays based on the _wagonProductionRate._secondsToCreateAvailableWagons: The number of seconds needed to create wagons based on the bin with the lowest amount of inventory. This value will change appropriately as inventory is used. _clockMinutes: The minutes calculated from the _secondsToCreateAvailableWagons for display in the clock._clockSeconds: The remaining seconds (after the minutes are calculated) for display in the clock._clockDisplay: An instance of the Date class that is used to create a time to display on the clock.currentTime: The string representation of the Date object that will display on the clock.In this section, you will replace the clock code from the main application MXML file with an equivalent custom component.
The component is based on the Canvas container.
width and height properties.clockTitle Label control and the following timeLeftBackground Canvas container and its contents.clockBase Canvas container and delete it.assemblyLineBackground Canvas container and add the ClockComponent from the comp namespace before the first BinComponent component instance. Note: Remember that the comp namespace has been defined in the opening Application tag to point to all class files in the components directory of this Flex Builder project. Since ClockComponent.mxml is a class file within this directory, you can reference it from the namespace.
<comp:ClockComponent id="clock"
width="197" height="90"
y="32"
horizontalCenter="0"/>
You should see the that clock is unformatted because the clockStyle styleName property has not been applied to ClockComponent.mxml.
currentTime and data type it to the String class.The currentTime variable is the String used for the clock display. You are creating it as a public variable because it will be a property of the custom component that can be set in the ClockComponent instance of the main application.
currentTime variable Bindable.[Bindable]
public var currentTime:String;
timeLeft Label control and change its id property value of 00 and bind the text property to currentTime.<mx:Label id="timeLeft"
text="{currentTime}"
.../>
You now create a variable to hold the time and display it in the clock.
variable declarations, declare and instantiate a bindable private variable named _clockDisplay and data type it to the Date class. The _clockDisplay is an instance of the Date class that you will use to format and display the clock value. An instance of the Date class represents a particular point in time.
[Bindable]
private var _clockDisplay:Date = new Date();
clock component instance and add a currentTime property with a value bound to _clockDisplay.Note: Remember the currentTime is the public property of the ClockComponent that you created earlier. You are binding the _clockDisplay Date object in the main application to it.
<comp:ClockComponent id="clock"
. . .
currentTime="{_clockDisplay}"/>
You should see the time displayed as shown in Figure 4. The time has not been formatted yet as minutes and seconds.
In this section you will use the DateFormatter ActionScript class to properly format the clock display.
DateFormatter component with its id property value set to clockFormatter and its formatString property value set to NN:SS.Note: NN:SS is a format mask where NN represents minutes in an hour and SS represents seconds in a minute. Check the Flex documentation for more information about the available masks.
<mx:DateFormatter id="clockFormatter"
formatString="NN:SS" />
clock component instance and use the format() function of the clockFormatter class to format the _clockDisplay property and bind it to the currentTime property of the custom component. The format() function returns a String representation of the information contained in the Date object.
<comp:ClockComponent id="clock"
. . .
currentTime="{clockFormatter.format(_clockDisplay)}"/>
As you can see in Figure 5, the clock component is not styled correctly.
styleName property to the opening Canvas tag with a value of clockStyle. Note: Remember, the clockStyle is defined in the CSS file named ShopFloor.css in the assets folder. It was not applied to the ClockComponent when the code was transferred from the main application.
You should see that the clock is now properly formatted (see Figure 6).
In this section you calculate the count down time on the clock based on the available inventory in the bins. You will also use the ActionScript Timer class to animate the clock.
_clockDisplay variable in the Script block and remove the assigned value.Note: Remember that this variable is a Date object that will hold the time, in minutes and seconds, for display in the clock. You need control as to when the value of currentTime is set which will happen in the function you create next.
[Bindable]
private var _clockDisplay:Date;
In this section, you calculate the total number of wagons that can be assembled based on the available inventory in each bin and the number of parts necessary from each bin to create a wagon.
clock component instance and delete the currentTime property from the instance.The currentTime property will be populated within ActionScript through the logic of the application.
_secondsToCreateAvailableWagons and data type it to the uint class.
This variable will hold the total number of seconds to make wagons based on available inventory. The DateFormatter object will also use this value to format the clock display. This variable will decrement as the clock counts down.
_wagonProductionRate, data type it to the uint class and give it a value of 6.The production rate represents how fast a wagon can be assembled. In this case, every 6 seconds a wagon is assembled.
[Bindable]
private var _secondsToCreateAvailableWagons:uint;
[Bindable]
private var _wagonProductionRate:uint = 6;
Script block, create a private function named calculateMaxWagonsPossibleFromInventory() that takes no arguments and returns a void data type.potentialWagonsFromCurrentBin and data type it to the uint class.Note: This variable will hold the total number of wagons that can be created from the current bin as it loops over each bin in the ArrayCollection data. Note that local variables are not declared with an access modifier such as private or public. Their scope is automatically restricted to the function in which they are created.
private function calculateMaxWagonsPossibleFromInventory():void
{
var potentialWagonsFromCurrentBin:uint;
}
lowestPotentialNumberOfWagons, data type it to the uint class. This variable will hold the value from the bin with the lowest number of possible wagons that can be created based on inventory.
private function calculateMaxWagonsPossibleFromInventory():void
{
var potentialWagonsFromCurrentBin:uint;
var lowestPotentialNumberOfWagons:uint;
}
for loop to loop over the _wagonInventory ArrayCollection. In the for statement, create an iterant named x and data type it to the uint class with a value of 0 (zero). While x is less than _wagonInventory.length, iterate the loop using the x++ syntax. for(var x:uint = 0; x < _wagonInventory.length; x++)
{
}
_wagonInventory Quantity divided by the current index of the _wagonInventory Parts to the potentialWagonsFromCurrentBin variable. Use the getItemAt() function. As you perform the loop, you will divide the available inventory by the number of parts necessary for each wagon. In other words, if you have 100 wheels (quantity) and you need 4 wheels per wagon (parts), then the potential number of wagons that you can create from the wheels bin is 25.
potentialWagonsFromCurrentBin =
_wagonInventory.getItemAt(x).Quantity / _wagonInventory.getItemAt(x).Parts;
x is equivalent to 0 (zero) or potentialWagonsFromCurrentBin is less than lowestPotentialNumberOfWagons. This is either the first time through the loop or the number of wagons that can be created from the current evaluated bin is less than the number of wagons that can be created from any previously evaluated bin.
Note: Remember that the lowestPotentialNumberOfWagons variable will hold the lowest number of wagons that can be created from all of the bins. In other words, as you loop over each bin, this conditional check is asking, "Does this bin that I am currently evaluating have a lower number of potential wagons than the previous bins that I have already evaluated?"
for(var x:uint = 0; x < _wagonInventory.length; x++)
{
potentialWagonsFromCurrentBin = _wagonInventory.getItemAt(x).Quantity /
_wagonInventory.getItemAt(x).Parts;
if (x == 0 || potentialWagonsFromCurrentBin < lowestPotentialNumberOfWagons)
{
}
}
potentialWagonsFromCurrentBin to lowestPotentialNumberOfWagons.This will set the number of wagons that can be created for this evaluated bin as the lowest possibility.
if (x == 0 || potentialWagonsFromCurrentBin < lowestPotentialNumberOfWagons)
{
lowestPotentialNumberOfWagons = potentialWagonsFromCurrentBin;
}
_wagonInventory quantity is less than the _wagonInventory parts. Use the getItemAt() function. This tests whether there are enough parts in the bin to create a full wagon.
0 (zero) to lowestPotentialNumberOfWagons.If there are not enough parts to make a wagon, then you will set the lowestPotentialNumberOfWagons to zero.
if (_wagonInventory.getItemAt(x).Quantity < _wagonInventory.getItemAt(x).Parts)
{
lowestPotentialNumberOfWagons = 0;
}
In this section, you will use the result of the previous calculation to determine the number of seconds it will take to create the possible wagons.
calculateMaxWagonsPossibleFromInventory() function, assign the result of lowestPotentialNumberOfWagons multiplied by _wagonProductionRate to the _secondsToCreateAvailableWagons variable.This calculates how many total seconds it takes to create all the wagons from the bin with the lowest inventory.
_secondsToCreateAvailableWagons = lowestPotentialNumberOfWagons *
_wagonProductionRate;
setClockDisplay() function. You will create this function next.You will call the setClockDisplay() function to display the countdown time in the clock component.
_secondsToCreateAvailableWagons = lowestPotentialNumberOfWagons *
_wagonProductionRate;
setClockDisplay();
In this section, you use the _secondsToCreateAvailableWagons value to calculate the minutes and seconds on the clock.
_clockMinutes and data type it to the uint class.This variable will hold the value of total number of minutes it takes to make all the wagons.
_clockSeconds and data type it to the uint class.This variable will hold the value of the remaining seconds after the minutes have been calculated.
[Bindable]
private var _clockMinutes:uint;
[Bindable]
private var _clockSeconds:uint;
setClockDisplay() that takes no arguments and returns a void data type.private function setClockDisplay(): void
{
}
_secondsToCreateAvailableWagons divided by 60 using the Math.floor() function and assign the results to _clockMinutes.This calculates the number of minutes.
_secondsToCreateAvailableWagons mod (using %) 60 to _clockSeconds.This calculates the remaining number of seconds using the modulus (%) operator. Remember that the modulus operator divides the _secondsToCreateAvailableWagons variable value by 60 and returns the remainder.
private function setClockDisplay(): void
{
_clockMinutes = Math.floor(_secondsToCreateAvailableWagons/60);
_clockSeconds = _secondsToCreateAvailableWagons % 60;
}
_clockDisplay variable to the Date class. _clockMinutes = Math.floor(_secondsToCreateAvailableWagons /60);
_clockSeconds = _secondsToCreateAvailableWagons % 60;
_clockDisplay = new Date();
setMinutes() method of the Date class to populate the new Date object by passing _clockMinutes and _clockSeconds as the two arguments. _clockDisplay = new Date();
_clockDisplay.setMinutes(_clockMinutes,_clockSeconds);
clockFormatter.format() function to format _clockDisplay and assign it to the text property of clock. This uses the previously defined DateFormatter instance to format the Date object display.
private function setClockDisplay(): void
{
_clockMinutes = Math.floor(_secondsToCreateAvailableWagons /60);
_clockSeconds = _secondsToCreateAvailableWagons % 60;
_clockDisplay = new Date();
_clockDisplay.setMinutes(_clockMinutes,_clockSeconds);
clock.currentTime = clockFormatter.format(_clockDisplay);
}
startProduction() function in the Script block.calculateMaxWagonsPossibleFromInventory(). This ensures that the possible number of wagons are calculated and that the clock is formatted with the correct number of minutes and seconds when the application is initialized.
populateData();
calculateMaxWagonsPossibleFromInventory();
You should see the clock display a starting time based on the Handles bin because it has the lowest inventory. Change any of the bins to 50. Note that the clock time does not change.
In this section, you animate the clock using the ActionScript Timer class.
import the Timer class from the flash.utils package. import flash.utils.Timer;
import flash.events.TimerEvent;
_productionTimer and data type it to the Timer class.private var _productionTimer:Timer;
This variable is an instance of the Timer class that you will use to animate the clock.
startProduction() function in the Script block._productionTimer to the Timer class and pass 1000 and 0 as arguments. This code instantiates a Timer instance that will dispatch every second (1000 milliseconds) and will not stop. The second argument, set to zero here, instructs the timer to not stop. If you set this value to a number, the timer will stop in that number of milliseconds.
private function startProduction():void
{
populateData();
calculateMaxWagonsPossibleFromInventory();
_productionTimer = new Timer(1000, 0);
}
_productionTimer instance that takes TimerEvent.TIMER and timerEventHandler as arguments. Note: Event listeners execute code in response to specific events. The first argument declares the type of event to be handled and the second argument declares the name of the function that will execute. You will create the timerEventHandler() function next.
_productionTimer = new Timer(1000, 0);
_productionTimer.addEventListener(TimerEvent.TIMER, timerEventHandler);
timerEventHandler() that takes one argument named event. Data type the argument to the TimerEvent class and set the function to return a void data type.private function timerEventHandler(event:TimerEvent):void
{
}
_secondsToCreateAvailableWagons variable using -- syntax.When the timerEventHandler() function is called every second (1000 milliseconds), you will decrement the display on the clock by one second.
setClockDisplay() function.private function timerEventHandler(event:TimerEvent):void
{
_secondsToCreateAvailableWagons--;
setClockDisplay();
}
Now that there is one less second on the clock, you call the function that recalculates the minutes and seconds and changes the clock display, which animates the clock.
Earlier you created the timer instance to never stop. This means that you must explicitly call the stop() and start() methods of the Timer class to control the instance. In this section, you create the function to start the timer when the application is initialized and the function to stop the timer when the clock counts down to zero.
startTimer() that takes no arguments and returns a void data type.start() function of _productionTimer instance.private function startTimer():void
{
_productionTimer.start();
}
startProduction() function and call the startTimer() function before the closing curly brace. This will start the timer when the application initializes.
_productionTimer.addEventListener(TimerEvent.TIMER, timerEventHandler);
startTimer();
You should see the clock counting down. If you wait until the clock counts down to zero, you see that the TimerEvent event is still being dispatched so the clock never stops.
startTimer() function, create a private function named stopTimer() that take no arguments and returns a void data type.stop() function of _productionTimer.private function stopTimer():void
{
_productionTimer.stop();
}
timerEventHandler() function in the Script block and delete the code in the function._secondsToCreateAvailableWagons is equivalent to 0 (zero).stopTimer() function.else statement to decrement the _secondsToCreateAvailableWagons variable if the conditional statement is false.else statement, call the setClockDisplay() function.if(_secondsToCreateAvailableWagons == 0)
{
stopTimer();
}
else
{
_secondsToCreateAvailableWagons--;
setClockDisplay();
}
Let the clock count down to zero. You should see that the clock stops.
In this tutorial, you created the custom clock component, styled it, and used it in the main application. You also used the ActionScript Timer class to animate the clock and the colored bins and stopped the clock when it reached zero.
If you are interested in learning more about the topics in this tutorial, refer to the following resources:
In Part 6, you will synchronize the clock with the wagon assembly so that, as the clock counts down, inventory will be removed from the colored bins based on the production rate.
For your reference, here are all the parts in this series: