In the first post of this Simple Calculator App series, we took some baby steps toward creating a simple calculator. We're using the comp, shown below, that graphics designer Mark Dingman of Malden Labs supplied:
For teaching purposes, I'm taking a very incremental and sometimes indirect approach to building this. For example, in the first post I used the SwingButton class for the calculator keys. In this post, we'll use a Group that consists of a Rectangle and Text object to create each key. Also, we'll use a for expression to succinctly create the keys -- a technique that has another benefit related to event handling that you'll see in a moment. And remember, the calculator in its current state simply displays the keys typed rather than performing any calculations. We'll create that code when showing you how to define classes, functions, etc. in JavaFX.
Click on the calculator image above to invoke the Simple Calculator JavaFX applet, or the Java Web Start icon below to make the application appear in its own window:
Understanding the Code Behind this JavaFX Applet
Take a look at the code below, and note what has changed since its first incarnation.
/*
* SimpleCalc2.fx
*
* Developed 2008 by James L. Weaver (jim.weaver at javafxpert.com)
* to demonstrate creating applications using JavaFX SDK 1.0
*/
package javafxpert;
import javafx.ext.swing.*;
import javafx.stage.Stage;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.input.*;
import javafx.scene.layout.*;
import javafx.scene.paint.*;
import javafx.scene.shape.*;
import javafx.scene.text.*;
import javafx.scene.transform.*;
/**
* The "stage" for the application
*/
Stage {
// The model
var displayStr:String;
var keyLabels = ["7", "8", "9", "+",
"4", "5", "6", "-",
"1", "2", "3", "x",
"0", ".", "=", "/"];
title: "Calculator 2"
scene: Scene {
// The gradient on the calculator keys
var btnGradient = LinearGradient {
startX: 0.0, startY: 0.0,
endX: 0.0, endY: 1.0
stops: [
Stop {
offset: 0.0
color: Color.#a6a6a8
},
Stop {
offset: 1.0
color: Color.#7a7a7a
}
]
};
content: [
// The rounded rectangle behind the entire calculator
Rectangle {
width: 278
height: 401
arcWidth: 16
arcHeight: 16
fill: LinearGradient {
startX: 0.0, startY: 0.0,
endX: 0.5, endY: 1.0
stops: [
Stop {
offset: 0.0
color: Color.#77747b
},
Stop {
offset: 1.0
color: Color.#020202
}
]
},
},
VBox {
transforms: bind Translate.translate(20, 20)
spacing: 27
content: [
// The calculator's display
TextBox {
text: bind displayStr
width: 238
height: 65
editable: false
style: bind "text-fill: #343434; "
"background-fill: #f4f4f4; "
"font: 28pt Dialog; "
"font-weight: bold; "
},
// The calculator's keys
VBox {
spacing: 2
content: for (row in [0..3]) {
HBox {
spacing: 2
content: for (column in [0..3]) {
Group {
var rectRef:Rectangle;
var textRef:Text;
content: [
rectRef = Rectangle {
width: 58
height: 50
fill: btnGradient
},
textRef = Text {
transforms: bind
Translate.translate((rectRef.layoutBounds.width -
textRef.layoutBounds.width) / 2,
(rectRef.layoutBounds.height -
textRef.layoutBounds.height) / 2)
content: keyLabels[row * 4 + column]
textOrigin: TextOrigin.TOP
fill: Color.#dedede
font: Font {
name: "Arial Bold"
size: 30
}
}
]
onMouseClicked: function(me:MouseEvent):Void {
displayStr = "{displayStr}{textRef.content}"
}
}
}
}
}
}
]
}
]
}
}
Notice the for expressions that creates an HBox for each row, and a Group for each calculator key in a row. The value of a for expression is a sequence (think array) of objects. More information on the for expression can be found in the JavaFX Language Reference (in development at this writing).
Another noteworthy item is the value of the transforms attribute in the Text object. The Translate.translate function above causes the Text to be centered horizonally and vertically within the Rectangle.
One of the advantages of using the for expression is that only one event handler is required for all of the keys, rather than one for each key as in the previous Simple Calculator version. In this case the onMouseClicked attribute is on the Group that contains the Rectangle and Text.
One more item of note is the use of the LinearGradient class to make the application begin looking more like the graphic comp shown above. Consult the JavaFX API docs for more information on using this class. By the way, the API documentation is available online as well as being in the JavaFX SDK.
Please leave a comment if you have any questions! By the way, the third and final part of this series is now ready, written by Dean Iverson.
Regards,
Jim Weaver
JavaFXpert.com
"...So thanks in advance for your answers.
Regards
Alex"
Alex,
I'm sorry that I haven't responded sooner to your questions. Are you using the JavaFX SDK 1.0 (as opposed to a previous version)? Binding performance was improved a few months ago. If you are still having issues or questions, can you please post some code examples that I can look at and respond to?
Merci,
Jim Weaver
Posted by: James Weaver | January 01, 2009 at 07:48 PM
"So thank you for your tutorials."
javafxnut,
It's my pleasure!
Thanks,
Jim Weaver
Posted by: James Weaver | January 01, 2009 at 07:36 PM
Simply kewl! Im in my 1st year java programming degree. We learnt fundamentals using processing.org. While I thought that was awesome, this is much more useful to me. So thank you for your tutorials.
Posted by: javafxnut | January 01, 2009 at 06:23 PM
Hi James,
First thanks for your usefull tips and lessons.
I'm writing right now an app in order to layout elements (text or images) for a printer (industrial) and I've some disagrements with rendering's performance when moving my customNode with the mouse(I've tryed javafx.com sample way). I've found that including many composite customNode such as group decrease performance and quality.
Is the best way is to make as possible larges scripts instead of a lot a composed? And what about binding performance, is it better to do: [string: bind "{bind varA}iznizx{bind varB}"] instead of binding directly to a well formed String. I've see on the compiler's hudson builds that the team is rewriting binding, can we wait better performances? for now i'm rewriting with less groups of customNodes and i'm thinking about restricting event with timing test. So thanks in advance for your FX'guru answers.
Regards
Alex
(ps: sorry i'm french :-)
Posted by: Alex | December 08, 2008 at 09:39 AM