» Publishers, Monetize your RSS feeds with FeedShow: More infos (Show/Hide Ads)
Just a quick note that the email address to which you may submit your JavaFXpert RIA Exemplar Challenge entry has been created. It is:
Please visit the original post to see the description and rules for this challenge.
Happy designing and developing!
Judges Jim Weaver, Jasper Potts, Romain Guy and Stephen Chin
I posed the question "Should There be Enterprise RIA Style Guidelines?" in late 2008, and received some valuable feedback/discussion. Based upon that feedback, I'm replacing my question with the following challenge:
"Create an application in JavaFX that exemplifies the appearance and behavior of a next-generation enterprise RIA (rich internet application)".
Here are the rules to this JavaFXpert RIA Exemplar Challenge:
1) Each application must be created in JavaFX by a team consisting of two participants: A graphic designer, and an application developer. Both participants on the winning team will be recognized, and will share the prize.
2) The code must be offered as open source, in the form of the updated Berkeley Software Distribution License. The code for the winning entry will be added to the open source JFXtras project samples library.
3) The entries must be submitted in the form of a NetBeans or an Eclipse project by 00:00 GMT on 10 January 2010 to ria-exemplar-challenge@javafxpert.com. The entries will be judged by two JavaFX developers (Jim Weaver and Stephen Chin), and two graphics designers (Jasper Potts and Romain Guy). The criteria for judging will be how well we feel that the entry "exemplifies the appearance and behavior of a next-generation enterprise RIA".
4) The winning entry will be announced on 20 January 2010 on this JavaFXpert.com blog.
5) The prize, which is a cash award of USD 2000 (USD 1000 for each one of the two participants), will be awarded after the winning team is announced.
If you have any questions about this contest, please leave a comment on this blog post.
Thanks, and have fun with the contest!
Jim Weaver
JavaFXpert.com
Java Champion, JavaOne Rock Star, and all-around good guy Stephen Chin is going to teach JavaFX and give away stuff at Devoxx 2009 in Antwerp, Belgium.
He'll first teach a university session to get you up to speed on the JavaFX platform, and later in the week will show you how to build Rich Internet Applications for the enterprise.
Leading up to both these sessions Stephen will post a question via the LearnFX application (see this blog post for more info on LearnFX). To win a prize, all you have to do is:
- Answer the question correctly using the LearnFX client
- Come to the next Devoxx session that Stephen Chin is speaking at
- Winners will be selected by a random drawing from all the correct responses
To run the LearnFX application, make sure that you have a recent version (update 14 or later) of Java SE 6, and simply click on the blue brain LearnFX icon in the Devoxx logo at the top of this post. Please don't tell Stephan Janssen (the Devoxx conference organizer) that I desecrated his logo. :-)
Break a leg, Stephen!
P.S. Last year I posted the Whack-a-Janssen game in honor of the great job that Stephen Janssen does (with the awesome help of Valérie Hillewaere and Kristien Leyn) in organizing Devoxx.
Take a whack at this now-retro game!
Regards,
Jim Weaver
Readers of this blog know that I like to hold contests to make things fun and encourage participation (e.g. see the 2000 USD JavaFXpert RIA Exemplar Challenge that ends 10 January, 2010). In keeping with that sentiment, some JavaFX questions relevant to my presentation will be tweeted by @projavafxcourse to the twitterverse during the Øredev 2009 RIA Enterprise Application Developing with JavaFX session.
The seven people that earn the highest scores by tweeting fast, correct, answers to these questions will each win a copy of the Pro JavaFX book. Six of these books will be composed of bits (e-books), and one will be composed of atoms. The atom-based book is reserved for the person with the highest score of those present in the audience, but the e-books will be awarded (via an emailed Apress coupon) to the winners regardless on their location during the session. This session starts on Friday, November 6 at 3:35pm CET, lasting until 4:25pm, so please have your Twitter client ready. When you tweet an answer, be sure to reply to the original tweet with your answer.
On a technical note, all correct answers consist of one word or character, and the software that checks for a correct answer trims the spaces from the end of the reply and then uses the string found between the final space and the end of the reply. So, if you want to put other words in your reply, presumably to give your followers a clue about what your terse message is, just make sure that your answer is at the end of your response. For example, you might reply to the question:
The name of the layout class that arranges UI nodes horizontally is ____
with this message:
Answering a JavaFXpert presentation question with HBox
If you want to use an application that I'm developing for the purpose of responding to these questions, please make sure that you have a recent version (update 14 or later) of Java SE 6 and click the LearnFX icon over on the left.
When the application starts up, the most recent question is displayed as shown in the screenshot below. If the question is still active (i.e. the answer has not been revealed), then the text box will be enabled, allowing you to enter an answer and click the Send button or press the Enter key. Responses that have been received appear as well, along with the the Twitter profile image, screen name, and indication of whether the question is correct (if the answer has been revealed -- if not, a question mark will be displayed).
The first time that you run the application, you'll need to click the configuration icon to open the configuration dialog shown below, in which you can enter your Twitter screen name and password, as well as some preferences. This information is stored on your local machine, and automatically loaded on subsequent invocations of the application.
Until the contest begins, there is a sample question available if you wish to give this application a try before the real questions begin appearing. By the way, when you use the LearnFX client, your tweet is a reply to the projavafxcourse user, and the message contains the following with the question ID, and answer that you supply, added to the end:
@projavafxcourse Answering JavaFXpert question http://bit.ly/2dn1kK
The bit.ly link in the message may be clicked by your followers to see what your strange tweet is about :-)
There is a WidgetFX version of this application in the works that I'll release and blog about in a subsequent post. By the way, I'd like to publicly thank Steven Herod for the Twitter API this application uses, which he donated to the open source JFXtras project.
Regards, and good luck! Please leave a comment if you have any questions,
Jim Weaver
Sten Anderson, winner of the Sun JavaFX Coding Challenge with his Music Explorer FX app, has just released a version for JavaFX Mobile. Sten donated the source code to the JFXtras project, and has provided a write-up in his blog which includes a Java Web Start link.
Music Explorer FX Mobile Edition is a great resource/reference for creating JavaFX Mobile apps, so check it out!
Enjoy, and thanks Sten,
Jim Weaver
In the JavaFXpert RIA Exemplar Challenge announcement, I stated that the graphic design judge would be chosen and disclosed soon. I am pleased to say that there will actually be two very high-caliber graphic design judges for this contest: Jasper Potts and Romain Guy. As if these guys needed an introduction:
Jasper Potts is a developer on the JavaFX & Swing teams at Sun Microsystems, and lives in Bath, UK. He is currently working on the new JavaFX UI controls and graphics frameworks, and was the bloke that created the JavaFX Charts API. Jasper, and Richard Bair, have an excellent JavaFX blog at fxexperience.com, where Jasper has already begun providing tips for the RIA Exemplar Challenge participants.
Romain Guy is the co-author of the ground-breaking Filthy Rich Clients book, and is a user interface toolkit engineer at Google on the Android project. Romain lives in San Francisco, California, and has a blog named Curious Creature that contains some stunning photographs, very recently of the Colorado River at sunset. He also has a "French, goofier version of Curious Creature" named #ProgX.
Stephen Chin, a Pro JavaFX book co-author, is going to help me judge the RIA Exemplar Challenge entries from a developer perspective. Stephen is the leader (with Keith Combs), of the WidgetFX and JFXtras projects. Stephen is very active in the leadership of the JavaFX community, having recently founded the Silicon Valley JavaFX User Group, and has a JavaFX/Java blog at steveonjava.com.
All three are internationally recognized experts and speakers, and I am very pleased to have them help me judge the RIA Exemplar Challenge. Please be sure and enter the challenge!
Regards,
Jim Weaver
One of the student winners of the the JavaFX Coding Challenge is Kazuki Hamasaki, who created the CalcFX program. CalcFX is a deceptively functional (and incredibly useful) calculator, and is now one of the Java Web Start shortcuts on my desktop.
Kazuki: If you're reading this, please create a desktop icon for CalcFX so that it doesn't blend in with the usual coffee-cup icons deposited by Java Web Start. For those wondering, just put the following line subordinate to the information element of the JNLP file:
<icon href="some_icon.png" width="64" height="64"/>
Congrats, and nice work, Kazuki!
Jim Weaver
If you've hung around senior [-citizen] programmers (or are one), you've no doubt heard tales such as "we only had 4K of memory, and had to make use of every byte/bit". For your amusement and edification, here's a picture from thegreatgeekmanual.com of the first Apple II (circa 1977), which sported 4K of RAM:
Josh Marinacci's latest JavaFX Studio Challenge is a bit reminiscent of these good old days, in that he is encouraging us to create a cool JavaFX program that "must not be more than 30 lines of code or 3000 characters (your choice)".
The deadline for submitting your entry for this JFXStudio Challenge is midnight Wednesday night (30 September 2009). The challenge is to create something cool in only 30 lines of JavaFX Script code, using the theme of 'Time'. To give you some ideas Josh has posted his own entry. Take a look for some inspiration!
Speaking of contests, the winners of the WidgetFX Developers Contest were announced yesterday, and I'd like to convey my congratulations to Pär Dahlberg (ScreenshotFX widget), Yannick Van Godtsenhoven (RadioFX widget), and Larry Dickson (Weather widget). A screenshot of these widgets in the WidgetFX dock is shown on the right. I'm using the RadioFX widget as I'm writing this to listen to an internet music channel that plays baroque music.
Regards, and congratulations again to these winners!
Jim Weaver
If you've been following this blog recently, you know that I've been developing an application in the SpeedReaderFX category that helps me quickly keep up on new happenings in world events, technology, gadgets, music, and social networks. SpeedReaderFX is located in the JFXtras open source project, and also serves as an example of using JFXtras classes with JavaFX.
One of the newest enhancements made to the SpeedReaderFX program takes advantage of the Picker control that David Armitage created in the JFXtras project. Here's a screen shot of a program in the JFXtras project that he created to demonstrate and test the Picker control varieties:
SpeedReaderFX uses the spinner-style variation of the Picker control, shown in the Thumb Wheel row of the screen shot above. This enables SpeedReaderFX users to quickly choose how many entries of a given feed-type (e.g. Yahoo News feeds), to display in the scrolling table. The screen shot below shows a portion of the SpeedReaderFX Criteria dialog in which the Pickers appear:
As noted previously in the Picker demo screen shot, the user can operate the mouse wheel, as well as the keyboard or mouse buttons, to select the desired number.
For more information about the SpeedReaderFX application, check out the first post in the SpeedReaderFX category of this blog. To run the application, click the SpeedReaderFX icon located on the left side of this paragraph.
By the way, since this dialog is getting pretty crowded, Dean Iverson is creating some UI design comps in which each feed type will have its own page. I've also received other ideas from readers that I plan to implement, and would like to express my appreciation for your continued input!
Regards,
Jim Weaver
When adding more feeds to the SpeedReaderFX application's Criteria dialog, I found that some of them don't *quite* comply with RSS/Atom formats. For example, I thought it'd be cool to have RSS feeds from Engadget and Gizmodo (what self-respecting geek wouldn't want those feeds?) I also wanted to have a daily dose of Dilbert delivered, but in these three cases the JavaFX RSS API reported that the dates for the feed items were just prior to the OS epoch.
Naturally, I consulted the RSS/Atom Smasher himself (Rakesh Menon) and he instructed me in the ways of creating a custom feed parser, which I added to SpeedReaderFX. Here's a partial screenshot of the Criteria dialog where you can select the Gizmodo, Engadget, TechCrunch, and Dilbert feeds:
For more information about the SpeedReaderFX application, check out the first post in the SpeedReaderFX category of this blog. To run the application, click the SpeedReaderFX icon located on the left side of this paragraph.
By the way, one of the next things that SpeedReaderFX needs is a GUI makeover (I've received lots of helpful feedback in that regard, and my graphical skills are admittedly weak). Please leave a comment to this post if you have ideas or graphic comps that would make this look more like an iPhone app. SpeedReaderFX is a sample in the open source JFXtras project, and your contributions to the appearance of this application would be much appreciated and acknowledged on this blog.
Enjoy!
Jim Weaver
Some of you may remember the advertisements for the Evelyn Wood Reading Dynamics course back in the 1970s. Here's a flashback in which Steve Allen (an excellent former host of the Tonight Show), is interviewing students of this course. Gotta love those '70s hairstyles :-)
This course promised to increase reading speed and comprehension, partially (as I recall) by using the technique of scanning a page while limiting eye movement.
The ability to digest new information quickly is at least as important now as it was 20+ cranks of Moore's Law ago. RSS and Atom feeds have provided mechanisms to publish information as it becomes available, and social networking tools such as Twitter have HTTP-based APIs. Because it is time consuming to visit various sources of new information, I've begun developing a tool named SpeedReaderFX to present information obtained via RSS, Atom, and social networking APIs in one place.
Here's a screen shot of SpeedReaderFX as it is displaying new items of interest:
In the screen shot above, there are tweets from interesting people (Josh Marinacci and Dean Iverson), a picture taken by some fortunate soul viewing a beautiful Australian sunrise, a video containing stunning new pictures from the Hubble telescope, and an article about an expensive bottle of wine. These are displayed in the very impressive Table component that Stephen Chin and Keith Combs are creating for the open source JFXtras project. A future post will explain how to use the JFXtras Table component, using this SpeedReaderFX program as the example.
The popup menu shown above enables the user to hide a given feed (l8r, Dean!), or open the URL associated with an item in a browser (in this case, Dean's Twitter post). Note that because JavaFX does not yet have a popup menu, I leveraged the menu bar/menu capabilities of the JFXtras project to make this slightly odd-looking popup menu. If a popup menu isn't available in the next version of JavaFX, one will be created in the JFXtras project.
To inform SpeedReaderFX about your items of interest, choose the Channels>Configure menu option. The Criteria dialog will appear (a portion shown below), enabling you to specify user names, tags, and blog feeds for various feed sources and social networks.
The prompts in the shorter text boxes indicate the type of information that may be typed into the box. For example, you'll separate the search tags in a Flickr text box with commas. The program has some text boxes filled in with examples, but you can overwrite them. A not-too-distant future version of this program will automatically save your criteria when exiting, and will load it again upon start-up. The SpeedReaderFX category of this blog will contain posts that point out some JavaFX code behind new functionality, as well as catching you up on some of the code used so far in this program. For example, I'll cite the JavaFX library that Rakesh Menon created to read the YouTube Data API, which I'm using in this program.
Selecting a check box causes the associated information source to be read and displayed in the table shown previously, with its items interspersed with items from other sources in reverse chronological order. Deselecting a check box removes the associated items from the table, which is the same result as the Hide this feed popup menu item discussed earlier.
When you're finished with the Criteria dialog, click the Close button in the upper right corner, or select the Channels>Configure menu option again. By the way, the View menu has a couple of options that help you manage the application's window, given that you'll be opening up content in a browser. One menu item is the self-explanatory Always on Top, and the other one enables you to Hide the Date Published Column.
Now that you know how to use SpeedReaderFX, please give it a whirl by clicking the Web Start Launch button below. As always, please leave a comment if you have any questions or comments.
Regards,
Jim Weaver
As I mentioned the previous article in this series, the next step in the development of the BandmatesFX program will be to enable the user to navigate more general information than just musicians and bands. In this article, I've added the ability to navigate soccer players and their teams. In addition, the program (whose working title is now Topic Map Navigator), can be deployed as a full-browser-page JavaFX applet. Shown below is a screenshot of the program running in a Chrome 2.0 browser, and it also runs in FireFox 3.5+ and Internet Explorer 7+. You'll usually need have Java SE 6 update 10+ in order for the applet to run in the browser (e.g. on Mac with Java 1.5 this app opens in its own window).
One of the features of this program is that it opens a web page when you right-click on an image. Deploying this program in the browser makes this a tighter experience, because the page appears on top of the Topic Map Navigator program. When you subsequently close the web page, the program is revealed again. The screenshot below shows the result of clicking the secondary mouse button on the picture of Lionel Messi, and then clicking the Wikipedia link from his Freebase web page that appears. You may also recall from previous articles in this series that clicking the primary mouse button on an image moves that person to the top of the app.
If you have a late-model Chrome, FireFox or Internet Explorer browser, and Java SE 6 update 10+, go ahead an run this program as a JavaFX applet. If not, you can launch the program via Java Web Start by clicking the Launch button below. Note: Please leave a comment if you have any difficulty deploying this JavaFX applet, providing your hardware/OS/browser/Java version information. I'll report these difficulties to the JavaFX JIRA issue tracker, as I'd like for the JavaFX applet deployment experience to be as solid as it is when deploying with Java Web Start.
Exercising Resizing
In order for the JavaFX applet to occupy the full browser page, we change the width and height of the applet to 100% in the HTML file as shown in the snippet below.
javafx(
{
archive: "TopicMapNavigator.jar",
draggable: true,
width: "100%",
height: "100%",
code: "org.jfxtras.topicmapnav.ui.TopicMapNavMain",
name: "TopicMapNavigator",
loading_image_url: "http://www.jmentor.com/JavaFX_1-2/TopicMapNavigator/images/guitar.png",
loading_image_width: 500,
loading_image_height: 350
}
);
When the browser page is resized, the width and height values of the JavaFX Stage change dynamically. You can bind to these values and control the appearance of the program, using layout classes that are provided with JavaFX. In addition, the JFXtras project has some classes such as ResizableScene, ResizableCustomNode, ResizableHBox and ResizableVBox that make quick work of responding to a resized Stage. For example, the Topic Map Navigator program uses these classes, and the resizable Shelf class, to automatically reposition and scale the cover flows when the user resizes the program.
Regards, and please let me know if you have any questions,
Jim Weaver
Jonathan Giles has announced the release of the JavaFX Menubar that he created for the JFXtras project. To demonstrate how to use it, I've added a menu to the BandmatesFX program that I've been developing in this series of blog posts.
By the way, my grand scheme for the BandmatesFX program is for it to become what I'm calling an image-based knowledge navigation facility. It's going to be an open-source JFXtras example, where Sten Anderson (JavaFX Coding Challenge winner) and others will collaborate with me in its continued design and development. A baby step in that direction has been to add the ability to reference space mission information so that users can interact with astronauts and missions in the same way they can now interact with musicians and bands. Here's a screenshot of the program with the JFXtras menu:
As you can see from the screenshot, there are a couple of items in the View menu, enabling the user to toggle the following states:
- The Google Map menu item controls whether the map shown above is visible.
- The Topics with No Image menu item controls whether topics (e.g. astronauts, missions, musicians, and bands) that don't have an associated image in the Freebase database) will be displayed.
Go ahead and launch the application using the Java Web Start Launch button below, consulting past articles such as this one in the series for instructions on how to use it. Note: Now that there is a Help menu, it'd probably be a good idea for me to put the instructions in a help facility :-P
Here's the menu-related code in this program:
var menubar: MenuBar;
menubar = MenuBar {
width: bind sceneRef.width;
menus: [
// View Menu
Menu {
text: "View"
items: [
CheckedMenuItem {
text: "Google Map"
checked: bind showLocationMap with inverse
graphic: bind if (showLocationMap) {
ImageView {
image: Image {
url: "{__DIR__}images/lightbulb_on.png";
}
}
}
else {
ImageView {
image: Image {
url: "{__DIR__}images/lightbulb.png";
}
}
}
},
CheckedMenuItem {
text: "Topics with No Image"
checked: bind showTopicsThatHaveNoImage with inverse
action: function() {
obtainGroupsForArtist(artistToSearch);
}
graphic: bind if (showTopicsThatHaveNoImage) {
ImageView {
image: Image {
url: "{__DIR__}images/lightbulb_on.png";
}
}
}
else {
ImageView {
image: Image {
url: "{__DIR__}images/lightbulb.png";
}
}
}
}
]
},
// Help Menu
Menu {
text: "Help"
items: [
MenuItem {
text: "About"
action: function() {
Alert.inform("About BandmatesFX",
"Developed by Jim Weaver/JFXtras team to "
"demonstrate JFXtras (and Freebase) features.");
}
graphic: ImageView {
image: Image {
url: "{__DIR__}images/about.png";
}
}
}
]
}
]
}
Jonathan has done a great job in defining a simple API, as well as creating an appearance for these menus that blend well with the Caspian skin in the JavaFX 1.2 UI controls. Thanks Jonathan!
Regards,
Jim Weaver
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
Prolific reviewer highway_star had this to say about the Three Dog Night Captured Live at the Forum album (paraphrased a bit).
"Three Dog Night was one of the seventies' most popular groups. Even though most of their songs were composed by other songwriters (Paul Williams, Hoyt Axton, Randy Newman and Nilsson to name a few), the combination of three excellent lead singers and a fantastic backup band ensured that they had more hits than most bands. This concert recorded on September 12th, 1969 at The Forum in Los Angeles, is one of the best concerts this reviewer has ever heard."
As an aside, I was surprised to find out that Randy Newman wrote the Three Dog Night hit song entitled "Mama Told Me Not to Come" :-O
At any rate, if you missed Three Dog Night live at the Forum, as a consolation I can highly recommend the JFXtras and WidgetFX presentation captured live at the SDForum.
If you want to keep up on the latest additions to these JavaFX open source projects, then watch the video of this presentation in which Stephen Chin and Keith Combs skillfully walk you through the very cool and useful features!
Regards,
Jim Weaver
So far in this Freebase Contributing Artists App series, we've been developing a JavaFX application that enables the user to navigate connections among musicians. I'm calling this application BandmatesFX, and it uses the JSONHandler feature of the JFXtras open source library to easily query the Freebase.com database.
Note: While using this app, I noticed that drummer Aynsley Dunbar has played with lots of bands over the years, so he's a good starting point for trying to navigate to another given musician in six or less degrees. Try, for example, to navigate from Aynsley Dunbar to Roy Orbison. Here's one way in five degrees:
- Aynsley Dunbar played in The Animals, with Andy Summers
- Andy Summers played in The Police, with Sting
- Sting played in Band Aid, with Paul McCartney
- Paul McCartney played in The Quarrymen (and of course The Beatles), with George Harrison
- George Harrison played in The Traveling Wilburys, with Roy Orbison
Today I'm going to point out some enhancements to this application, and the updated code, which are reflected in the following screenshot:
These enhancements include:
- The user can type in an artist name, and with each keystroke the Freebase database web service is invoked, returning suggested names from which the user can choose.
- Band and artist pictures are presented in a cover-flow style, using the JFXtras Shelf component. In addition, the JFXtras TitledBorder component is employed to enclose and label the cover-flows.
- When the user clicks the central image in the bottom (e.g. Steve Smith in the screenshot above), that artist becomes the featured artist at the top of the page. The group cover-flow images are updated to reflect the groups in which the artist has played, and the member cover-flow images contain the artists that have played in the currently selected group.
One of the cool feature of the JFXtras Shelf component is that the mouse wheel moves the images horizontally on the Shelf, enabling the user to navigate even more quickly than clicking on one of the images. Before invoking this application from the Web Start link at the end of this article, take a look at the main script of the program (named BandmatesFX.fx) in the listing below:
/*
* BandmatesMain.fx
*
* Uses Freebase and JFXtras with JavaFX to explore connections
* between musical artists
*
* Developed by James L. Weaver to demonstrate using JavaFX and JFXtras
*/
package javafxpert;
import javafx.io.http.HttpRequest;
import javafx.animation.transition.*;
import javafx.geometry.HPos;
import javafx.scene.*;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.ListView;
import javafx.scene.control.TextBox;
import javafx.scene.image.*;
import javafx.scene.input.*;
import javafx.scene.layout.*;
import javafx.scene.paint.*;
import javafx.scene.text.*;
import javafx.stage.Stage;
import org.jfxtras.data.pull.*;
import org.jfxtras.scene.border.TitledBorder;
import org.jfxtras.scene.control.Shelf;
/**
* Height of the images that we'll be working with
*/
def IMAGE_HEIGHT:Integer = 140;
/**
* Artist for which we're searching bandmates
*/
var artistToSearch:String = "/en/aynsley_dunbar";
/**
* Name for which we're finding matches and ids
*/
var nameToFind:String = "Aynsley Dunbar";
var textBoxRef:TextBox;
var listViewRef:ListView;
var selectedNameIndex = bind listViewRef.selectedIndex on replace {
def selectedId = freebaseSearchResult.result[selectedNameIndex].id;
if (selectedId != "") {
println("selectedId:{selectedId}");
nameToFind = freebaseSearchResult.result[selectedNameIndex].name;
obtainGroupsForArtist(selectedId);
}
};
/**
* Hover text that contains the name of the artist or group
*/
var nameHoverText:String;
/**
* A reference to the HTTP request, for the purpose of monitoring progress
*/
var req:HttpRequest;
/**
* The root class that will hold the object graph from the JSON results
*/
var freebaseResult:FreebaseResult;
/**
* The root class that will hold the object graph from the JSON results
*/
var freebaseSearchResult:FreebaseSearchResult;
/**
* The base URL for the freebase query
*/
def freebaseURL = "http://www.freebase.com/api/service/mqlread?";
/**
* The base URL for a freebase search request
*/
def freebaseSearchURL = "http://www.freebase.com/api/service/search?";
/**
* The base URL to get a freebase image
*/
def freebaseImageURL = "http://img.freebase.com/api/trans/image_thumb";
var artistsNode:Node;
/**
* Fade in transition for artists
*/
def shelfFadeIn = FadeTransition {
node: bind artistsNode
duration: 1500ms
fromValue: 0.2
toValue: 1.0
}
/**
* Indexe into groups list
*/
var groupOneIndex:Integer on replace {
shelfFadeIn.playFromStart();
// Choose the middle one
def numArtists:Integer = sizeof freebaseResult.result.
musicGroupMemberMembership[groupOneIndex].
group.musicMusicalGroupMember;
artistTwoIndex = if (numArtists > 1) numArtists / 2 else 0;
groupOneName = freebaseResult.result.
musicGroupMemberMembership[groupOneIndex].group.name;
};
/**
* Name of the currently selected band
*/
var groupOneName:String;
/**
* Index into artists list
*/
var artistTwoIndex:Integer;
/**
* Create a search query and invoke the JSON handler
*/
function obtainIdForArtistPartialName(artistPartialName:String) {
listViewRef.visible = true;
nameToFind = artistPartialName;
def partialName = artistPartialName.replace(" ", "+");
var searchUrl = "{freebaseSearchURL}prefix={partialName}&type=/music/artist&limit=10&mql_output=[\{\"id\":null,\"name\":null\}]";
println("searchUrl:{searchUrl}");
var albumHandler:JSONHandler = JSONHandler {
rootClass: "javafxpert.FreebaseSearchResult"
onDone: function(obj, isSequence): Void {
freebaseSearchResult = obj as FreebaseSearchResult;
println("# of search results:{sizeof freebaseSearchResult.result},freebaseSearchResult:{freebaseSearchResult.code}");
req.stop();
}
};
req = HttpRequest {
location: searchUrl
onInput: function(is: java.io.InputStream) {
albumHandler.parse(is);
}
};
req.start();
}
/**
* Create the Freebase query and invoke the JSON handler
*/
function obtainGroupsForArtist(artistFreebaseId:String) {
listViewRef.visible = false;
artistToSearch = artistFreebaseId;
var queryUrl = "{freebaseURL}query=\{\"query\":"
" \{ "
" \"/common/topic/image\": [\{ "
" \"id\": null "
" \}], "
" \"/music/group_member/membership\": [\{ "
" \"group\": \{ "
" \"name\": null, "
" \"id\": null, "
" \"/common/topic/image\": [\{ "
" \"id\": null "
" \}], "
" \"/music/musical_group/member\": [\{ "
" \"member\": \{ "
" \"name\": null, "
" \"id\": null, "
" \"/common/topic/image\": [\{ "
" \"id\": null "
" \}] "
" \} "
" \}] "
" \} "
" \}], "
" \"id\": \"{artistFreebaseId}\", "
" \"name\": null, "
" \"type\": \"/music/artist\" "
" \} \}";
println("queryUrl:{queryUrl}");
var albumHandler:JSONHandler = JSONHandler {
rootClass: "javafxpert.FreebaseResult"
onDone: function(obj, isSequence): Void {
freebaseResult = obj as FreebaseResult;
println("# of bands:{sizeof freebaseResult.result.musicGroupMemberMembership}");
req.stop();
// Choose the middle one
def numGroups:Integer = sizeof freebaseResult.result.musicGroupMemberMembership;
groupOneIndex = if (numGroups > 1) numGroups / 2 else 0;
}
};
req = HttpRequest {
location: queryUrl
onInput: function(is: java.io.InputStream) {
albumHandler.parse(is);
}
};
req.start();
}
var sceneRef:Scene;
Stage {
title: bind "BandmatesFX - {nameToFind}"
scene: sceneRef = Scene {
width: 1000
height: 700
content: [
ImageView {
image: Image {
url: "http://www.popsci.com/files/imagecache/article_image_large/files/articles/guitar.gif"
}
fitWidth: bind sceneRef.width
preserveRatio: true
opacity: 0.1
},
VBox {
nodeHPos: HPos.CENTER
layoutY: 10
spacing: 10
content: [
HBox {
spacing: 10
content: [
ImageView {
image: bind Image {
url: "{freebaseImageURL}{artistToSearch}?maxheight={IMAGE_HEIGHT}"
}
},
VBox {
content: [
textBoxRef = TextBox {
promptText: "Enter artist name"
text: bind nameToFind with inverse
columns: 20
font: Font.font(null, 14)
action:function():Void {
if (nameToFind != "" and (req.percentDone == 0.0 or req.percentDone == 100.0)) {
obtainIdForArtistPartialName(nameToFind);
}
}
onKeyTyped:function(ke:KeyEvent) {
println("req.percentDone:{req.percentDone}");
if (textBoxRef.rawText != "" and (req.percentDone == 0.0 or req.percentDone == 100.0)) {
obtainIdForArtistPartialName(textBoxRef.rawText);
}
}
},
listViewRef = ListView {
visible: false
height: 100
items: bind for (rslt in freebaseSearchResult.result) {
rslt.name
}
layoutInfo: LayoutInfo {
width: bind textBoxRef.width - 10
height: 80
}
}
]
},
ProgressIndicator {
progress: bind req.progress
},
Label {
text: bind nameHoverText
font: Font.font(null, FontWeight.BOLD, 18)
}
]
},
TitledBorder {
text: bind "{nameToFind} played in these groups:"
font: Font.font(null, FontWeight.BOLD, 14)
lineColor: Color.GREY
node: Shelf {
reflection: false
showScrollBar: false
showText: true
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;
}
index: bind groupOneIndex with inverse
thumbnailWidth: IMAGE_HEIGHT
thumbnailHeight: IMAGE_HEIGHT
layoutInfo: LayoutInfo {
width: bind sceneRef.width
height: IMAGE_HEIGHT + 120
}
}
},
artistsNode = Panel {
content: bind for (grp in freebaseResult.result.musicGroupMemberMembership) {
TitledBorder {
text: bind "{groupOneName} has had the following members:"
font: Font.font(null, FontWeight.BOLD, 14)
lineColor: Color.GREY
node: Shelf {
visible: bind indexof grp == groupOneIndex
reflection: false
showScrollBar: false
showText: true
imageUrls: bind for (mmbr in freebaseResult.result.
musicGroupMemberMembership[indexof grp].
group.musicMusicalGroupMember) {
"{freebaseImageURL}{mmbr.member.id}?maxheight={IMAGE_HEIGHT}"
}
imageNames: bind for (mmbr in freebaseResult.result.
musicGroupMemberMembership[indexof grp].
group.musicMusicalGroupMember) {
mmbr.member.name
}
onCenterImagePressed:function(me:MouseEvent):Void {
var mmbr = freebaseResult.result.
musicGroupMemberMembership[indexof grp].
group.musicMusicalGroupMember[artistTwoIndex];
nameToFind = mmbr.member.name;
artistToSearch = mmbr.member.id;
obtainGroupsForArtist(mmbr.member.id);
}
index: bind artistTwoIndex with inverse
thumbnailWidth: IMAGE_HEIGHT
thumbnailHeight: IMAGE_HEIGHT
layoutInfo: LayoutInfo {
width: bind sceneRef.width
height: IMAGE_HEIGHT + 120
}
}
}
}
layoutInfo: LayoutInfo {
width: bind sceneRef.width
height: IMAGE_HEIGHT + 120
}
}
]
}
]
}
}
obtainGroupsForArtist(artistToSearch);
To try out this application, click the Web Start link below. Since this application is a work in progress, I'll give you a couple of pointers:
- When typing the name of an artist in the text box, each key invokes a web service, so wait until the progress indicator is solid before typing the final character (I'll make this smoother in a future iteration).
- Not all artists are in groups, and I'm not doing the necessary exception handling yet to catch this in the response from Freebase. I know that many artists such as Eric Clapton, Bob Dylan, and Steve Winwood work, as well as any artist that you can click in the bottom cover flow (by definition they are members of a group).
For more background on this application, see the previous article in this series. As always, please leave a comment if you have any questions. Also, if you have any suggestions for future enhancements, please leave a comment as well.
Regards,
Jim Weaver
Philip Dodds was a brilliant audio engineer, an e-learning pioneer, and operated the synthesizer in Close Encounters of the Third Kind (he's the one in the middle of the picture shown from the movie). Philip used to say that "working code trumps all theories", which is very relevant to the the JavaFX examples repository library announcement that Stephen Chin made this morning.
This repository, which is located in the JFXtras Community Site, contains working code and Web Start links for a growing number of examples in JavaFX books and blogs.
You are cordially invited to take a look at the examples in the site, and contribute your own JavaFX examples! To get you started, here's the link to the site that contains a Color Picker example from the Pro JavaFX book.
Regards,
Jim Weaver
Congratulations to the winners of the JavaFX Coding Challenge: Sten Anderson, Naoaki Suganuma, and Evgeni Sergeev! The apps written by all three winners are oustanding, and 1st-place winner Sten Anderson's Music Explorer FX app is IMHO brilliant! Here's a screenshot from the app:
Sten's JavaFX app enables the user to navigate among musical artists whose music has a degree of similarity by making use of Echo Nest web services. The Music Explorer FX app has a graphically pleasing user interface that helps the user explore interesting details about musical artists and the connections between them. The Help page even features one of my favorite groups: Liquid Tension Experiment :-)
Congrats again to Sten, Naoaki and Evgen!
Jim Weaver
It's great to finally be able to give you some details about a cool JavaFX project in which I've had the privilege of participating. For the last couple of months, some co-workers at Veriana and I have been helping Indaba Music create an internet-based, collaborative recording studio named Indaba Console 2.0. The alpha release was this morning (July 9, 2009) and it is written entirely in JavaFX/Java. Check out this screenshot, and a Wired article that contains a demonstration video by musician and co-founder Dan Zaccagnino.
If you were at JavaOne 2009 you may remember the JavaFX panel session on which Chris Danzig (Indaba co-founder) and I participated.
One of the main reasons that Indaba chose JavaFX/Java is that it enables recording high-quality audio directly onto the client platform. This is achieved by leveraging the capability of JavaFX to call Java methods, and Java to call JavaFX functions. One person with whom we work closely is Bjorn Roche, who created a Java-based facility and API that interacts with the audio capabilities of the client machine. The JavaFX portion of the Console provides most of the UI, and uses the Java-based API when it needs to play a track, apply audio effects, render a sound-wave, or record music. This Java library then invokes JavaFX callback functions as it is doing its work.
The UI consists of many custom components, some of which subclass CustomNode, and many which subclass Contol (when skinnable and/or highly interactive components are required). Recording session, mix, and audio data are stored "in the cloud", as they say. The application is written in JavaFX 1.1, but is being ported to JavaFX 1.2, where performance and memory usage are greatly optimized.
Congrats, and thanks to the Indaba folks! Please give the music console a whirl and unleash your musical creativity!
Regards,
Jim Weaver
Long-time readers of this blog will remember some JavaFX examples in which I query the Freebase.com database, which holds structured information on just about any topic. Today's post uses JavaFX, with some help from the JFXtras open source project, to query the Freebase database for groups that an artist has played with, and for the artists that have played in those groups. For example, the screenshot of the BandmatesFX app below shows the results of querying this info on Eric Clapton:
To indicate which artist you'd like to query, type the Freebase ID (which as shown above is usually their Wikipedia ID with "/en/" appended to the beginning). Pressing the Enter key or clicking the Search button initiates the search, which sends a query articulated in the JSON-based Metaweb Query Language (MQL) The left column of the grid shows the groups that Clapton has played in, and the other images in each row are the artists that have played with that row's group. Hovering over an image of a band or artist causes the name (in this case Steve Winwood) to appear at the top of the app.
Clicking on an artist performs the same behavior described above on the chosen artist, resulting in the following screenshot that shows bands and artists that Winwood has played with.
Clicking Dave Mason (who was a bandmate with Steve Winwood in Traffic) as shown above will produce the bands and artists with which he has played, ad infinitum.
One of the key enablers of this application is the JSONHandler class (developed by Jim Clarke) found in the JFXtras project. Take a look at the following listing of the main script in this application to see how it is used.
/*
* BandmatesMain.fx
*
* Uses Freebase and JFXtras with JavaFX to explore connections
* between musical artists
*
* Developed by James L. Weaver to demonstrate using JavaFX and JFXtras
*/
package javafxpert;
import javafx.io.http.HttpRequest;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.TextBox;
import javafx.scene.image.*;
import javafx.scene.input.*;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.*;
import javafx.stage.Stage;
import org.jfxtras.data.pull.*;
import org.jfxtras.scene.layout.*;
/**
* Artist for which we're searching bandmates
*/
var artistToSearch:String = "/en/eric_clapton";
/**
* Hover text that contains the name of the artist or group
*/
var nameHoverText:String;
/**
* A reference to the HTTP request, for the purpose of monitoring progress
*/
var req:HttpRequest;
/**
* The root class that will hold the object graph from the JSON results
*/
var freebaseResult:FreebaseResult;
/**
* The base URL for the freebase query
*/
def freebaseURL = "http://www.freebase.com/api/service/mqlread?";
/**
* The base URL for a freebase image
*/
def freebaseImageURL = "http://img.freebase.com/api/trans/image_thumb";
/**
* Create the Freebase query and invoke the JSON handler
*/
function obtainGroupsForArtist(artistFreebaseId:String) {
artistToSearch = artistFreebaseId;
var queryUrl = "{freebaseURL}query=\{\"query\":"
" \{ "
" \"/common/topic/image\": [\{ "
" \"id\": null "
" \}], "
" \"/music/group_member/membership\": [\{ "
" \"group\": \{ "
" \"name\": null, "
" \"id\": null, "
" \"/common/topic/image\": [\{ "
" \"id\": null "
" \}], "
" \"/music/musical_group/member\": [\{ "
" \"member\": \{ "
" \"name\": null, "
" \"id\": null, "
" \"/common/topic/image\": [\{ "
" \"id\": null "
" \}] "
" \} "
" \}] "
" \} "
" \}], "
" \"id\": \"{artistFreebaseId}\", "
" \"name\": null, "
" \"type\": \"/music/artist\" "
" \} \}";
println("queryUrl:{queryUrl}");
var albumHandler:JSONHandler = JSONHandler {
rootClass: "javafxpert.FreebaseResult"
onDone: function(obj, isSequence): Void {
freebaseResult = obj as FreebaseResult;
println("# of bands:{sizeof freebaseResult.result.musicGroupMemberMembership}");
req.stop();
}
};
req = HttpRequest {
location: queryUrl
onInput: function(is: java.io.InputStream) {
albumHandler.parse(is);
}
};
req.start();
}
Stage {
title: "BandmatesFX"
scene: Scene {
width: 1000
height: 750
content: [
VBox {
layoutX: 10
layoutY: 10
spacing: 10
content: [
HBox {
spacing: 10
content: [
ImageView {
image: bind Image {
url: "{freebaseImageURL}{artistToSearch}?maxheight=80"
}
},
TextBox {
text: bind artistToSearch with inverse
columns: 20
font: Font.font("arial", 14)
action:function():Void {
obtainGroupsForArtist(artistToSearch);
}
},
Button {
text: "Search"
strong: true
action:function():Void {
obtainGroupsForArtist(artistToSearch);
}
},
ProgressIndicator {
progress: bind req.progress
},
Label {
text: bind nameHoverText
font: Font.font("arial", FontWeight.BOLD, 18)
}
]
},
Grid {
rows: bind for (grp in freebaseResult.result.musicGroupMemberMembership)
Row {
cells: [
ImageView {
image: Image {
url: "{freebaseImageURL}{grp.group.id}?maxheight=80"
}
onMouseEntered:function(me:MouseEvent):Void {
nameHoverText = grp.group.name;
}
onMouseExited:function(me:MouseEvent):Void {
nameHoverText = "";
}
},
for (mmbr in freebaseResult.result.
musicGroupMemberMembership[indexof grp].
group.musicMusicalGroupMember)
ImageView {
image: Image {
url: "{freebaseImageURL}{mmbr.member.id}?maxheight=80"
backgroundLoading: true
}
onMousePressed:function(me:MouseEvent):Void {
artistToSearch = mmbr.member.id;
obtainGroupsForArtist(mmbr.member.id);
}
onMouseEntered:function(me:MouseEvent):Void {
nameHoverText = mmbr.member.name;
}
onMouseExited:function(me:MouseEvent):Void {
nameHoverText = "";
}
}
]
}
}
]
}
]
}
}
As you can see in the listing above, we construct an MQL query that will return information about the bands, and the artists that have played in those bands. We then supply the JSONHandler with a rootClass, an instance of which will be created and populated with a graph of objects that represents the JSON stream returned from the Freebase server. JSONHandler uses JavaFX reflection to create instances of classes that correspond to each element name, shown below:
package javafxpert;
public class FreebaseResult {
public var code: String;
public var result: Result;
public var status: String;
public var transactionId: String;
}
package javafxpert;
public class Result {
public var commonTopicImage: ArtistImage[] ;
public var musicGroupMemberMembership: MusicGroupMemberMembership[];
public var id: String;
public var name: String;
public var type: String;
}
package javafxpert;
public class ArtistImage {
public var id: String;
}
package javafxpert;
public class MusicGroupMemberMembership {
public var group: Group;
}
package javafxpert;
public class Group {
public var commonTopicImage: ArtistImage[] ;
public var musicMusicalGroupMember: MusicMusicalGroupMember[];
public var name: String on replace {
name = name.replaceAll("&", "&");
};
public var id: String;
}
package javafxpert;
public class MusicMusicalGroupMember {
public var member: Member;
}
package javafxpert;
public class Member {
public var commonTopicImage: ArtistImage[] ;
public var name: String on replace {
name = name.replaceAll("&", "&");
};
public var id: String;
}
As you can see, JSONHandler makes quick work out of populating object graphs from JSON streams, doing the heavy lifting for you.
Another facility of the JFXtras project that we're using is the Grid layout container, as shown previously in the BandmatesMain.fx code listing above. While you're looking at that listing, notice how easy it is in JavaFX 1.2 to create a ProgressIndicator that reflects the progress of a task such as HttpRequest.
To get the most out of this post, please build and run this application, putting the JFXtras JAR file (that you can get from the JFXtras Core open source project) on the classpath. If you'd like to short-circuit the JavaFX learning process, click this Java Web Start link:
Also, this is the first cut of the BandmatesFX app, so please leave a comment with ideas for additional features. And if you possess the graphics design gene (as I admittedly don't), I'd be honored if you'd create a mock-up to make this app look graphically pleasing.
Regards,
Jim Weaver







