In our previous post we presented a JavaFX Sketch Pad example that uses three RGB sliders to manipulate a drawing line’s color. In this post, we’ll use a color picker made with all the JavaFX built-in colors. Since JavaFX colors are are static fields with familiar (and maybe not so familiar) names like BLUE
, GREEN
, and MINTCREAM
, we can use them quite easily as follows.
Rectangle rec = new Rectangle( 5 , 5 , 60 , 30 ); rec.setFill(Color.RED); |
You can also build a Color object from a color name with Color static method web()
.
rec.setFill(Color.web( "TURQUOISE" )); |
Here’s a screenshot of the JavaFX Sketch Pad Version 2.
Note that we’ve moved the drawing controls below the canvas. The sample line appears at the top. The Clear button and stroke width slider are at the bottom left. At the bottom right, you see 147 color rectangles. Clicking on any of the rectangles changes the drawing color to the selected color (and also changes the color of the sample line).
How should we implement this feature? One way is to create an array of Color objects initialized to each of the static fields in Color.
private static final Color[] colors = { Color.ALICEBLUE, Color.ANTIQUEWHITE, Color.AQUA, . . . }; |
Then, we can build a small rectangle for each color and add it to a FlowPane to create a our Color picker.
That works, but too much typing for all the JavaFX colors. Instead, let’s get all the public static fields in Color and build a Color object with Color.web(color_name)
for each rectangle. We can do this with Java reflection. Java stores all kinds of interesting information inside the class object. While reflection is not used that often in application code, it is quite common in development tools (providing code completion choices in Java editors, for example).
Here’s our color picker code. First, we instantiate a Label to hold the selected color’s name. We use a FlowPane layout component to hold the color rectangles. FlowPane arranges its children in a flow, filling each successive row, and wrapping (going to the next row) when needed. Properties vgap
and hgap
provide vertical and horizontal spacing for the rectangles. Property prefWrapLength
tells FlowPane when to wrap to the next row. The default behavior is row-wise filling, but column-wise filling is also possible.
1 2 3 4 5 6 7 8 9 | // Use a Label to hold the selected color name final Label colorLabel = new Label( "color: blue" ); . . . // Put all the color rectangles in a flow container FlowPane flow = new FlowPane(); flow.setVgap( 2 ); flow.setHgap( 2 ); flow.setPrefWrapLength( 400 ); |
Next, we’ll use reflection to grab all of the public static fields for Color. It turns out that the only public static field we want to skip is TRANSPARENT
, which creates a see-through color that is not useful with our drawing program. And, just in case the JavaFX development team slips in a public static field that is not a valid Color name, we use a try – catch block. Color throws an IllegalArgumentException if you attempt to make a Color with an unrecognizable String.
Once we have a Color, we build and configure the rectangle (we’ll show you the configuration code next), and add the rectangle to the FlowPane layout container.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | // Get the declared fields for the Color class Field[] colorFields = Color. class .getDeclaredFields(); for (Field fieldname : colorFields) { // get the field's modifiers so we can tell // if it's public and static int mods = fieldname.getModifiers(); // Only use the field if it's // public, static, and NOT 'TRANSPARENT' if (Modifier.isPublic(mods) && Modifier.isStatic(mods) && !(fieldname.getName().equals( "TRANSPARENT" ))) { try { // create a color from the fieldname Color c = Color.web(fieldname.getName()); // Make a rectangle with that field name's color final Rectangle r = new Rectangle( 15 , 15 , c); // Configure the rectangle . . . // Add it to the flow container flow.getChildren().add(r); } catch (IllegalArgumentException e) { // just ignore it if for some reason we can't make // a color } } } |
To configure each color picker rectangle, we set its cursor to Cursor.HAND
. We then build a tooltip. JavaFX provides a built-in Tooltip object that you can easily configure with any JavaFX Control. However, a Rectangle is not a Control (it’s just a Shape). In this case, use the Tooltip static method install()
for the expected pop-up tooltip behavior. We convert the color to lower case because lower case letters are easier to read.
(Note: The standard JavaFX tooltip pops up quite slowly and stays visible for only a short time. There is currently no way to configure the animation properties of a tooltip. I’m hoping that changes in a future release.)
We also want to display the Color name next to the sample line, since it’s easier to remember a color’s name than just viewing it. So, we save the color name (actually, the tooltip’s text) in the rectangle’s userData
property. Once saved, we can retrieve it when the user selects this color.
The final configuration specifies a mouse clicked event handler for each rectangle. Inside the handler, we set sampleLine
‘s stroke property to the rectangle’s fill property and the colorLabel
‘s text property to the text in the rectangle’s userData
(the color name).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // Configure the rectangle r.setCursor(Cursor.HAND); // Create a tooltip Tooltip t = new Tooltip(fieldname.getName().toLowerCase()); Tooltip.install(r, t); // Save the color name in userData r.setUserData(t.getText()); r.setOnMouseClicked( new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent me) { sampleLine.setStroke(r.getFill()); colorLabel.setText( "color: " + ((String) r.getUserData())); } }); |
Have fun sketching! Download the Sketch Pad (Version 2) JavaFX source code here.
We welcome comments and feedback!
Hi !Nice job BTW, I’m looking for a way to build a oaukgrbcnd-color transition, do you know any way to achieve this ? It seems I can’t access CSS properties that easy in JavaFX