Today we'll build a fully functional calendar widget using AS3. It's not rocket science, just an excellent example of using the Date
class, which can handle all the complexity of extracting times, dates, months and years. We are also going to use some Flash components, and make sure that this calendar is portable to Flash Builder, FlashDevelop, and so on.
Final Result Preview
Let's take a look at the final result we will be working towards:
Step 1: Brainstorming
Before starting to build the code, let's take a look at what we'll need:
- A class file, Calendar.as,
- A cell grid, to place dates,
- Labels for day names,
- Function to get current date, month and year,
- Function to get previous months' days,
- Function to get future months' days,
- Something to address the leap year issue,
- Interface to pick any month and year,
- Portability (for Flash Builder, FlashDevelop, and other IDEs).
Step 2: Preparing the Calendar.as Class File
In this step we shall create a basic structure of our Calendar.as class file.
There are several ways of creating ActionScript class file, like using FDT, Flash Builder, FlashDevelop. And, of course, the "Higgs Boson" of this multimedia world: the one true Flash IDE. I am using Flash Professional. Create an ActionScript 3.0 class file. The following is the basic structure of Calendar.as to start with.
package { import flash.display.Sprite; public class Calendar extends Sprite { //variables public function Calendar () { // constructor code } } }
I am sure that you have saved this class file as Calendar.as in the new folder for this calendar app. If not please save it.
Step by step we shall modify this Calendar.as to make it fully functional.
In the next step we will create a few text fields to label the weekdays and dates.
Step 3: Setting Up Text Formats for Dates and Weekdays
In this step we will modify our class by declaring some variables and creating a new function setTextFormat( fontFace, fontSize )
.
We will also passing some parameters to the constructor of the class.
Initially it will look like "Syntactic Sugar", but as the tutorial progresses it will become "Syntactic Salt". Sorry, but we want to keep this class compact so as to make it easily portable. The idea is to have only one class and that's all.
So, keep a close watch on the constructor's number of parameters and their order as we progress.
Back to modifying the document class...
package { import flash.display.Sprite; import flash.text.TextFormat; public class Calendar extends Sprite { //variables private var dateCellFormat:TextFormat; private var dayLabelTxtFmt:TextFormat; public function Calendar( fontFace:String = "Arial", fontSize:int = 15 ) { setTextFormat( fontFace, fontSize ); } private function setTextFormat(whichFont:String, size:int):void { //date text format dateCellFormat = new TextFormat(); dateCellFormat.font = whichFont; dateCellFormat.color = 0xFFFFFF; dateCellFormat.size = size; dateCellFormat.align = "center"; //day label text format dayLabelTxtFmt = new TextFormat(); dayLabelTxtFmt.font = "_sans"; dayLabelTxtFmt.color = 0x000000; dayLabelTxtFmt.size = size - 3; } } }
Observe all the parameters passed to the constructor: we have already assigned values to them. This is the way to create default values for parameters. If no parameters are specified when instantiating the class, these default values will be used instead. We will see this in coming steps.
Nothing visible yet. Wait for a few steps.
Step 4: Creating Cell Grid to Arrange Dates
The calendar is made up of rows and columns forming a grid.
But how many rows and columns?
It depends on the calendar type - Vertical or Horizontal.
Vertical view needs 7 (rows) x 6 (columns), while horizontal view needs 6 (rows) x 7 (columns) as shown:
As our calendar is limited to horizontal view for this tutorial, we will need 7 columns and 6 rows. Thus total (7 x 6) = 42 cells.
So, let us modify our class so that it will generate a grid required for calendar view. Along with adding an import statement, new variables and constructor parameters, we will also add a new function makeDatesCellGrid(..)
to the class as shown:
package { import flash.display.Sprite; import flash.text.TextFormat; import flash.text.TextField; public class Calendar extends Sprite { //variables private var cellW:Number; //cell width private var cellP:Number; //cell padding private var allDatesCells:Array = new Array(); private var dateCellFormat:TextFormat; private var dayLabelTxtFmt:TextFormat; public function Calendar( fontFace:String = "Arial", fontSize:int = 15, cellWidth:Number = 30, padding:Number = 3, originX:Number = 15, originY:Number = 15 ) { cellW = cellWidth; cellP = padding; setTextFormat( fontFace, fontSize ); makeDatesCellGrid( originX, originY ); } private function setTextFormat(whichFont:String, size:int):void { //date text format dateCellFormat = new TextFormat(); dateCellFormat.font = whichFont; dateCellFormat.color = 0xFFFFFF; dateCellFormat.size = size; dateCellFormat.align = "center"; //day label text format dayLabelTxtFmt = new TextFormat(); dayLabelTxtFmt.font = "_sans"; dayLabelTxtFmt.color = 0x000000; dayLabelTxtFmt.size = size - 3; } private function makeDatesCellGrid(cellXPos:Number, cellYPos:Number):void { //Create grid of date cells for (var i:int = 0; i < 42; i++) { var dateCell:TextField = new TextField(); addChild(dateCell); //position cells to form a grid (7 x 6 = 42) dateCell.x = cellXPos + (cellW * (i-(Math.floor(i/7)*7))); dateCell.y = cellYPos + (cellW * Math.floor(i/7)); //put all date cells into array for further access allDatesCells.push(dateCell); } } } }
Oops, lots of code is written and nothing visible yet. I know you are waiting to see some visible results. For that we will make a temporary change in makeDatesCellGrid
by adding a new line as shown:
private function makeDatesCellGrid(cellXPos:Number, cellYPos:Number):void { //Create grid of date cells for (var i:int = 0; i < 42; i++) { var dateCell:TextField = new TextField(); addChild(dateCell); //position cells to form a grid (7 x 6 = 42) dateCell.x = cellXPos + (cellW * (i-(Math.floor(i/7)*7))); dateCell.y = cellYPos + (cellW * Math.floor(i/7)); //put all date cells into array for further access allDatesCells.push(dateCell); allDatesCells[i].text = i; } }
Wait...Wait. Now create a new Flash file for ActionScript 3.0 and save this file with any name (ideally Calendar.fla as I did) in the same folder where you saved Calendar.as. Now open the ActionScript panel by pressing "F9" and type the following code:
var myCalendar:Sprite = new Calendar(); addChild(myCalendar);
Now test the movie.
There are a few things to fix, like setting up the dates for current month, labels for days, etc.
Also note that though the constructor can take parameters, we saw a result without passing any values for parameters. This is what I was talking about in the previous step.
Observe the following two lines we placed inside the constructor,
cellW = cellWidth; cellP = padding;
We created these vars for global access. We have assigned values to these variables from constructor parameters.
We used "cellW" for placing cells in grid form. We will use "cellP", i.e. cell padding (the gap between neighbouring cells), in coming steps. So wait and watch.
One more thing. After testing above code, remove the line allDatesCells[i].text = i;
from the makeDatesCellGrid(..)
function. We added it for testing purposes only. Actually, we are going to put dates in those text fields in coming steps.
Step 5: Adding Names of Weekdays
In this step we will add labels for weekdays to our calendar. For that we are going to add a new function makeDaysLabels(...)
and call it inside the constructor.
First of all, add new array weekDays[]
at the start of the class under the //Variables
declaration section. This array hold names of all weekdays as shown:
private var weekDays:Array = new Array("Sun","Mon","Tue","Wed","Thu","Fri","Sat");
Now add the following function to the class.
private function makeDaysLabels(cellXPos:Number, cellYPos:Number):void { //Add week day names for (var i:int = 0; i < 7; i++) { var dayLabel:TextField = new TextField(); addChild(dayLabel); dayLabel.selectable = false; dayLabel.text = weekDays[i]; dayLabel.setTextFormat(dayLabelTxtFmt); dayLabel.x = cellXPos + (cellW * i); dayLabel.y = cellYPos - 15; } }
Finally, call this function inside constructor as shown:
public function Calendar( fontFace:String = "Arial", fontSize:int = 15, cellWidth:Number = 30, padding:Number = 3, originX:Number = 15, originY:Number = 15 ) { cellW = cellWidth; cellP = padding; setTextFormat( fontFace, fontSize ); makeDatesCellGrid( originX, originY ); makeDaysLabels( originX, originY ); }
Weekdays are ready and waiting for dates to have their right places.
Step 6: Initializing and Decorating Dates' Cells
In this step we will make textfield background visible so that there's no need to draw a rectangle shape. But if you want to add more style then after completing this tutorial go ahead and create rectangle instances as dates' cells background and add a gradient color to them. For now it's ok to set the textfield's background to true
and apply a solid color to it.
Add the following new function to our class.
private function monthSetup():void { for (var i:int = 0; i < 42; i++){ allDatesCells[i].text = ""; //decor all cells allDatesCells[i].background = true; allDatesCells[i].backgroundColor = 0x000000; allDatesCells[i].border = true; allDatesCells[i].borderColor = 0xCCCCCC; allDatesCells[i].selectable = false; allDatesCells[i].width = allDatesCells[i].height = cellW - cellP; allDatesCells[i].setTextFormat(dateCellFormat); } }
Then, call this function inside constructor as shown:
public function Calendar( fontFace:String = "Arial", fontSize:int = 15, cellWidth:Number = 30, padding:Number = 3, originX:Number = 15, originY:Number = 15 ) { cellW = cellWidth; cellP = padding; setTextFormat( fontFace, fontSize ); makeDatesCellGrid( originX, originY ); makeDaysLabels( originX, originY ); monthSetup(); }
Test the movie.
Cool... The cells are ready to hold dates for the selected month.
Step 7: Arrange Dates for Selected Month
We will add a new function arrangeDates()
. This is the most important function for this Calendar app.
We will be completing the following must-do tasks through this function:
- Creating date object from
Date
class. - Identifying the column number for first day of selected month.
- Getting maximum number of days for selected month (with respect to leap year, if applicable).
- Putting dates at correct places for selected month.
- Highlighting today's date, if selected month is current month.
Pretty challenging tasks, but interesting.
Ok, back to the job. First we will declare some variables at the start of the class under the //Variables
section as shown:
private var currDateTime:Date = new Date(); private var firstDay:Date = new Date(currDateTime.fullYear,currDateTime.month,1); private var firstDayColumn:uint = firstDay.day; private var daysOfMonths:Array = new Array(31,28,31,30,31,30,31,31,30,31,30,31); private var maxDays:uint;
Here currDateTime
gives the current time, today's date, and the current month and year.
firstDay
gives all info about the first day (i.e. Date 1) of the current month. Observe that we have passed the value for third parameter as "1" for date. Try tracing with various dates to get an idea of what this is doing. But remember to make it "1" again after testing.
firstDayColumn
, as the name suggests, gives the column number for the first day of current month,
daysOfMonths
array holds maximum number of days from "January..." to "...December". But for "February" we have "28" days; in case of leap year we must assign "29" days as the maximum number of days for February. We will see it later in this step.
maxDays
will hold maximum number of days for current month.
Fine. Good enough explanation for each var. It's time for new function arrangeDates()
.
In order to understand this function neatly, we will modify it gradually.
So first, In this function we will get the column number for first day (i.e. Date 1) of a selected month as shown:
private function arrangeDates():void { //get column number for first day of the month if (firstDay.day == 0) { //when last date of previous month is on saturday then move to second row firstDayColumn = firstDay.day + 7; } else { firstDayColumn = firstDay.day; } }
This "firstDayColumn"
is important since once we get column number for first day (i.e Date 1) then placing next dates becomes very easy. We will simply use this "firstDayColumn"
number inside a for
loop to place the next dates. We will see it soon in coming steps.
Step 8: Getting the Maximum Number of Days for Selected Month
We will modify the "arrangeDates()"
function to get the maximum days for selected month as shown:
private function arrangeDates():void { //get column number for first day of the month if (firstDay.day == 0) { //when last date of previous month is on saturday then move to second row firstDayColumn = firstDay.day + 7; } else { firstDayColumn = firstDay.day; } //get max days for current month w.r.t leap year if any maxDays = (firstDay.getFullYear()%4 == 0 && firstDay.getMonth() == 1 ? 29 : daysOfMonths[firstDay.getMonth()]); }
The above new line is a shorthand method for an if
statement. New users may consider it "Syntactic Salt", but once used to it will consider it "Syntactic Sugar".
Observe the question mark "?" in the above line. On the left side of this question mark - i.e. "firstDay.getFullYear()%4 == 0 && firstDay.getMonth() == 1"
- is a conditional statement. If this left side is true
then the statement after the "?" mark (here 29
) is executed; if it is false
the statment on the right of the ":" mark (here daysOfMonths[firstDay.getMonth()]
) is executed. See the image below for a better understanding.
Ok, back to our shorthand if statement. It gives the maximum number of days for selected month. But you have to consider leap years, too. In a leap year, February has 29 maximum days. This leap year comes every fourth year.
So we have two conditions, one is to check if it is leap year and second is to check if it is February.
To check whether current year is leap year we used modulus operator (which gives the remainder after dividing one number by another), as in "firstDay.getFullYear() % 4"
. If it gives a remainder of 0 (zero) then we can say that it is a leap year. For example, 2012 is a leap year, as (2012 % 4 == 0)
. Similarly (2016 % 4 == 0)
, so 2016 is a leap year, and so on. If the remainder is nonzero then obviously it is not a leap year. Thus we can detect leap years.
(Editor's note: actually, there's a little more to it than that: years divisible by 100 are usually not leap years - unless they are also divisible by 400. To keep this tutorial simple, we'll omit that, but you can program it in if you wish!)
Now we will check whether the current month is "February". Before that let us consider the index number for months generated by Date class. It starts from 0 for Jan, 1 for Feb, 2 for March and so on. So to find out whether the current month is "February", we use "firstDay.getMonth() == 1"
. "1" for "February".
If both these conditions are true then we set maxDays = 29
; otherwise we refer to the "daysOfMonths"
array.
Step 9: Placing Dates of Selected Month at Their Positions
Now we have "maxDays"
and "firstDayColumn"
with us so we are able to arrange dates for selected month.
Modify "arrangeDates()"
as shown below,
private function arrangeDates():void { //get column number for first day of the month if (firstDay.day == 0) { //when last date of previous month is on saturday then move to second row firstDayColumn = firstDay.day + 7; } else { firstDayColumn = firstDay.day; } //get max days for current month w.r.t leap year if any maxDays = (firstDay.getFullYear()%4 == 0 && firstDay.getMonth() == 1 ? 29 : daysOfMonths[firstDay.getMonth()]); //put dates for current month for (var i:int = 0; i < maxDays; i++) { allDatesCells[firstDayColumn + i].text = i + 1; allDatesCells[firstDayColumn + i].setTextFormat(dateCellFormat); allDatesCells[firstDayColumn + i].alpha = 1; } }
We set "allDatesCells[firstDayColumn + i].alpha = 1"
for date field as we are going to set previous and next month's date fields' alpha to 0.5
in coming steps. So we set alpha = 1
in advance to avoid returning to this function, since this function is almost finished.
Now "arrangeDates()"
function has something to show. But we need to call it somewhere. The function "monthSetup()"
is the right place to call this function.
So modify function "monthSetup()"
as shown:
private function monthSetup():void { for (var i:int = 0; i < 42; i++){ allDatesCells[i].text = ""; //decor all cells allDatesCells[i].background = true; allDatesCells[i].backgroundColor = 0x000000; allDatesCells[i].border = true; allDatesCells[i].borderColor = 0xCCCCCC; allDatesCells[i].selectable = false; allDatesCells[i].width = allDatesCells[i].height = cellW - cellP; allDatesCells[i].setTextFormat(dateCellFormat); } arrangeDates(); }
At this point you can test the movie.
Wow. exciting. See those dates at their positions.
Step 10: Highlighting Today's Date
In this step we will highllight today's date. So modify the "for"
loop in "arrangeDates()"
as shown:
private function arrangeDates():void { //get column number for first day of the month if (firstDay.day == 0) { //when last date of previous month is on saturday then move to second row firstDayColumn = firstDay.day + 7; } else { firstDayColumn = firstDay.day; } //get max days for current month w.r.t leap year if any maxDays = (firstDay.getFullYear()%4 == 0 && firstDay.getMonth() == 1 ? 29 : daysOfMonths[firstDay.getMonth()]); //put dates for current month for (var i:int = 0; i < maxDays; i++) { allDatesCells[firstDayColumn + i].text = i + 1; allDatesCells[firstDayColumn + i].setTextFormat(dateCellFormat); allDatesCells[firstDayColumn + i].alpha = 1; //Highlight today if (firstDay.fullYear == currDateTime.fullYear && firstDay.month == currDateTime.month) { if(allDatesCells[firstDayColumn + i].text == currDateTime.date) { allDatesCells[firstDayColumn + i].backgroundColor = 0xEE5D15; } } } }
Test movie to see today's date highlighted.
We are very close to getting that "Calendar" feel. Once the dates of previous month and next month are placed, then we are done - for the current month, at least.
Step 11: Adding Dates of the Previous Month
We want to add dates from before the "firstDayColumn", so we will get all previous date fields in reverse order by using a decrementing "for"
loop.
Add the following new function to the class:
private function prevMonthDates():void { var prevMonthFirstDay:Date = new Date(firstDay.fullYear,firstDay.month,firstDay.date - 1); for (var i:int = firstDayColumn-1; i >= 0; i--) { allDatesCells[i].text = prevMonthFirstDay.date - ((firstDayColumn - 1) - i); allDatesCells[i].setTextFormat(dateCellFormat); allDatesCells[i].alpha = 0.5; } }
Now call this function inside "monthSetup()"
as shown:
private function monthSetup():void { for (var i:int = 0; i < 42; i++){ allDatesCells[i].text = ""; //decor all cells allDatesCells[i].background = true; allDatesCells[i].backgroundColor = 0x000000; allDatesCells[i].border = true; allDatesCells[i].borderColor = 0xCCCCCC; allDatesCells[i].selectable = false; allDatesCells[i].width = allDatesCells[i].height = cellW - cellP; allDatesCells[i].setTextFormat(dateCellFormat); } arrangeDates(); prevMonthDates(); }
Test the movie to see the previous month's dates placed at their positions.
Step 12: Adding Dates of the Next Month
In this step we will add dates of next month. Add the following new function to the class as shown:
private function nextMonthDates():void { for (var i:int = 1; i < (42 - maxDays - (firstDayColumn - 1)); i++){ allDatesCells[(firstDayColumn - 1) + i + maxDays].text = i; allDatesCells[(firstDayColumn - 1) + i + maxDays].setTextFormat(dateCellFormat); allDatesCells[(firstDayColumn - 1) + i + maxDays].alpha = 0.5; } }
Now call this function inside "monthSetup()"
function as shown:
private function monthSetup():void { for (var i:int = 0; i < 42; i++){ allDatesCells[i].text = ""; //decor all cells allDatesCells[i].background = true; allDatesCells[i].backgroundColor = 0x000000; allDatesCells[i].border = true; allDatesCells[i].borderColor = 0xCCCCCC; allDatesCells[i].selectable = false; allDatesCells[i].width = allDatesCells[i].height = cellW - cellP; allDatesCells[i].setTextFormat(dateCellFormat); } arrangeDates(); prevMonthDates(); nextMonthDates(); }
Test the movie to see dates of next month placed at their positions.
At this point our calendar is ready, displaying all the dates required in the current month's display. Now in the coming steps we will allow the user to select any month and any year, to complete the Calendar application.
Step 13: Adding the Capability to Select Any Month
Selecting any month will add more sense to our Calendar. To select any month from the list, the "ComboBox"
component is the best option available.
Therefore we need to add a ComboBox which holds a list of months. It should be placed at an appropriate position, somewhere near the calendar. So we will allow placing this ComboBox as per the developer's choice. How will we allow the developer to change this? We will pass parameter for the X and Y positions of this ComboBox to the constructor of Calendar.as.
To add and place the ComboBox, first consider the following tasks we must perform:
- Passing parameters for the ComboBox's
"X"
and"Y"
positions to the constructor. - Adding new function
"monthPicker(...)"
where we will manipulate the ComboBox. - Adding one more new function
"pickMonth()"
which will hear the ComboBox's events and will call a function to show the selected month. - Importing the ComboBox component by dragging it from the Component Panel to the Library Panel of our FLA.
First, add the following import statements to the class,
import flash.events.Event; import fl.controls.ComboBox; import fl.data.DataProvider;
Then add new variables at start of the class under //Variables
section as shown:
private var months:Array = [ {label:"January", data:0}, {label:"February", data:1}, {label:"March", data:2}, {label:"April", data:3}, {label:"May", data:4}, {label:"June", data:5}, {label:"July", data:6}, {label:"August", data:7}, {label:"September", data:8}, {label:"October", data:9}, {label:"November", data:10}, {label:"December", data:11}, ]; private var monthPickerCB:ComboBox; //combobox to pick a month
We will use the "months"
array to put values inside combobox.
Ok, now let us modify the constructor function as shown:
public function Calendar( fontFace:String = "Arial", fontSize:int = 15, cellWidth:Number = 30, padding:Number = 3, originX:Number = 15, originY:Number = 15, cbX:Number = 15, cbY:Number = 15 ) { cellW = cellWidth; cellP = padding; monthPickerCB = new ComboBox(); setTextFormat( fontFace, fontSize ); makeDatesCellGrid( originX, originY ); makeDaysLabels( originX, originY ); monthSetup(); monthPicker( cbX, cbY ); }
We added the parameters "cbX"
and "cbY"
into the constructor's signature to pass position values specified by user, or to use default values if not specified.
Then we created a ComboBox instance as "monthPickerCB = new ComboBox()"
.
We called "monthPicker(...)"
in advance, as we are going to create it now.
So we will add two new functions "monthPicker(...)"
and "pickMonth(...)"
as shown:
private function monthPicker(cbX:Number, cbY:Number):void { monthPickerCB.dataProvider = new DataProvider(months); addChild(monthPickerCB); //position combobox monthPickerCB.x = cbX; monthPickerCB.y = (cellW * 6) + cbY; monthPickerCB.selectedIndex = currDateTime.month; monthPickerCB.addEventListener(Event.CHANGE, pickMonth); } private function pickMonth(e:Event):void { firstDay.month = ComboBox(e.target).selectedItem.data; monthSetup(); }
One more step and we are done adding the ComboBox.
Drag the ComboBox from the Component panel into your Flash file's Library panel. (Use the Window menu if you can't find the panels.) This way, we can access the ComboBox component and its classes at runtime.
Test the movie to see combobox at bottom left of the calendar (as default value).
Now you can display all the months, for the current year.
Exciting... So we will now add year selection, giving one more dimension to our calendar.
Step 14: Adding the Capability to Select Any Year
Selecting any year will add one more dimension to our Calendar. To select any year within a range, the NumericStepper
component is a good choice.
Therefore we will add "NumericStepper"
component which will allow the user to increment or decrement the selected year. As with the ComboBox, we will pass parameters to the constructor to specify its X and Y positions.
To add and place a NumericStepper
, we'll have to perform the following tasks:
- Passing parameters for NumericStepper's
"X"
and"Y"
positions to the constructor. - Adding new function
"yearPicker(...)"
where we will manipulate NumericStepper. - Adding one more new function
"pickYear()"
which will hear NumericStepper events and will tell to show the selected year. - Importing the
NumericStepper
component by dragging it from the Component panel to the Library panel of our FLA.
First, add the following import statement to the class,
import fl.controls.NumericStepper;
Then add a new variable at the start of the class under the //Variables
section as shown:
private var yearPickerNS:NumericStepper; //numeric stepper to pick a year
Ok, now let us modify constructor function as shown:
public function Calendar( fontFace:String = "Arial", fontSize:int = 15, cellWidth:Number = 30, padding:Number = 3, originX:Number = 15, originY:Number = 15, cbX:Number = 15, cbY:Number = 15, nsX:Number = 26, nsY:Number = 15, monthsRange:int = 39 ) { cellW = cellWidth; cellP = padding; monthPickerCB = new ComboBox(); yearPickerNS = new NumericStepper(); setTextFormat( fontFace, fontSize ); makeDatesCellGrid( originX, originY ); makeDaysLabels( originX, originY ); monthSetup(); monthPicker( cbX, cbY ); yearPicker( nsX, nsY, monthsRange ); }
We have added three parameters: "nsX"
, "nsY"
and "monthsRange"
. The first two are to specify the position, and the third one is to specify the range (previous and next months). 39 will allow to pick any year within the next and previous 40.
OK, now we will add two new functions "yearPicker(...)"
and "pickYear(...)"
, as shown:
private function yearPicker(nsX:Number, nsY:Number, maxYrsRange:int):void { yearPickerNS.maximum = currDateTime.fullYear + maxYrsRange; yearPickerNS.minimum = currDateTime.fullYear - maxYrsRange; yearPickerNS.value = currDateTime.fullYear; addChild(yearPickerNS); //position numeric stepper yearPickerNS.x = monthPickerCB.width + nsX; yearPickerNS.y = (cellW * 6) + nsY; yearPickerNS.addEventListener(Event.CHANGE, pickYear); } private function pickYear(e:Event):void { firstDay.fullYear = e.target.value; monthSetup(); }
One more step and we are done adding the numeric stepper.
Drag the Numeric Stepper component from the Component panel to your Flash file's Library panel. This way, we can access the Numeric Stepper component and its classes at runtime.
That's it. Test your movie to see a basic Calendar App running.
Step 15: Customizing the Calendar
We were testing the Calendar app with default values. Now we will customize it by passing different parameter values to its constructor.
To start with, we will try using the different fonts available on our system.
So, in the Flash file, go to the first statement in the ActionScript panel as shown:
var myCalendar:Sprite = new Calendar(); addChild(myCalendar);
Click inside parenthesis next to new Calendar
, and type "Courier New"
. Now your statement will look like this:
var myCalendar:Sprite = new Calendar("Courier New");
Test the movie to see the new font applied to dates.
Now try changing next parameter (font size) like so:
var myCalendar:Sprite = new Calendar("Courier New", 17);
While typing the next parameter you will see a code hint pop up, showing all necessary parameters with their default values, like so:
Make sure all parameters are there in the code hint popup. There is a bug(?) I have found when I was working on one project, which I'd like to make you aware of.
When I was writing a document class, at a point when there were only four parameters in the constructor signature, I closed this class file. When I got back to the work, I continued working on this class. At one point there were six parameters. Then I wanted to check this parameter passing stuff. So I opened the Flash file I created for this project and started typing parameters, as we did in our Calendar Flash file. I was expecting my newly added parameters to be displayed in code hints... but surprisingly there were still only four parameters.
I saved and restarted all related files but the result was the same. After lots of testing I found the reason. The reason was, I created a new folder in the same folder where the document class was placed. I removed this folder outside the main folder, then I opened the class file to make some changes and I saved it. Then I went back to Flash file and started typing parameters and this time all parameters showed up.
Ok, not a severe bug, but the lesson is: be careful while setting up a folder structure for your project.
So back to the point where we left off. Try passing proper values for different parameters. You can also set up variables in Flash file (or in a class), so if one value is changed then other related parameters will also get adjusted accordingly - for example, if you want to increase the Font Size then Cell Width must increase accordingly, and so on.
Step 16: Checking the Portability of This Calendar App
The real power of this class is that you can use it in any of your project like so:
- Using in Flash IDE (as we've been doing)
- Calling this Calendar.as inside another document class.
- Using it with FlashDevelop.
- Using it with Flash Builder.
We have already seen how to instantiate Calendar.as in Flash timeline code through this tutorial.
Now we will see how to call it in another class.
There is nothing much that we have to do differently when calling our class from another one. We simply put the same code we typed in Flash timeline into some other class. See the "Main.as"
class below for better understanding:
package { import flash.display.Sprite; public class Main extends Sprite { public function Main():void { init(); } private function init():void { var c:Sprite = new Calendar(); addChild(c); } } }
You can use the above "Main.as" in Flash, FlashDevelop, FlashBuilder, FDT. Only you will have to carry Calendar.as, "Main.as" in your project folders.
In Flash IDE set this "Main.as" as Document class using Property Inspector panel. Make sure you have both Calendar.as, "Main.as" in your project folder. Also make sure you have "Combobox" and "NumericStepper" in your library panel. Now you can test the movie to see the working calendar.
FlashDevelop
Now we will test it with FlashDevelop 4.
Create a new folder "Calendar_FD".
Open FlashDevelop, create a new project, and browse for the above "Calendar_FD"
folder. Press OK to create new project.
Inside "Calendar_FD" folder, you will see three new folders: "bin", "lib" and "src".
Now in the "src" folder copy Calendar.as. Also copy above "Main.as" we created and replace it with the auto generated "Main.as" in the same folder.
Now every thing is set up. But what about the "ComboBox"
and "NumericStepper"
components? Here no Library panel is present, unlike in the Flash IDE.
The solution to this issue is making SWC files. We will create a "CalendarComp.swc"
file.
Create a new FLA (in Flash Pro), name it "CalendarComp.fla", and save it anywhere (but remember its path).
Drag "ComboBox"
and "NumericStepper"
components to the Library panel and save the file.
Open Publish Settings and in the "Flash" tab check "Export SWC" under "SWF settings" group. Hit Publish.
"CalendarComp.swc"
is created in the folder where you saved "CalendarComp.fla"
, so browse to this folder and copy "CalendarComp.swc"
to the "lib"
folder of the "Calendar_FD"
main folder.
Back in FlashDevelop, look in the Project Panel. Right click "CalendarComp.swc"
and select "Add to Library"
. Now we can access all class files required for our components, just like in the Flash IDE.
Finally click "Test Project" to see your Calendar app running.
Flash Builder
How about Flash Builder 4?
Create a new folder, "Calendar_FB".
Open Flash Builder and create a new project.
Browse for the above "Calendar_FB" folder. Also enter a project name (I used "MyProject" as a project name). Press Finish to create a new project.
Inside the "Calendar_FB" folder, you will see a "src" directoy, which already contains "MyProject.as" as the main class.
Copy our Calendar.as class file to this folder. It is now visible in the "Package Explorer".
As in, FlashDevelop there is no library panel like the Flash IDE has. So how to get "ComboBox"
and "NumericStepper"
components?
The solution is to use a SWC, like we did with FlashDevelop. But you need to specify the class path in Flash Builder.
So first copy the "CalendarComp.swc"
we created in the above section for FlashDevelop, and paste this file inside the "src" folder of the "Calendar_FB" main folder.
In Flash Builder we need to add a class path. So click on Project > Properties to open the "Properties" window. Select "ActionScript Build Path" and click the "Add SWC..." button. Then browse for "CalendarComp.swc" which you pasted in the "src" folder.
Modify "MyProject.as" as shown:
package { import flash.display.Sprite; public class MyProject extends Sprite { public function MyProject():void { var myCalendar:Sprite = new Calendar(); addChild(myCalendar); } } }
Finally click "Run My Project" to see your Calendar app running.
Conclusion
So, friends, I would like to stop at this stage. I hope you have got something useful from this tutorial that will help in your coming projects. Best of luck... See you soon.
In this tutorial we saw the logical use of the "Date()"
class in AS3. We addressed the leap year issue. We displayed the next and previous month's dates. We saw how to deal with Flash components. We also checked the portability of this Calendar app using FlashDevelop, Flash Builder and Flash Pro. During the process we also saw how to create and use SWC files to access Flash components outside Flash Pro.
All good. Friends, keep reading Activetuts+. Enjoy!
Comments