A Note from Jim Weaver:
First of all, I would like to wish you a Happy and Blessed New Year! I'm excited about the momentum that JavaFX has achieved since its 1.0 release in December 2008, and about its increased adoption for RIA and mobile development in 2009.
Also, I'd like to introduce Dean Iverson, the author of today's article, who for many of you needs no introduction because he's been active in the Java/Swing/JavaFX community for some time now. Dean will be a regular contributor to the JavaFXpert blog, as well as to the JFXtras project that he mentions in todays article.
Dean has been writing software professionally for over 15 years. In
that time he has worked on everything from games, to driving
simulators, to large-scale enterprise applications and just about
everything in between. Currently, he is employed by the Virginia Tech
Transportation Institute where he has a cool job (of which I'm jealous) as a senior researcher where he is working on
rich client applications. He also has a small
software consultancy called Pleasing Software Solutions which he
co-founded along with his wife who, by his own admission, is the
brains of the outfit and the better programmer. I personally think that his wife develops these JavaFX examples and that he just writes about them :-) Also, he is the Technical Reviewer of the upcoming Pro JavaFX book that Weiqi Gao, Stephen Chin and I are currently writing. With that being said, please enjoy Dean's first post on the JavaFXpert blog:
JavaFX Skins Game
Simon Morris, author of the upcoming JavaFX in Action book, has just written a very informative
post
on his blog. He shows how to create a custom progress bar that is skinnable using JavaFX's support
for CSS. It's definitely worth a read. Using CSS you can customize just about every aspect of the rectangles
that Simon uses in his progress meter: their size, color, even how many there are! It's the ultimate in
flexibility.
A New Shape
But what if you want to use a different shape entirely? Even that is possible without having to recompile
Simon's original code. For example, there is a new shape in the
JFXtras project
called the VariableCornerRectangle. This shape is a rectangle that can have any combination of rounded or
square corners. It looks like this:
The code to produce the shape above looks like this:
VariableCornerRectangle {
ulArc: 80
urArc: 0
lrArc: 80
llArc: 0
width: 200
height: 200
fill: Color.CORNFLOWERBLUE
stroke: Color.BLUE
strokeWidth: 5
}
We just use the shape's corner arc attributes to set the upper-left and lower-right corners as arcs with a
radius of 80 pixels. We then set the other corners to be square by using an arc size of 0.
A New Skin
So how do we use this new shape in Simon's progress meter? First, we create a new skin class. It will
be almost exactly the same as Simon's, but it will use the new shape.
public class VariableCornerProgressSkin extends Skin {
public var boxCount:Integer = 10;
public var boxWidth:Number = 15;
public var boxHeight:Number = 20;
public var boxHGap:Number = 2;
public var unsetHighColor:Color = Color.YELLOW;
public var unsetMidColor:Color = Color.GREEN;
public var unsetLowColor:Color = Color.DARKGREEN;
public var setHighColor:Color = Color.ORANGE;
public var setMidColor:Color = Color.RED;
public var setLowColor:Color = Color.DARKRED;
var boxValue:Integer = bind {
var p:Progress = control as Progress;
var v:Number = (p.value - p.minimum) / (p.maximum - p.minimum);
(boxCount * v) as Integer;
}
init {
def border:Number = bind boxWidth / 10;
def arc:Number = bind boxWidth / 2;
def lgUnset:LinearGradient = bind makeLG(unsetHighColor,unsetMidColor,unsetLowColor);
def lgSet:LinearGradient = bind makeLG(setHighColor,setMidColor,setLowColor);
scene = HBox {
spacing: bind boxHGap;
content: bind for(i in [0..<boxCount]) {
Group {
content: [
VariableCornerRectangle {
width: bind boxWidth;
height: bind boxHeight;
ulArc: bind arc;
lrArc: bind arc;
urArc: 0
llArc: 0
fill: bind if(i < boxValue) setLowColor else unsetLowColor;
},
VariableCornerRectangle {
translateX: bind border;
translateY: bind border;
width: bind boxWidth - border * 2;
height: bind boxHeight - border * 2;
ulArc: bind arc;
lrArc: bind arc;
urArc: 0
llArc: 0
fill: bind if(i < boxValue) lgSet else lgUnset;
}
]
}
}
}
}
function makeLG(c1:Color,c2:Color,c3:Color) : LinearGradient {
LinearGradient {
endX: 0;
endY: 1;
proportional: true;
stops: [
Stop {
offset:0;
color: c2;
},
Stop {
offset:0.25;
color: c1;
},
Stop {
offset:0.50;
color: c2;
},
Stop {
offset:0.85;
color: c3;
}
]
}
}
}
A Small Change Of Style
Simon's original code used the following CSS to style the three different progress meters in his sample
program.
"Progress" {
boxWidth: 15;
boxHGap: 2;
setHighColor: yellow;
setMidColor: red;
setLowColor: darkred;
unsetHighColor: cyan;
unsetMidColor: blue;
unsetLowColor: darkblue;
}
"Progress"#testId {
boxWidth: 25;
boxHeight: 30;
boxCount: 7;
boxHGap: 1;
unsetHighColor: white;
unsetMidColor: silver;
unsetLowColor: dimgray;
}
"Progress".testClass {
boxWidth: 7;
boxHGap: 2;
boxCount: 20;
setHighColor: yellow;
setMidColor: limegreen;
setLowColor: darkgreen;
}
This stylesheet produced the three progress meters in Simon's original example.
All we have to do to use our new skin is to add a new CSS selector. In this case I want to change
the skin of the progress meter with the id of testId
. So I'll add a new selector just
above the existing one that applies the new skin.
"Progress"#testId {
skin: javafx_css.VariableCornerProgressSkin;
}
"Progress"#testId {
boxWidth: 25;
boxHeight: 30;
boxCount: 7;
boxHGap: 1;
unsetHighColor: white;
unsetMidColor: silver;
unsetLowColor: dimgray;
}
And voila, we have a new progress meter! Notice that the middle meter (whose id is testId
)
is the only one that changed. Click the Java Web Start Launch icon below to see it in action.
I have used the fully qualified path name for the VariableCornerProgressSkin class in the selector's
skin
declaration. In this case, the class is located in my javafx_css package.
It is important that the selector with the skin:
declaration be a separate selector and be
placed above any other selectors that match the same control. Otherwise all of the style declarations
will be applied to the original skin instead of the new one. Apparently the skin is only replaced after
the entire selector is parsed so any styles contained within the same selector will be applied to the
existing skin rather than the new one.
Thanks again to Simon Morris for the original example!
Regards, and Happy New Year,
Dean Iverson
JavaFXpert.com
Recent Comments