Jim has posted 66 posts at DZone. You can read more from them at their website. View Full User Profile

More Cowbell: An Imaginary Audio Control Example in JavaFX

12.26.2008
| 5123 views |
  • submit to reddit

One of my favorite Saturday Night Live sketches is More Cowbell, in which Christopher Walken's character keeps asking for "more cowbell" during a recording session.  Today's example covers some of the simple but powerful concepts of JavaFX in the context of an imaginary iPhone-esque application that lets you select a music genre and control the volume.  Of course, "Cowbell Metal", shortened to "Cowbell", is one of the available genres :-)   Click the screenshot below to launch the application, and then I'll show you the code behind it.

Audio_configuration


Application Behavior and the Code Behind It

When you play with the application, notice that adjusting the volume slider changes the associated decibel (dB) level displayed.  Also, selecting the Muting checkbox disables the slider, and selecting various genres changes the volume slider.  This behavior is enabled by concepts that you'll see in the code below, such as binding to a class that contains a model, on replace triggers, and sequences (think arrays).

Here is the main program, which contains the declarative script that expresses the UI:

/*
* AudioConfigMain.fx - A JavaFX Script example program that demonstrates
* "the way of JavaFX" (binding to model classes, triggers, sequences, and
* declaratively expressed, node-centric UIs). Note: Because this example
* covers beginning JavaFX concepts, it is more verbose than necessary.
*
* Developed 2008 by James L. Weaver jim.weaver [at] javafxpert.com
* as a JavaFX Script SDK 1.0 example for the Pro JavaFX book.
*/
package projavafx.audioconfig.ui;

import javafx.ext.swing.*;
import javafx.stage.Stage;
import javafx.scene.*;
import javafx.scene.paint.*;
import javafx.scene.shape.*;
import javafx.scene.text.*;
import javafx.scene.transform.*;
import projavafx.audioconfig.model.AudioConfigModel;

Stage {
var acModel = AudioConfigModel {
selectedDecibels: 35
}
title: "Audio Configuration"
scene: Scene {
content: [
Rectangle {
x: 0
y: 0
width: 320
height: 45
fill: LinearGradient {
startX: 0.0
startY: 0.0
endX: 0.0
endY: 1.0
stops: [
Stop {
color: Color.web("0xAEBBCC")
offset: 0.0
},
Stop {
color: Color.web("0x6D84A3")
offset: 1.0
},
]
}
},
Text {
translateX: 65
translateY: 12
textOrigin: TextOrigin.TOP
fill: Color.WHITE
content: "Audio Configuration"
font: Font {
name: "Arial Bold"
size: 20
}
},
Rectangle {
x: 0
y: 43
width: 320
height: 300
fill: Color.rgb(199, 206, 213)
},
Rectangle {
x: 9
y: 54
width: 300
height: 130
arcWidth: 20
arcHeight: 20
fill: Color.color(1.0, 1.0, 1.0)
stroke: Color.color(0.66, 0.67, 0.69)
},
Text {
translateX: 18
translateY: 69
textOrigin: TextOrigin.TOP
fill: Color.web("0x131021")
content: bind "{acModel.selectedDecibels} dB"
font: Font {
name: "Arial Bold"
size: 18
}
},
SwingSlider {
translateX: 120
translateY: 69
width: 175
enabled: bind not acModel.muting
minimum: bind acModel.minDecibels
maximum: bind acModel.maxDecibels
value: bind acModel.selectedDecibels with inverse
},
Line {
startX: 9
startY: 97
endX: 309
endY: 97
stroke: Color.color(0.66, 0.67, 0.69)
},
Text {
translateX: 18
translateY: 113
textOrigin: TextOrigin.TOP
fill: Color.web("0x131021")
content: "Muting"
font: Font {
name: "Arial Bold"
size: 18
}
},
SwingCheckBox {
translateX: 280
translateY: 113
selected: bind acModel.muting with inverse
},
Line {
startX: 9
startY: 141
endX: 309
endY: 141
stroke: Color.color(0.66, 0.67, 0.69)
},
Text {
translateX: 18
translateY: 157
textOrigin: TextOrigin.TOP
fill: Color.web("0x131021")
content: "Genre"
font: Font {
name: "Arial Bold"
size: 18
}
},
SwingComboBox {
translateX: 204
translateY: 148
width: 93
items: bind for (genre in acModel.genres) {
SwingComboBoxItem {
text: genre
}
}
selectedIndex: bind acModel.selectedGenreIndex with inverse
}
]
}
}

Notice how the bind operator is used in various places to cause the UI to reflect the state of the model.  In a couple of places, a bind with inverse is employed to keep the UI and the model class in sync bi-directionally.  Now take a look at the model class, and in particular the on replace trigger that is invoked when the user selects a genre:

/*
* AudioConfigModel.fx - The model class behind a JavaFX Script example
* program that demonstrates "the way of JavaFX" (binding to model classes,
* triggers, sequences, and declaratively expressed, node-centric UIs).
*
* Developed 2008 by James L. Weaver jim.weaver [at] javafxpert.com
* as a JavaFX Script SDK 1.0 example for the Pro JavaFX book.
*/
package projavafx.audioconfig.model;

/**
* The model class that the AudioConfigMain.fx script uses
*/
public class AudioConfigModel {
/**
* The minimum audio volume in decibels
*/
public var minDecibels:Integer = 0;

/**
* The maximum audio volume in decibels
*/
public var maxDecibels:Integer = 160;

/**
* The selected audio volume in decibels
*/
public var selectedDecibels:Integer = 0;

/**
* Indicates whether audio is muted
*/
public var muting:Boolean = false;


/**
* List of some musical genres
*/
public var genres = [
"Chamber",
"Country",
"Cowbell",
"Metal",
"Polka",
"Rock"
];

/**
* Index of the selected genre
*/
public var selectedGenreIndex:Integer = 0 on replace {
if (genres[selectedGenreIndex] == "Chamber") {
selectedDecibels = 80;
}
else if (genres[selectedGenreIndex] == "Country") {
selectedDecibels = 100;
}
else if (genres[selectedGenreIndex] == "Cowbell") {
selectedDecibels = 150;
}
else if (genres[selectedGenreIndex] == "Metal") {
selectedDecibels = 140;
}
else if (genres[selectedGenreIndex] == "Polka") {
selectedDecibels = 120;
}
else if (genres[selectedGenreIndex] == "Rock") {
selectedDecibels = 130;
}
};
}

As always, please leave a comment if you have any questions!

Learn JavaFX in Stockholm, Sweden in January 2009

I'll be speaking on JavaFX at the Jfokus 2009 conference in January.  While in the area, I will also be conducting a two day public JavaFX class in Stockholm on January 29 & 30, 2009 entitled "Rich Internet Application Development with JavaFX".

Regards,
Jim Weaver
JavaFXpert.com

 

Published at DZone with permission of its author, Jim Weaver.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags: