In this episode of the building the BandmatesFX application series, I'm going to show you how to add the following functionality to a JavaFX application:
- Showing a splash page before the main functionality of the program starts.
- Handling mouse events on the individual images in a JFXtras Shelf (cover flow) control.
- Detect whether the JavaFX program is running as an applet in the browser.
- Open a web page in a browser, whether running as an applet or from Java Web Start.
- Use the GoogleMap custom node from Sergey Surikov's open source CRUDfx project (the treasure trove mentioned in the title) to display an interactive Google map in a JavaFX program.
Before we get into how to do these things, take the newest iteration of the BandmatesFX app for a spin, making sure that you test out the new functionality mentioned above.
There are some pointers after the screenshot below to help you along.
- After invoking the BandmatesFX app, start typing (for example) Axl Rose and notice that a list appears suggesting William Bailey (Axl's given name).
- Click on William in the list, and the screen should look almost like the screenshot above. Note: After this was posted, I altered the queries so that if an image of an artist or band isn't available in Freebase (and therefore not in Wikipedia), it will show a placeholder image.
- Hover the mouse on the images in each of the cover flows, noticing that the name of the band or artist appears in a popup window on the upper left side of the images.
- Click the secondary mouse button on any of the images, and the Freebase.com page for that band or artist should appear in the browser.
- Look at the map in the upper right portion of the application, noticing that Axl was born in Lafayette, Indiana (which is about 80 miles from where I live). Try clicking on the map and dragging the mouse, which should expose other parts of Indiana and the United States.
- Recall from the last article in this series that clicking the artist that is in the center of the bottom Shelf will move his/her picture to the top of the app. The bands and artists in the cover flows will be replaced accordingly. Trivia: According to the screenshot above, the given name of the Guns N' Roses lead guitarist is Saul Hudson (who is more widely known as Slash).
Some Example Code Snippets
Here are some code snippets behind the new functionality:
/**
* Use the availability of the eval function (that uses the browser's
* JavaScript engine), as an indicator that we're running as an applet
*/
def runningAsApplet:Boolean = AppletStageExtension.eval("2+3") != null;
var splashStageRef:Stage;
/*
* Show a "splash" page (Stage) while the main Stage is performing some startup
* functionality. At the end of this script, this splash screen will be removed
* with the following lines of code:
*
* if (not runningAsApplet) {
* splashStageRef.close();
* }
*
* Note: To show a splash page when tunning as an applet, use the following
* loading_image_* lines in a script tag of the HTML file:
*
* javafx(
* {
* archive: "BandmatesFX.jar",
* draggable: true,
* width: 1000,
* height: 700,
* code: "javafxpert.BandmatesMain",
* name: "BandmatesFX",
* loading_image_url: "http://www.jmentor.com/JavaFX_1-2/BandmatesFXnext/images/guitar.png",
* loading_image_width: 1000,
* loading_image_height: 700
* }
* );
*
*/
if (not runningAsApplet) {
splashStageRef = Stage {
title: "BandmatesFX Splash Page"
style: StageStyle.TRANSPARENT
scene: Scene {
width: 1000
height: 700
content: {
ImageView {
image: Image {
url: "http://www.jmentor.com/JavaFX_1-2/BandmatesFXnext/images/guitar.png"
}
}
}
}
}
}
...some code omitted...
/*
* This instantiates the GoogleMap custom node that Sergey Surikov developed
*/
googleMapRef = GoogleMap {
focusTraversable: true
width: IMAGE_HEIGHT
height: IMAGE_HEIGHT
zoom: 4.0
// Make the up and down arrows affect the zoom level of the map
onKeyPressed:function(ke:KeyEvent):Void {
println("googleMapRef.zoom:{googleMapRef.zoom}");
if (ke.code == KeyCode.VK_UP) {
googleMapRef.zoom += 1;
}
else if (ke.code == KeyCode.VK_DOWN) {
googleMapRef.zoom -= 1;
}
}
},
...some code omitted...
/*
* To change the geographic location shown by the GoogleMap node, change its
* latitude and longitude variables.
*/
googleMapRef.latitude = if (latStr == "") 0.0 else Float.parseFloat(latStr);
googleMapRef.longitude = if (longStr == "") 0.0 else Float.parseFloat(longStr);
/*
* Here is the Shelf that contains the images of the bands for the chosen artist
*/
Shelf {
reflection: false
showScrollBar: false
showText: true
centerEventsOnly: false
imageUrls: bind for (grp in freebaseResult.result.musicGroupMemberMembership) {
"{freebaseImageURL}{grp.group.id}?maxheight={IMAGE_HEIGHT}"
}
imageNames: bind for (grp in freebaseResult.result.musicGroupMemberMembership) {
grp.group.name;
}
/*
* When the secondary mouse button is pressed on an image, open a page
* in the browser for that band. If running as an applet, use the
* javafx.stage.AppletStageExtension class. If running as a Java Web Start
* application, the use the BasicService class (located in jnlp.jar)
* to open the web page.
*/
onImagePressed:function(se:ShelfEvent):Void {
if (se.mouseEvent.button == MouseButton.SECONDARY) {
def fbId = freebaseResult.result.musicGroupMemberMembership[se.index].
group.id;
if (runningAsApplet) {
AppletStageExtension.showDocument("http://www.freebase.com/view/{fbId}",
"_blank");
}
else {
def basicService:BasicService =
ServiceManager.lookup("javax.jnlp.BasicService") as BasicService;
def url:URL = new URL("http://www.freebase.com/view/{fbId}");
basicService.showDocument(url);
}
}
}
/*
* When the mouse enters an image, make a popup visible that contains the
* name of the band represented by the image.
*/
onImageEntered:function(se:ShelfEvent):Void {
println("Entered image:{se.index}, obj:{se.mouseEvent.node}");
println(freebaseResult.result.musicGroupMemberMembership[se.index].
group.name);
infoBoxName = freebaseResult.result.musicGroupMemberMembership[se.index].
group.name;
infoBoxVisible = true;
infoBoxPosX = se.mouseEvent.node.localToScene(se.mouseEvent.node.layoutBounds).minX;
infoBoxPosY = se.mouseEvent.node.localToScene(se.mouseEvent.node.layoutBounds).minY;
}
/*
* When the mouse exits an image, make the popup invisible
*/
onImageExited:function(se:ShelfEvent):Void {
println("Exited image:{se.index}");
infoBoxVisible = false;
}
index: bind groupOneIndex with inverse
thumbnailWidth: IMAGE_HEIGHT
thumbnailHeight: IMAGE_HEIGHT
layoutInfo: LayoutInfo {
width: bind sceneRef.width
height: IMAGE_HEIGHT + 120
}
},
As always, please leave a comment if you have any questions.
Regards,
Jim Weaver
Comments