PK
cZFoa, mimetypeapplication/epub+zipPK cZF iTunesMetadata.plist:
This appendix lists code for the following JavaFX samples:
For the descriptions of this source files, see Skinning JavaFX Applications with CSS.
/* * Copyright (c) 2011, 2014, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the distribution. * - Neither the name of Oracle nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package uicontrolcss; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.layout.HBox; import javafx.stage.Stage; import uicontrolcss.StyleStage.SceneCreator; /** * * @author Alexander Kouznetsov */ public class DownloadButton extends Application implements SceneCreator { public static void main(String[] args) { Application.launch(args); } @Override public void start(Stage stage) { StyleStage styleStage = new StyleStage(stage); styleStage.add("Style1", "DownloadButtonStyle1.css"); styleStage.add("Style2", "DownloadButtonStyle2.css"); stage.show(); styleStage.setSceneCreator(this); } @Override public Scene createScene() { Button download = new Button("Download"); download.getStyleClass().add("button1"); Button go = new Button("Go"); Button submit = new Button("Submit"); submit.getStyleClass().add("button2"); HBox hBox = new HBox(10); hBox.getChildren().addAll(download, go, submit); return new Scene(hBox, 400, 100); } }
/* * Copyright (c) 2011, 2014, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the distribution. * - Neither the name of Oracle nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Document : controlStyle */ .button { -fx-text-fill: #e4f3fc; -fx-font: bold 20pt "Tahoma"; -fx-padding: 10; -fx-color: #2d4b8e } .button:hover{ -fx-color: #395bae; }
/* * Copyright (c) 2011, 2014, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the distribution. * - Neither the name of Oracle nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Document : controlStyleLong */ .button { -fx-text-fill: #e4f3fc; -fx-font: bold 20pt "Tahoma"; -fx-padding: 10; -fx-color: #2d4b8e } .button:hover{ -fx-color: #395bae; } .button1 { -fx-font: 20pt "Verdana"; -fx-text-fill: #5086bb; -fx-padding: 10; -fx-background-color: linear-gradient(#cbd0d7, white); -fx-background-radius: 23; -fx-border-radius: 23; -fx-border-color: #767676; } .button1:hover { -fx-background-color: linear-gradient(white, #cbd0d7); } .button2 { -fx-background-color: #a0b616; -fx-text-fill: white; -fx-font: bold 22pt "Courier New"; -fx-padding: 10; -fx-background-radius: 10; -fx-border-radius: 10; -fx-border-style: dotted; -fx-border-color: #67644e; -fx-border-width: 2; } .button2:pressed { -fx-scale-x: 0.8; -fx-scale-y: 0.8; }
/* * Copyright (c) 2011, 2014, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the distribution. * - Neither the name of Oracle nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package uicontrolcss; import javafx.beans.Observable; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.beans.value.ObservableValue; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.ToggleButton; import javafx.scene.control.ToggleGroup; import javafx.scene.layout.HBox; import javafx.stage.Stage; import javafx.stage.WindowEvent; /** * * @author Alexander Kouznetsov */ public class StyleStage { private final StylePanel stylePanel; private final Stage stage; private Stage demoStage; public StyleStage(Stage stage) { this.stage = stage; stylePanel = new StylePanel(); Scene scene = new Scene(stylePanel); stage.setScene(scene); stage.setTitle("Choose style"); stage.setOnCloseRequest((WindowEvent t) -> { demoStage.close(); }); } public void add(String name, String styleSheetName) { stylePanel.add(name, styleSheetName); } public void setSceneCreator(final SceneCreator sceneCreator) { if (demoStage == null) { demoStage = new Stage(); demoStage.setTitle("Demo"); demoStage.setX(stage.getX()); demoStage.setY(stage.getY() + stage.getHeight()); } demoStage.setScene(sceneCreator.createScene()); demoStage.show(); stylePanel.selected.addListener((ObservableValue<? extends String> ov, String t, String t1) -> { demoStage.setScene(sceneCreator.createScene()); if (t1 != null) { demoStage.getScene().getStylesheets().setAll( UIControlCSS.class.getResource(t1).toString()); } }); } public static interface SceneCreator { Scene createScene(); } } class StylePanel extends HBox { public StringProperty selected = new SimpleStringProperty(); ToggleGroup stylesheetToggleGroup = new ToggleGroup(); public StylePanel() { super(5); StyleButton defaultStylesheetButton = new StyleButton("Default", null); defaultStylesheetButton.setSelected(true); defaultStylesheetButton.setToggleGroup(stylesheetToggleGroup); setPadding(new Insets(0, 0, 30, 0)); setAlignment(Pos.BOTTOM_LEFT); getChildren().addAll(defaultStylesheetButton); } public void add(String name, String styleSheetName) { StyleButton styleButton = new StyleButton(name, styleSheetName); styleButton.setToggleGroup(stylesheetToggleGroup); getChildren().addAll(styleButton); } class StyleButton extends ToggleButton { public StyleButton(String text, final String styleSheetName) { super(text); selectedProperty().addListener((Observable ov) -> { selected.set(styleSheetName); }); } } }
/* * Copyright (c) 2011, 2014, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the distribution. * - Neither the name of Oracle nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package uicontrolcss; import javafx.application.Application; import javafx.beans.binding.Bindings; import javafx.collections.FXCollections; import javafx.event.ActionEvent; import javafx.geometry.Insets; import javafx.geometry.Orientation; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.CheckBox; import javafx.scene.control.ChoiceBox; import javafx.scene.control.Label; import javafx.scene.control.PasswordField; import javafx.scene.control.ProgressIndicator; import javafx.scene.control.RadioButton; import javafx.scene.control.ScrollPane; import javafx.scene.control.Separator; import javafx.scene.control.Slider; import javafx.scene.control.TextField; import javafx.scene.control.ToggleGroup; import javafx.scene.control.Tooltip; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.stage.Stage; import uicontrolcss.StyleStage.SceneCreator; /** * * @author Alexander Kouznetsov */ public class UIControlCSS extends Application implements SceneCreator { /** * @param args the command line arguments */ public static void main(String[] args) { Application.launch(args); } @Override public void start(Stage stage) { StyleStage styleStage = new StyleStage(stage); styleStage.add("controlStyle1", "controlStyle1.css"); styleStage.add("controlStyle2", "controlStyle2.css"); stage.show(); styleStage.setSceneCreator(this); } @Override public Scene createScene() { ToggleGroup toggleGroup = new ToggleGroup(); RadioButton radioButton1 = new RadioButton("High"); radioButton1.setToggleGroup(toggleGroup); radioButton1.setSelected(true); RadioButton radioButton2 = new RadioButton("Medium"); radioButton2.setToggleGroup(toggleGroup); RadioButton radioButton3 = new RadioButton("Low"); radioButton3.setToggleGroup(toggleGroup); VBox vBox1 = new VBox(2); vBox1.getChildren().addAll(radioButton1, radioButton2, radioButton3); TextField textField = new TextField(); textField.setPrefColumnCount(10); textField.setPromptText("Your name"); PasswordField passwordField = new PasswordField(); passwordField.setPrefColumnCount(10); passwordField.setPromptText("Your password"); VBox vBox2 = new VBox(); vBox2.getChildren().addAll(textField, passwordField); ChoiceBox<String> choiceBox = new ChoiceBox<>( FXCollections.observableArrayList("English", "???????", "Fran\u00E7ais")); choiceBox.setTooltip(new Tooltip("Your language")); choiceBox.getSelectionModel().select(0); HBox hBox1 = new HBox(5); hBox1.setAlignment(Pos.BOTTOM_LEFT); hBox1.getChildren().addAll(vBox1, vBox2, choiceBox); final Label label1 = new Label("Not Available"); label1.getStyleClass().add("borders"); Button button1 = new Button("Accept"); button1.getStyleClass().add("button1"); button1.setOnAction((ActionEvent t) -> { label1.setText("Accepted"); }); Button button2 = new Button("Decline"); button2.getStyleClass().add("button2"); button2.setOnAction((ActionEvent t) -> { label1.setText("Declined"); }); HBox hBox2 = new HBox(10); hBox2.setAlignment(Pos.CENTER_LEFT); hBox2.getChildren().addAll(button1, button2, label1); CheckBox checkBox1 = new CheckBox("Normal"); Separator separator = new Separator(Orientation.VERTICAL); separator.setPrefSize(1, 15); CheckBox checkBox2 = new CheckBox("Checked"); checkBox2.setSelected(true); CheckBox checkBox3 = new CheckBox("Undefined"); checkBox3.setIndeterminate(true); checkBox3.setAllowIndeterminate(true); HBox hBox3 = new HBox(12); hBox3.getChildren().addAll(checkBox1, separator, checkBox2, checkBox3); Label label2 = new Label("Progress:"); label2.getStyleClass().add("borders"); Slider slider = new Slider(); ProgressIndicator progressIndicator = new ProgressIndicator(0); progressIndicator.progressProperty().bind(Bindings.divide( slider.valueProperty(), slider.maxProperty())); HBox hBox4 = new HBox(10); hBox4.getChildren().addAll(label2, slider, progressIndicator); final VBox vBox = new VBox(20); vBox.setPadding(new Insets(30, 10, 30, 10)); vBox.setAlignment(Pos.TOP_LEFT); vBox.getChildren().setAll(hBox1, hBox2, hBox3, hBox4); ScrollPane scrollPane = new ScrollPane(); scrollPane.setContent(vBox); return new Scene(scrollPane, 500, 350); } }
/* * Copyright (c) 2011, 2014, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the distribution. * - Neither the name of Oracle nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Document : controlStyle1 */ .root{ -fx-font-size: 14pt; -fx-font-family: "Tahoma"; -fx-base: #DFB951; -fx-background: #A78732; -fx-focus-color: #B6A678; } .button1{ -fx-text-fill: #006464; -fx-background-color: #DFB951; -fx-border-radius: 20; -fx-background-radius: 20; -fx-padding: 5; } .button2{ -fx-text-fill: #c10000; -fx-background-color: #DFB951; -fx-border-radius: 20; -fx-background-radius: 20; -fx-padding: 5; } .slider{ -fx-border-color: white; -fx-border-style: dashed; -fx-border-width: 2; }
/* * Copyright (c) 2011, 2014, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the distribution. * - Neither the name of Oracle nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* Document : controlStyle2 */ .root{ -fx-font-size: 16pt; -fx-font-family: "Courier New"; -fx-base: rgb(132, 145, 47); -fx-background: rgb(225, 228, 203); } .button{ -fx-text-fill: rgb(49, 89, 23); -fx-border-color: rgb(49, 89, 23); -fx-border-radius: 5; -fx-padding: 3 6 6 6; } .borders{ -fx-border-color: rgb(103, 100, 78); -fx-border-style: dotted; -fx-border-width: 1.5; -fx-border-insets: -5; }
In this chapter, you learn about the tooltip, the control that can be set for any UI control to appear when that control is hovered over by the mouse cursor.
The Tooltip
class represents a common UI component that is typically used to display additional information about the UI control. The tooltip can be set on any control by calling the setTooltip
method.
The tooltip has two different states: activated and showing. When the tooltip is activated, the mouse moves over a control. When the tooltip is in the showing state, it actually appears. A shown tooltip is also activated. There is usually some delay between when the Tooltip becomes activated and when it is actually shown.
A password field with a tooltip is shown in Figure 22-1.
Study the code fragment in Example 22-1 that creates the password field with a tooltip in the JavaFX application shown in the preceding figure.
Example 22-1 Adding a Tooltip to the Password Field
final PasswordField pf = new PasswordField(); final Tooltip tooltip = new Tooltip(); tooltip.setText( "\nYour password must be\n" + "at least 8 characters in length\n" + ); pf.setTooltip(tooltip);
Each UI control from the javafx.scene.control
package has the setTooltip
method to add a tooltip. You can define a text caption within a Tooltip
constructor or by using the setText
method.
Because the Tooltip
class is an extension of the Labeled
class, you can add not only a text caption, but a graphical icon as well. The code fragment in Example 22-2 adds an icon to the tooltip for the password field.
Example 22-2 Adding an Icon to a Tooltip
Image image = new Image( getClass().getResourceAsStream("warn.png") ); tooltip.setGraphic(new ImageView(image));
After you add this code fragment to the application, and the code is compiled, and run, the tooltip shown in Figure 22-2 appears.
A tooltip can contain not only additional or auxiliary information, but it can also present data.
The application in Figure 22-3 uses information presented in the tooltips to calculate the total cost of the hotel stay
Each checkbox is accompanied by a tooltip. Each tooltip displays the rate for a particular booking option. If a user selects a checkbox, the corresponding value is added to the total. If a selected checkbox is deselected, the corresponding value is deducted from the total.
Review the source code of the application shown in Example 22-3.
Example 22-3 Using Tooltips to Calculate Hotel Rates
import javafx.application.Application; import javafx.beans.value.ObservableValue; import javafx.geometry.Insets; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.CheckBox; import javafx.scene.control.Label; import javafx.scene.control.Tooltip; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.scene.text.Font; import javafx.stage.Stage; public class TooltipSample extends Application { final static String[] rooms = new String[]{ "Accommodation (BB)", "Half Board", "Late Check-out", "Extra Bed" }; final static Integer[] rates = new Integer[]{ 100, 20, 10, 30 }; final CheckBox[] cbs = new CheckBox[rooms.length]; final Label total = new Label("Total: $0"); Integer sum = 0; public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) { Scene scene = new Scene(new Group()); stage.setTitle("Tooltip Sample"); stage.setWidth(330); stage.setHeight(150); total.setFont(new Font("Arial", 20)); for (int i = 0; i < rooms.length; i++) { final CheckBox cb = cbs[i] = new CheckBox(rooms[i]); final Integer rate = rates[i]; final Tooltip tooltip = new Tooltip("$" + rates[i].toString()); tooltip.setFont(new Font("Arial", 16)); cb.setTooltip(tooltip); cb.selectedProperty().addListener( (ObservableValue<? extends Boolean> ov, Boolean old_val, Boolean new_val) -> { if (cb.isSelected()) { sum = sum + rate; } else { sum = sum - rate; } total.setText("Total: $" + sum.toString()); } ); } VBox vbox = new VBox(); vbox.getChildren().addAll(cbs); vbox.setSpacing(5); HBox root = new HBox(); root.getChildren().add(vbox); root.getChildren().add(total); root.setSpacing(40); root.setPadding(new Insets(20, 10, 10, 20)); ((Group) scene.getRoot()).getChildren().add(root); stage.setScene(scene); stage.show(); } }
The code line in Example 22-4 was used in Example 22-3 to create a tooltip and assign a text caption to it. The Integer
value of the option price was converted into a String
value.
Example 22-4 Setting the Value for a Tooltip
final Tooltip tooltip = new Tooltip("$" + rates[i].toString())
You can alter visual appearance of a tooltip by applying CSS.
Related API Documentation
In this chapter, you learn how to use sliders in your JavaFX applications to display and interact with a range of numeric values.
The Slider
control consists of a track and a draggable thumb. It can also include tick marks and tick labels that indicate numeric values of the range. Figure 18-1 shows a typical slider and indicates its main elements.
Take a moment to review the code fragment in Example 18-1 that produces a slider shown in Figure 18-1.
Example 18-1 Creating a Slider
Slider slider = new Slider(); slider.setMin(0); slider.setMax(100); slider.setValue(40); slider.setShowTickLabels(true); slider.setShowTickMarks(true); slider.setMajorTickUnit(50); slider.setMinorTickCount(5); slider.setBlockIncrement(10);
The setMin
and setMax
methods define, respectively, the minimum and the maximum numeric values represented by the slider. The setValue
method specifies the current value of the slider, which is always less than the maximum value and more than the minimum value. Use this method to define the position of the thumb when the application starts.
Two Boolean methods, setShowTickMarks
and setShowTickLabels
, define the visual appearance of the slider. In Example 18-1, the marks and labels are enabled. Additionally, the unit distance between major tick marks is set to 50, and the number of minor ticks between any two major ticks is specified as 5. You can assign the setSnapToTicks
method to true
to keep the slider's value aligned with the tick marks.
The setBlockIncrement
method defines the distance that the thumb moves when a user clicks on the track. In Example 18-1, this value is 10, which means that each time a user clicks on the track, the thumb moves 10 units toward the click location.
Now examine Figure 18-2. This application uses three sliders to edit rendering characteristics of a picture. Each slider adjusts a particular visual characteristic: opacity level, sepia tone value, or scaling factor.
Example 18-2 shows the source code of this application.
Example 18-2 Slider Sample
import javafx.application.Application; import javafx.beans.value.ObservableValue; import javafx.geometry.Insets; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.control.Slider; import javafx.scene.effect.SepiaTone; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.GridPane; import javafx.scene.paint.Color; import javafx.stage.Stage; public class SliderSample extends Application { final Slider opacityLevel = new Slider(0, 1, 1); final Slider sepiaTone = new Slider(0, 1, 1); final Slider scaling = new Slider (0.5, 1, 1); final Image image = new Image(getClass().getResourceAsStream( "cappuccino.jpg") ); final Label opacityCaption = new Label("Opacity Level:"); final Label sepiaCaption = new Label("Sepia Tone:"); final Label scalingCaption = new Label("Scaling Factor:"); final Label opacityValue = new Label( Double.toString(opacityLevel.getValue())); final Label sepiaValue = new Label( Double.toString(sepiaTone.getValue())); final Label scalingValue = new Label( Double.toString(scaling.getValue())); final static Color textColor = Color.BLACK; final static SepiaTone sepiaEffect = new SepiaTone(); @Override public void start(Stage stage) { Group root = new Group(); Scene scene = new Scene(root, 600, 400); stage.setScene(scene); stage.setTitle("Slider Sample"); GridPane grid = new GridPane(); grid.setPadding(new Insets(10, 10, 10, 10)); grid.setVgap(10); grid.setHgap(70); final ImageView cappuccino = new ImageView (image); cappuccino.setEffect(sepiaEffect); GridPane.setConstraints(cappuccino, 0, 0); GridPane.setColumnSpan(cappuccino, 3); grid.getChildren().add(cappuccino); scene.setRoot(grid); opacityCaption.setTextFill(textColor); GridPane.setConstraints(opacityCaption, 0, 1); grid.getChildren().add(opacityCaption); opacityLevel.valueProperty().addListener(( ObservableValue<? extends Number> ov, Number old_val, Number new_val) -> { cappuccino.setOpacity(new_val.doubleValue()); opacityValue.setText(String.format("%.2f", new_val)); }); GridPane.setConstraints(opacityLevel, 1, 1); grid.getChildren().add(opacityLevel); opacityValue.setTextFill(textColor); GridPane.setConstraints(opacityValue, 2, 1); grid.getChildren().add(opacityValue); sepiaCaption.setTextFill(textColor); GridPane.setConstraints(sepiaCaption, 0, 2); grid.getChildren().add(sepiaCaption); sepiaTone.valueProperty().addListener(( ObservableValue<? extends Number> ov, Number old_val, Number new_val) -> { sepiaEffect.setLevel(new_val.doubleValue()); sepiaValue.setText(String.format("%.2f", new_val)); }); GridPane.setConstraints(sepiaTone, 1, 2); grid.getChildren().add(sepiaTone); sepiaValue.setTextFill(textColor); GridPane.setConstraints(sepiaValue, 2, 2); grid.getChildren().add(sepiaValue); scalingCaption.setTextFill(textColor); GridPane.setConstraints(scalingCaption, 0, 3); grid.getChildren().add(scalingCaption); scaling.valueProperty().addListener(( ObservableValue<? extends Number> ov, Number old_val, Number new_val) -> { cappuccino.setScaleX(new_val.doubleValue()); cappuccino.setScaleY(new_val.doubleValue()); scalingValue.setText(String.format("%.2f", new_val)); }); GridPane.setConstraints(scaling, 1, 3); grid.getChildren().add(scaling); scalingValue.setTextFill(textColor); GridPane.setConstraints(scalingValue, 2, 3); grid.getChildren().add(scalingValue); stage.show(); } public static void main(String[] args) { launch(args); } }
The opacity property of the ImageView
object changes in accordance with the value of the first slider, named opacityLevel. The level of the SepiaTone
effect changes when the value of the sepiaTone slider changes. The third slider defines the scaling factor for the picture by passing to the setScaleX
and setScaleY
methods the current value of the slider.
The code fragment in Example 18-3 demonstrates the methods that convert the double value returned by the getValue
method of the Slider
class into String
. It also applies formatting to render the slider's value as a float number with two digits after the point.
Example 18-3 Formatting the Rendered Slider's Value
scalingValue.setText(String.format("%.2f", new_val));
The next step to enhance the look and feel of a slider is to apply visual effects or CSS styles to it.
Related API Documentation
In this chapter, you learn about the ToggleButton
class, another type of buttons available through the JavaFX API.
Two or more toggle buttons can be combined into a group where only one button at a time can be selected, or where no selection is required. Figure 5-1 is a screen capture of an application that combines three toggle buttons in a group. The application paints the rectangle with a specific color according to on which toggle button is selected.
You can create a toggle button in your application by using any of the three constructors of the ToggleButton
class, as shown in Example 5-1.
Example 5-1 Creating Toggle Buttons
//A toggle button without any caption or icon ToggleButton tb1 = new ToggleButton(); //A toggle button with a text caption ToggleButton tb2 = new ToggleButton("Press me"); //A toggle button with a text caption and an icon Image image = new Image(getClass().getResourceAsStream("icon.png")); ToggleButton tb3 = new ToggleButton ("Press me", new ImageView(image));
The ToggleButton
class is an extension of the Labeled
class, so you can specify a text caption, an image, or both image and text. You can use the setText
and setGraphic
methods of the Labeled
class to specify textual and graphical content for a toggle button.
Once you have defined toggle buttons in your code, you can combine them in a group and set a specific behavior.
The implementation of the ToggleButton
class is very similar to the implementation the RadioButton
class. However, unlike radio buttons, toggle buttons in a toggle group do not attempt to force the selection at least one button in the group. That is, clicking the selected toggle button causes it to become deselected, clicking the selected radio button in the group has no effect.
Take a moment to study the code fragment Example 5-2.
Example 5-2 Combining Toggle Buttons in a Group
final ToggleGroup group = new ToggleGroup(); ToggleButton tb1 = new ToggleButton("Minor"); tb1.setToggleGroup(group); tb1.setSelected(true); ToggleButton tb2 = new ToggleButton("Major"); tb2.setToggleGroup(group); ToggleButton tb3 = new ToggleButton("Critical"); tb3.setToggleGroup(group);
Example 5-2 creates three toggle buttons and adds them to the toggle group. The setSelected
method is called for the tb1 toggle button so that it is selected when the application starts. However, you can deselect the Minor toggle button so that no toggle buttons are selected in the group at startup, as shown in Figure 5-2.
Typically, you use a group of toggle buttons to assign a specific behavior for each button. The next section explains how to use these toggle buttons to alter the color of a rectangle.
The setUserData
method inherited by the ToggleButton
class from the Node
class helps you to associate any selected option with a particular value. In Example 5-3, the user data indicates which color should be used to paint the rectangle.
Example 5-3 Setting User Data for the Toggle Buttons
tb1.setUserData(Color.LIGHTGREEN); tb2.setUserData(Color.LIGHTBLUE); tb3.setUserData(Color.SALMON); Rectangle rect = new Rectangle(); rect.setHeight(50); rect.setFill(Color.WHITE); rect.setStroke(Color.DARKGRAY); rect.setStrokeWidth(2); rect.setArcHeight(10); rect.setArcWidth(10); final ToggleGroup group = new ToggleGroup(); group.selectedToggleProperty().addListener (ObservableValue<? extends Toggle> ov, Toggle toggle, Toggle new_toggle) -> { if (new_toggle == null) rect.setFill(Color.WHITE); else rect.setFill((Color) group.getSelectedToggle().getUserData()); }); rect.setWidth(hbox.getWidth());
The ChangeListener<Toggle>
object checks a selected toggle in the group. If none of the toggle buttons is selected, the rectangle is painted with the white color. If one of the toggle button is selected, consecutive calls of the getSelectedToggle
and getUserData
methods return a color to paint the rectangle.
For example, if a user selects the tb2 toggle button, the setSelectedToggle().getUserData()
call returns Color.LIGHTBLUE
. The result is shown in Figure 5-3.
See the ToggleButtonSample.java file to examine the complete code of the application.
You can enhance this application by applying CSS styles to the toggle buttons. Using CSS in JavaFX applications is similar to using CSS in HTML, because each case is based on the same CSS specification.
First, you declare the styles of the toggle buttons in the ControlStyle.css file as shown in Example 5-4.
Example 5-4 Declaring Alternative Colors of the Toggle Button
.toggle-button1{ -fx-base: lightgreen; } .toggle-button2{ -fx-base: lightblue; } .toggle-button3{ -fx-base: salmon; }
Second, you enable the styles in the ToggleButtonSample application. See how this is implemented in Example 5-5.
Example 5-5 Applying CSS Styles to Toggle Buttons
scene.getStylesheets().add("togglebuttonsample/ControlStyle.css"); tb1.getStyleClass().add("toggle-button1"); tb2.getStyleClass().add("toggle-button2"); tb3.getStyleClass().add("toggle-button3");
When added to the application code these lines change the visual appearance of the toggle buttons as shown in Figure 5-4.
You might want to try other CSS properties of the ToggleButton
class or apply animation, transformations, and visual effects available in the JavaFX API.
Related API Documentation
This chapter introduces the new user interface (UI) features available with JavaFX SDK 8.0.
Find the following additions:
Tree Table View
This chapter describes the TreeTableView
user interface component, a control designed to visualize an unlimited number of rows of data, broken out into columns.
Date Packer
This chapter describes DatePicker
, a control that enables selection of a day from the given calendar.
JavaFX UI Controls on the Embedded Platforms
This chapter describes the specific of using JavaFX UI controls in the embedded environments.
This tutorial describes basic UI components available in JavaFX SDK and contains the following chapters:
Each chapter provides code samples and applications to illustrate how to use a particular component. You can find the source files of the applications and the corresponding NetBeans projects in the tutorial appendixes.
In this chapter, you learn how to perform basic operations with tables in JavaFX applications, such as adding a table, populating the table with data, and editing table rows.
Several classes in the JavaFX SDK API are designed to represent data in a tabular form. The most important classes for creating tables in JavaFX applications are TableView
, TableColumn
, and TableCell
. You can populate a table by implementing the data model and by applying a cell factory.
The table classes provide built-in capabilities to sort data in columns and to resize columns when necessary.
Figure 13-1 shows a typical table representing contact information from an address book.
The code fragment in Example 13-1 creates an empty table with three columns and adds it to the application scene.
Example 13-1 Adding a Table
import javafx.application.Application; import javafx.geometry.Insets; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.layout.VBox; import javafx.scene.text.Font; import javafx.stage.Stage; public class TableViewSample extends Application { private final TableView table = new TableView(); public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) { Scene scene = new Scene(new Group()); stage.setTitle("Table View Sample"); stage.setWidth(300); stage.setHeight(500); final Label label = new Label("Address Book"); label.setFont(new Font("Arial", 20)); table.setEditable(true); TableColumn firstNameCol = new TableColumn("First Name"); TableColumn lastNameCol = new TableColumn("Last Name"); TableColumn emailCol = new TableColumn("Email"); table.getColumns().addAll(firstNameCol, lastNameCol, emailCol); final VBox vbox = new VBox(); vbox.setSpacing(5); vbox.setPadding(new Insets(10, 0, 0, 10)); vbox.getChildren().addAll(label, table); ((Group) scene.getRoot()).getChildren().addAll(vbox); stage.setScene(scene); stage.show(); } }
The table control is created by instantiating the TableView
class. In Example 13-1, it is added to the VBox
layout container, however, you can add it directly to the application scene.
Example 13-1 defines three columns to store the following information in an address book: a contact's first name and last name, and an email address. The columns are created by using the TableColumn
class.
The getColumns
method of the TableView
class adds the previously created columns to the table. In your applications, you can use this method to dynamically add and remove columns.
Compiling and running this application produces the output shown in Figure 13-2.
You can manage visibility of the columns by calling the setVisible
method. For example, if the logic of your application requires hiding user email addresses, you can implement this task as follows: emailCol.setVisible(false)
.
When the structure of your data requires a more complicated representation, you can create nested columns.
For example, suppose that the contacts in the address book have two email accounts. Then you need two columns to show the primary and the secondary email addresses. Create two subcolumns, and call the getColumns
method on emailCol
as shown in Example 13-2.
Example 13-2 Creating Nested Columns
TableColumn firstEmailCol = new TableColumn("Primary"); TableColumn secondEmailCol = new TableColumn("Secondary"); emailCol.getColumns().addAll(firstEmailCol, secondEmailCol);
After you have added these lines to Example 13-1, and compiled and run the application code, the table appears as shown in Figure 13-3.
Although the table is added to the application, the standard caption "No content in table" appears, because no data is defined. Instead of showing this caption, you can use the setPlaceholder
method to specify a Node
object to appear in an empty table.
When you create a table in a JavaFX application, it is a best practice to implement a class that defines the data model and provides methods and fields to further work with the table. Example 13-3 creates the Person
class to define data in an address book.
Example 13-3 Creating the Person Class
public static class Person { private final SimpleStringProperty firstName; private final SimpleStringProperty lastName; private final SimpleStringProperty email; private Person(String fName, String lName, String email) { this.firstName = new SimpleStringProperty(fName); this.lastName = new SimpleStringProperty(lName); this.email = new SimpleStringProperty(email); } public String getFirstName() { return firstName.get(); } public void setFirstName(String fName) { firstName.set(fName); } public String getLastName() { return lastName.get(); } public void setLastName(String fName) { lastName.set(fName); } public String getEmail() { return email.get(); } public void setEmail(String fName) { email.set(fName); } }
The firstName
, lastName
, and email
string properties are created to enable the referencing of a particular data element.
Additionally, the get
and set
methods are provided for each data element. Thus, for example, the getFirstName
method returns the value of the firstName
property, and the setFirstName
method specifies a value for this property.
When the data model is outlined in the Person
class, you can create an ObservableList
array and define as many data rows as you would like to show in your table. The code fragment in Example 13-4 implements this task.
Example 13-4 Defining Table Data in an Observable List
final ObservableList<Person> data = FXCollections.observableArrayList( new Person("Jacob", "Smith", "jacob.smith@example.com"), new Person("Isabella", "Johnson", "isabella.johnson@example.com"), new Person("Ethan", "Williams", "ethan.williams@example.com"), new Person("Emma", "Jones", "emma.jones@example.com"), new Person("Michael", "Brown", "michael.brown@example.com") );
The next step is to associate the data with the table columns. You can do this through the properties defined for each data element, as shown in Example 13-5.
Example 13-5 Setting Data Properties to Columns
firstNameCol.setCellValueFactory( new PropertyValueFactory<>("firstName") ); lastNameCol.setCellValueFactory( new PropertyValueFactory<>("lastName") ); emailCol.setCellValueFactory( new PropertyValueFactory<>("email") );
The setCellValueFactory
method specifies a cell factory for each column. The cell factories are implemented by using the PropertyValueFactory
class, which uses the firstName
, lastName
, and email
properties of the table columns as references to the corresponding methods of the Person
class.
When the data model is defined, and the data is added and associated with the columns, you can add the data to the table by using the setItems
method of the TableView
class: table.setItems(data)
.
Because the ObservableList
object enables the tracking of any changes to its elements, the TableView
content automatically updates whenever the data changes.
Examine the application code shown in Example 13-6.
Example 13-6 Creating a Table and Adding Data to It
import javafx.application.Application; import javafx.beans.property.SimpleStringProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.geometry.Insets; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.layout.VBox; import javafx.scene.text.Font; import javafx.stage.Stage; public class TableViewSample extends Application { private final TableView<Person> table = new TableView<>(); private final ObservableList<Person> data = FXCollections.observableArrayList( new Person("Jacob", "Smith", "jacob.smith@example.com"), new Person("Isabella", "Johnson", "isabella.johnson@example.com"), new Person("Ethan", "Williams", "ethan.williams@example.com"), new Person("Emma", "Jones", "emma.jones@example.com"), new Person("Michael", "Brown", "michael.brown@example.com") ); public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) { Scene scene = new Scene(new Group()); stage.setTitle("Table View Sample"); stage.setWidth(450); stage.setHeight(500); final Label label = new Label("Address Book"); label.setFont(new Font("Arial", 20)); table.setEditable(true); TableColumn firstNameCol = new TableColumn("First Name"); firstNameCol.setMinWidth(100); firstNameCol.setCellValueFactory( new PropertyValueFactory<>("firstName")); TableColumn lastNameCol = new TableColumn("Last Name"); lastNameCol.setMinWidth(100); lastNameCol.setCellValueFactory( new PropertyValueFactory<>("lastName")); TableColumn emailCol = new TableColumn("Email"); emailCol.setMinWidth(200); emailCol.setCellValueFactory( new PropertyValueFactory<>("email")); table.setItems(data); table.getColumns().addAll(firstNameCol, lastNameCol, emailCol); final VBox vbox = new VBox(); vbox.setSpacing(5); vbox.setPadding(new Insets(10, 0, 0, 10)); vbox.getChildren().addAll(label, table); ((Group) scene.getRoot()).getChildren().addAll(vbox); stage.setScene(scene); stage.show(); } public static class Person { private final SimpleStringProperty firstName; private final SimpleStringProperty lastName; private final SimpleStringProperty email; private Person(String fName, String lName, String email) { this.firstName = new SimpleStringProperty(fName); this.lastName = new SimpleStringProperty(lName); this.email = new SimpleStringProperty(email); } public String getFirstName() { return firstName.get(); } public void setFirstName(String fName) { firstName.set(fName); } public String getLastName() { return lastName.get(); } public void setLastName(String fName) { lastName.set(fName); } public String getEmail() { return email.get(); } public void setEmail(String fName) { email.set(fName); } } }
When you compile and run this application code, the table shown in Figure 13-4 appears.
The table in Figure 13-4 contains five rows of data, which cannot be modified so far.
You can use text fields to enter new values into the First Name, Last Name, and Email columns. The Text Field control enables your application to receive text input from a user. Example 13-7 creates three text fields, defines the prompt text for each field, and creates the Add button.
Example 13-7 Using Text Fields to Enter New Items in the Table
final TextField addFirstName = new TextField(); addFirstName.setPromptText("First Name"); addFirstName.setMaxWidth(firstNameCol.getPrefWidth()); final TextField addLastName = new TextField(); addLastName.setMaxWidth(lastNameCol.getPrefWidth()); addLastName.setPromptText("Last Name"); final TextField addEmail = new TextField(); addEmail.setMaxWidth(emailCol.getPrefWidth()); addEmail.setPromptText("Email"); final Button addButton = new Button("Add"); addButton.setOnAction((ActionEvent e) -> { data.add(new Person( addFirstName.getText(), addLastName.getText(), addEmail.getText() )); addFirstName.clear(); addLastName.clear(); addEmail.clear(); });
When a user clicks the Add button, the values entered in the text fields are included in a Person
constructor and added to the data
observable list. Thus, the new entry with contact information appears in the table.
Examine the application code shown in Example 13-8.
Example 13-8 Table with the Text Fields to Enter New Items
import javafx.application.Application; import javafx.beans.property.SimpleStringProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.geometry.Insets; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.TextField; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.scene.text.Font; import javafx.stage.Stage; public class TableViewSample extends Application { private final TableView<Person> table = new TableView<>(); private final ObservableList<Person> data = FXCollections.observableArrayList( new Person("Jacob", "Smith", "jacob.smith@example.com"), new Person("Isabella", "Johnson", "isabella.johnson@example.com"), new Person("Ethan", "Williams", "ethan.williams@example.com"), new Person("Emma", "Jones", "emma.jones@example.com"), new Person("Michael", "Brown", "michael.brown@example.com")); final HBox hb = new HBox(); public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) { Scene scene = new Scene(new Group()); stage.setTitle("Table View Sample"); stage.setWidth(450); stage.setHeight(550); final Label label = new Label("Address Book"); label.setFont(new Font("Arial", 20)); table.setEditable(true); TableColumn firstNameCol = new TableColumn("First Name"); firstNameCol.setMinWidth(100); firstNameCol.setCellValueFactory( new PropertyValueFactory<>("firstName")); TableColumn lastNameCol = new TableColumn("Last Name"); lastNameCol.setMinWidth(100); lastNameCol.setCellValueFactory( new PropertyValueFactory<>("lastName")); TableColumn emailCol = new TableColumn("Email"); emailCol.setMinWidth(200); emailCol.setCellValueFactory( new PropertyValueFactory<>("email")); table.setItems(data); table.getColumns().addAll(firstNameCol, lastNameCol, emailCol); final TextField addFirstName = new TextField(); addFirstName.setPromptText("First Name"); addFirstName.setMaxWidth(firstNameCol.getPrefWidth()); final TextField addLastName = new TextField(); addLastName.setMaxWidth(lastNameCol.getPrefWidth()); addLastName.setPromptText("Last Name"); final TextField addEmail = new TextField(); addEmail.setMaxWidth(emailCol.getPrefWidth()); addEmail.setPromptText("Email"); final Button addButton = new Button("Add"); addButton.setOnAction((ActionEvent e) -> { data.add(new Person( addFirstName.getText(), addLastName.getText(), addEmail.getText())); addFirstName.clear(); addLastName.clear(); addEmail.clear(); }); hb.getChildren().addAll(addFirstName, addLastName, addEmail, addButton); hb.setSpacing(3); final VBox vbox = new VBox(); vbox.setSpacing(5); vbox.setPadding(new Insets(10, 0, 0, 10)); vbox.getChildren().addAll(label, table, hb); ((Group) scene.getRoot()).getChildren().addAll(vbox); stage.setScene(scene); stage.show(); } public static class Person { private final SimpleStringProperty firstName; private final SimpleStringProperty lastName; private final SimpleStringProperty email; private Person(String fName, String lName, String email) { this.firstName = new SimpleStringProperty(fName); this.lastName = new SimpleStringProperty(lName); this.email = new SimpleStringProperty(email); } public String getFirstName() { return firstName.get(); } public void setFirstName(String fName) { firstName.set(fName); } public String getLastName() { return lastName.get(); } public void setLastName(String fName) { lastName.set(fName); } public String getEmail() { return email.get(); } public void setEmail(String fName) { email.set(fName); } } }
This application does not provide any filters to check if, for example, an email address was entered in an incorrect format. You can provide such functionality when you develop your own application.
The current implementation also does not check to determine if the empty values are entered. If no values are provided, clicking the Add button inserts an empty row in the table.
Figure 13-5 demonstrates how a user adds a new row of data.
Figure 13-6 shows the table after the Add button is clicked. The contact details of Emma White now appear in the table.
The TableView
class provides built-in capabilities to sort data in columns. Users can alter the order of data by clicking column headers. The first click enables the ascending sorting order, the second click enables descending sorting order, and the third click disables sorting. By default, no sorting is applied.
Users can sort multiple columns in a table and specify the priority of each column in the sort operation. To sort multiple columns, the user presses the Shift key while clicking the header of each column to be sorted.
In Figure 13-7, an ascending sort order is applied to the first names, while last names are sorted in a descending order. Note that the first column has priority over the second column.
As the application developer, you can set sorting preferences for each column in your application by applying the setSortType
method. You can specify both ascending and descending type. For example, use the following code line to set the descending type of sorting for the emailCol column: emailCol.setSortType(TableColumn.SortType.DESCENDING);
.
You can also specify which columns to sort by adding and removing TableColumn
instances from the TableView.sortOrder
observable list. The order of columns in this list represents the sort priority (for example, the zero item has higher priority than the first item).
To prohibit sorting of data, call the setSortable(false)
method on the column.
The TableView
class not only renders tabular data, but it also provides capabilities to edit it. Use the setEditable
method to enable editing of the table content.
Use the setCellFactory
method to reimplement the table cell as a text field with the help of the TextFieldTableCell
class. The setOnEditCommit
method processes editing and assigns the updated value to the corresponding table cell. Example 13-9 shows how to apply these methods to process cell editing in the First Name, Last Name, and Email columns.
Example 13-9 Implementing Cell Editing
firstNameCol.setCellFactory(TextFieldTableCell.<Person>forTableColumn()); firstNameCol.setOnEditCommit( (CellEditEvent<Person, String> t) -> { ((Person) t.getTableView().getItems().get( t.getTablePosition().getRow()) ).setFirstName(t.getNewValue()); }); lastNameCol.setCellFactory(TextFieldTableCell.<Person>forTableColumn()); lastNameCol.setOnEditCommit( (CellEditEvent<Person, String> t) -> { ((Person) t.getTableView().getItems().get( t.getTablePosition().getRow()) ).setLastName(t.getNewValue()); }); emailCol.setCellFactory(TextFieldTableCell.<Person>forTableColumn()); emailCol.setOnEditCommit( (CellEditEvent<Person, String> t) -> { ((Person) t.getTableView().getItems().get( t.getTablePosition().getRow()) ).setEmail(t.getNewValue()); });
The complete code of the application shown in Example 13-10.
Example 13-10 TableViewSample with Enabled Cell Editing
import javafx.application.Application; import javafx.beans.property.SimpleStringProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.geometry.Insets; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.TableColumn; import javafx.scene.control.TableColumn.CellEditEvent; import javafx.scene.control.TableView; import javafx.scene.control.TextField; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.control.cell.TextFieldTableCell; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.scene.text.Font; import javafx.stage.Stage; public class TableViewSample extends Application { private final TableView<Person> table = new TableView<>(); private final ObservableList<Person> data = FXCollections.observableArrayList( new Person("Jacob", "Smith", "jacob.smith@example.com"), new Person("Isabella", "Johnson", "isabella.johnson@example.com"), new Person("Ethan", "Williams", "ethan.williams@example.com"), new Person("Emma", "Jones", "emma.jones@example.com"), new Person("Michael", "Brown", "michael.brown@example.com")); final HBox hb = new HBox(); public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) { Scene scene = new Scene(new Group()); stage.setTitle("Table View Sample"); stage.setWidth(450); stage.setHeight(550); final Label label = new Label("Address Book"); label.setFont(new Font("Arial", 20)); table.setEditable(true); TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name"); firstNameCol.setMinWidth(100); firstNameCol.setCellValueFactory( new PropertyValueFactory<>("firstName")); firstNameCol.setCellFactory(TextFieldTableCell.<Person>forTableColumn()); firstNameCol.setOnEditCommit( (CellEditEvent<Person, String> t) -> { ((Person) t.getTableView().getItems().get( t.getTablePosition().getRow()) ).setFirstName(t.getNewValue()); }); TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name"); lastNameCol.setMinWidth(100); lastNameCol.setCellValueFactory( new PropertyValueFactory<>("lastName")); lastNameCol.setCellFactory(TextFieldTableCell.<Person>forTableColumn()); lastNameCol.setOnEditCommit( (CellEditEvent<Person, String> t) -> { ((Person) t.getTableView().getItems().get( t.getTablePosition().getRow()) ).setLastName(t.getNewValue()); }); TableColumn<Person, String> emailCol = new TableColumn<>("Email"); emailCol.setMinWidth(200); emailCol.setCellValueFactory( new PropertyValueFactory<>("email")); emailCol.setCellFactory(TextFieldTableCell.<Person>forTableColumn()); emailCol.setOnEditCommit( (CellEditEvent<Person, String> t) -> { ((Person) t.getTableView().getItems().get( t.getTablePosition().getRow()) ).setEmail(t.getNewValue()); }); table.setItems(data); table.getColumns().addAll(firstNameCol, lastNameCol, emailCol); final TextField addFirstName = new TextField(); addFirstName.setPromptText("First Name"); addFirstName.setMaxWidth(firstNameCol.getPrefWidth()); final TextField addLastName = new TextField(); addLastName.setMaxWidth(lastNameCol.getPrefWidth()); addLastName.setPromptText("Last Name"); final TextField addEmail = new TextField(); addEmail.setMaxWidth(emailCol.getPrefWidth()); addEmail.setPromptText("Email"); final Button addButton = new Button("Add"); addButton.setOnAction((ActionEvent e) -> { data.add(new Person( addFirstName.getText(), addLastName.getText(), addEmail.getText())); addFirstName.clear(); addLastName.clear(); addEmail.clear(); }); hb.getChildren().addAll(addFirstName, addLastName, addEmail, addButton); hb.setSpacing(3); final VBox vbox = new VBox(); vbox.setSpacing(5); vbox.setPadding(new Insets(10, 0, 0, 10)); vbox.getChildren().addAll(label, table, hb); DU ((Group) scene.getRoot()).getChildren().addAll(vbox); stage.setScene(scene); stage.show(); } public static class Person { private final SimpleStringProperty firstName; private final SimpleStringProperty lastName; private final SimpleStringProperty email; private Person(String fName, String lName, String email) { this.firstName = new SimpleStringProperty(fName); this.lastName = new SimpleStringProperty(lName); this.email = new SimpleStringProperty(email); } public String getFirstName() { return firstName.get(); } public void setFirstName(String fName) { firstName.set(fName); } public String getLastName() { return lastName.get(); } public void setLastName(String fName) { lastName.set(fName); } public String getEmail() { return email.get(); } public void setEmail(String fName) { email.set(fName); } } }
In Figure 13-8, the user is editing the last name of Michael Brown. To edit a table cell, the user enters the new value in the cell, and then presses the Enter key. The cell is not modified until the Enter key is pressed. This behavior is determined by the implementation of the TextField
class.
Note that the default implementation of the TextField
control requires that users press the Enter key to commit the edit. You can redefine the TextField
behavior to commit the edit on the focus change, which is an expected user experience. Try the modified code in to implement such an alternative behavior.
Example 13-11 Alternative Solution Of Cell Editing
import javafx.application.Application; import javafx.beans.property.SimpleStringProperty; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.geometry.Insets; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TableColumn.CellEditEvent; import javafx.scene.control.TableView; import javafx.scene.control.TextField; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.scene.text.Font; import javafx.stage.Stage; import javafx.util.Callback; public class TableViewSample extends Application { private final TableView<Person> table = new TableView<>(); private final ObservableList<Person> data = FXCollections.observableArrayList( new Person("Jacob", "Smith", "jacob.smith@example.com"), new Person("Isabella", "Johnson", "isabella.johnson@example.com"), new Person("Ethan", "Williams", "ethan.williams@example.com"), new Person("Emma", "Jones", "emma.jones@example.com"), new Person("Michael", "Brown", "michael.brown@example.com")); final HBox hb = new HBox(); public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) { Scene scene = new Scene(new Group()); stage.setTitle("Table View Sample"); stage.setWidth(450); stage.setHeight(550); final Label label = new Label("Address Book"); label.setFont(new Font("Arial", 20)); table.setEditable(true); Callback<TableColumn<Person, String>, TableCell<Person, String>> cellFactory = (TableColumn<Person, String> p) -> new EditingCell(); TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name"); TableColumn<Person, String> lastNameCol = new TableColumn<>("Last Name"); TableColumn<Person, String> emailCol = new TableColumn<>("Email"); firstNameCol.setMinWidth(100); firstNameCol.setCellValueFactory( new PropertyValueFactory<>("firstName")); firstNameCol.setCellFactory(cellFactory); firstNameCol.setOnEditCommit( (CellEditEvent<Person, String> t) -> { ((Person) t.getTableView().getItems().get( t.getTablePosition().getRow()) ).setFirstName(t.getNewValue()); }); lastNameCol.setMinWidth(100); lastNameCol.setCellValueFactory( new PropertyValueFactory<>("lastName")); lastNameCol.setCellFactory(cellFactory); lastNameCol.setOnEditCommit( (CellEditEvent<Person, String> t) -> { ((Person) t.getTableView().getItems().get( t.getTablePosition().getRow()) ).setLastName(t.getNewValue()); }); emailCol.setMinWidth(200); emailCol.setCellValueFactory( new PropertyValueFactory<>("email")); emailCol.setCellFactory(cellFactory); emailCol.setOnEditCommit( (CellEditEvent<Person, String> t) -> { ((Person) t.getTableView().getItems().get( t.getTablePosition().getRow()) ).setEmail(t.getNewValue()); }); table.setItems(data); table.getColumns().addAll(firstNameCol, lastNameCol, emailCol); final TextField addFirstName = new TextField(); addFirstName.setPromptText("First Name"); addFirstName.setMaxWidth(firstNameCol.getPrefWidth()); final TextField addLastName = new TextField(); addLastName.setMaxWidth(lastNameCol.getPrefWidth()); addLastName.setPromptText("Last Name"); final TextField addEmail = new TextField(); addEmail.setMaxWidth(emailCol.getPrefWidth()); addEmail.setPromptText("Email"); final Button addButton = new Button("Add"); addButton.setOnAction((ActionEvent e) -> { data.add(new Person( addFirstName.getText(), addLastName.getText(), addEmail.getText())); addFirstName.clear(); addLastName.clear(); addEmail.clear(); }); hb.getChildren().addAll(addFirstName, addLastName, addEmail, addButton); hb.setSpacing(3); final VBox vbox = new VBox(); vbox.setSpacing(5); vbox.setPadding(new Insets(10, 0, 0, 10)); vbox.getChildren().addAll(label, table, hb); ((Group) scene.getRoot()).getChildren().addAll(vbox); stage.setScene(scene); stage.show(); } public static class Person { private final SimpleStringProperty firstName; private final SimpleStringProperty lastName; private final SimpleStringProperty email; private Person(String fName, String lName, String email) { this.firstName = new SimpleStringProperty(fName); this.lastName = new SimpleStringProperty(lName); this.email = new SimpleStringProperty(email); } public String getFirstName() { return firstName.get(); } public void setFirstName(String fName) { firstName.set(fName); } public String getLastName() { return lastName.get(); } public void setLastName(String fName) { lastName.set(fName); } public String getEmail() { return email.get(); } public void setEmail(String fName) { email.set(fName); } } class EditingCell extends TableCell<Person, String> { private TextField textField; public EditingCell() { } @Override public void startEdit() { if (!isEmpty()) { super.startEdit(); createTextField(); setText(null); setGraphic(textField); textField.selectAll(); } } @Override public void cancelEdit() { super.cancelEdit(); setText((String) getItem()); setGraphic(null); } @Override public void updateItem(String item, boolean empty) { super.updateItem(item, empty); if (empty) { setText(null); setGraphic(null); } else { if (isEditing()) { if (textField != null) { textField.setText(getString()); } setText(null); setGraphic(textField); } else { setText(getString()); setGraphic(null); } } } private void createTextField() { textField = new TextField(getString()); textField.setMinWidth(this.getWidth() - this.getGraphicTextGap()* 2); textField.focusedProperty().addListener( (ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2) -> { if (!arg2) { commitEdit(textField.getText()); } }); } private String getString() { return getItem() == null ? "" : getItem().toString(); } } }
Note that this approach might become redundant in future releases as the TextFieldTableCell
implementation is being evolved to provide better user experience.
You can add the Map
data to the table. Use the MapValueFactory
class as shown in Example 13-12 to display the map of student IDs in the table.
Example 13-12 Adding Map Data to the Table
import java.util.HashMap; import java.util.Map; import javafx.application.Application; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.geometry.Insets; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.cell.MapValueFactory; import javafx.scene.control.cell.TextFieldTableCell; import javafx.scene.layout.VBox; import javafx.scene.text.Font; import javafx.stage.Stage; import javafx.util.Callback; import javafx.util.StringConverter; public class TableViewSample extends Application { public static final String Column1MapKey = "A"; public static final String Column2MapKey = "B"; public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) { Scene scene = new Scene(new Group()); stage.setTitle("Table View Sample"); stage.setWidth(300); stage.setHeight(500); final Label label = new Label("Student IDs"); label.setFont(new Font("Arial", 20)); TableColumn<Map, String> firstDataColumn = new TableColumn<>("Class A"); TableColumn<Map, String> secondDataColumn = new TableColumn<>("Class B"); firstDataColumn.setCellValueFactory(new MapValueFactory(Column1MapKey)); firstDataColumn.setMinWidth(130); secondDataColumn.setCellValueFactory(new MapValueFactory(Column2MapKey)); secondDataColumn.setMinWidth(130); TableView tableView = new TableView<>(generateDataInMap()); tableView.setEditable(true); tableView.getSelectionModel().setCellSelectionEnabled(true); tableView.getColumns().setAll(firstDataColumn, secondDataColumn); Callback<TableColumn<Map, String>, TableCell<Map, String>> cellFactoryForMap = (TableColumn<Map, String> p) -> new TextFieldTableCell(new StringConverter() { @Override public String toString(Object t) { return t.toString(); } @Override public Object fromString(String string) { return string; } }); firstDataColumn.setCellFactory(cellFactoryForMap); secondDataColumn.setCellFactory(cellFactoryForMap); final VBox vbox = new VBox(); vbox.setSpacing(5); vbox.setPadding(new Insets(10, 0, 0, 10)); vbox.getChildren().addAll(label, tableView); ((Group) scene.getRoot()).getChildren().addAll(vbox); stage.setScene(scene); stage.show(); } private ObservableList<Map> generateDataInMap() { int max = 10; ObservableList<Map> allData = FXCollections.observableArrayList(); for (int i = 1; i < max; i++) { Map<String, String> dataRow = new HashMap<>(); String value1 = "A" + i; String value2 = "B" + i; dataRow.put(Column1MapKey, value1); dataRow.put(Column2MapKey, value2); allData.add(dataRow); } return allData; } }
The MapValueFactory
class implements the Callback
interface, and it is designed specifically to be used within cell factories of table columns. In Example 13-12, the dataRow hash map presents a single row in the TableView
object. The map has two String
keys: Column1MapKey and Column2MapKey to map the corresponding values in the first and second columns. The setCellValueFactory
method called for the table columns populates them with data matching with a particular key, so that the first column contains values that correspond to the "A" key and second column contains the values that correspond to the "B" key.
When you compile and run this application, it produces the output shown in Figure 13-9.
TreeTableView
is the further extension of the tabular data controls in JavaFX. Refer to the Tree Table View chapter for more information about this UI control.
Related API Documentation
In addition to the full range of desktop features, the JavaFX Software Development Kit (SDK) introduces new operational capabilities of UI controls designed for touch-enabled devices.
This chapter describes the specific of using JavaFX UI controls in the embedded environments.
Although JavaFX UI controls do not include any additional public APIs to work in the embedded environments, significant implementation changes were made to enable developers using the desktop controls in their JavaFX applications for touch-enabled devices.
The JavaFX SDK introduces additional operational capabilities of UI controls for touch-enabled devices: gestures and touches. Gestures can be generated for both touchscreen and trackpad actions, but touches are delivered for touchscreen actions only. In the current release JavaFX SDK supports only a single-point touch and only swipe gestures.
See the chapter on working with events from touch-enabled devices in Handling Events for more information about handling gestures and touch events in JavaFX.
The virtual keyboard is a control that enables typing text on devices that lack a hardware keyboard. It operates the same way as any hardware keyboard except that numbers and symbols are located one tap away due to space constraints. Figure 30-1 shows an example of the virtual keyboard.
The virtual keyboard is shown automatically when a text input field is in focus. Note that the control that is associated with the keyboard remains visible when the keyboard is displayed. There is no need to push the parent stage up. The keyboard slides in from the bottom area pushing the parent stage up in order to keep the control that the keyboard is associated with visible on the screen.
When the virtual keyboard appears, users can type a character by tapping the appropriate key. After that, character that is associated with the key is sent to the control. User can type alpha, numeric, and symbol characters. To switch between the keyboards, they need to use the ABC/!#123 key and the Shift key. To access less frequently used characters, users need to press the appropriate key for longer time and choose the target key from the pop-up menu, as shown in Figure 30-2.
Table 30-1 lists possible navigating actions within the virtual keyboard on touch-enabled devices.
Table 30-1 Navigation Within the Virtual Keyboard
Action | Sequence of Keys |
---|---|
Type a letter. |
On the alpha keyboard: type a letter key. On the numeric/alternative keyboard: tap the "ABC" key, then type a letter key. |
Type a capital letter. |
On the alpha keyboard: tap the Shift key once, then type a letter key. |
Type capital letters. |
On the alpha keyboard: tap the Shift key twice, then tap letter keys. |
Type a number. |
On the numeric/alternative keyboard: type a number. On the alpha keyboard: tap the "!#123" key and type any number you want. |
Type an alternative symbol. |
On the numeric/alternative keyboard: type an alternative symbol. On the alpha keyboard: tap the "!#123" key and type an alternative symbol. |
Delete a character. |
Tap the Backspace key to delete character on the left side of the caret. |
Confirmed entered data. |
Tap the Enter key to confirm entered data. |
Hide the virtual keyboard. |
Tap a Hide button that is left-most in the lowest row of the keyboard. |
When UI interface of your application requires typing email address, URL, or only numeric symbols, you can set one of the alternative keyboard layouts. This setting is defined for a particular text control as shown in Example 30-1.
Example 30-1 Setting Email Layout for the Virtual Keyboard
final TextField emailAddress = new TextField("myEmail@example.com");
text.getProperties().put("vkType", "email");
Currently, the vkType
property supports the following values: numeric
, url
, email
, and text
. The last one corresponds to the default layout.
Figure 30-3 shows the email layout of the keyboard. In addition to letters, number, and alternative symbols, the layout has "@" and ".com" (".org") keys that are particularly helpful for quick typing email addresses.
The URL layout of the virtual keyboard, shown in Figure 30-4, does not have the Space key. Instead, it provides capabilities to type in "www." and "http//".
In some UIs, users are supposed to enter only numbers. Figure 30-5 shows a simplified numeric layout of the virtual keyboard that implements this task. No Shift key, neither alternative symbols are available in this layout.
The embedded.css
style sheet is designed specifically for touch-enabled platforms. It supplements the modena.css
style sheet and overrides some of its styles. The UAStylesheetLoader
class manages the styles of UI controls switching the CSS files when a touch-enabled platform is detected.
The embedded.css
style sheet alters the appearance of the following controls:
ScrollBar
, ScrollPane
— redefines the scrolling elements
TextArea
, TextField
, PasswordField
, DatePicker
— visualizes carets
ContextMenu
— introduces the horizontal context menu
A scroll pane on a mobile touch platform performs scrolling as a result of touch gestures targeted on scroll view content. Scroll bars are hidden when the user is not interacting with the scroll pane, even if the content is bigger than the scroll pane. Scroll bars appear automatically after the touch gestures are performed, but only as an indicator of content size and position, not as an active control that the user uses for scrolling. When scroll bars appear, they overlap visible content and do not shift it like on the desktop platform, as shown in Figure 30-6.
Scroll bars disappear automatically when the content stops scrolling.
On touch-enabled platforms, TextArea
, TextField
, and PasswordField
are implemented as rectangular spaces where users can enter a single line or multiple lines of text. Interaction with text controls is similar for all platforms. However, for embedded environments, the caret is used to facilitate navigation and text selection. The virtual keyboard is used to enter characters into text controls.
Figure 30-7 shows the TextFieldSample application running on a touch-enabled platform. The caret indicates the place where a user enters the symbols with the help of the virtual keyboard. See the code at TextFieldSample.java.
The virtual keyboard is shown automatically when a text input field is in focus and it reacts when devices are rotated. The virtual keyboard appears when users need to enter text into TextField
, TextArea
, PasswordField
, and other UI controls if their edit state is enabled. To hide the keyboard, users can tap a Hide button or tap outside the control.
The default appearance of the context menu is changed for the embedded environments to provide the horizontal layout of the menu items.
The context menu is invoked when users tap and hold. If nothing is in the clipboard, then the context menu shows Select and Select All menu items. If something is in the clipboard, then the context menu looks as shown in Figure 30-8.
If the text in the text control is selected, then the Cut, Copy, Paste, and Select All items are displayed, as shown in Figure 30-9.
You can find another style sheet for the touch-enabled platforms in the JavaFX SDK. The embedded-qvga.css
file defines the CSS styles for embedded devices that have QVGA screens. In particular, it specifies alternative padding for the TextField
and TextArea
controls.
In addition to the visual changes mentioned in the previous section, the behavior of UI controls is modified to accommodate single point touches and swipe gestures. Table 30-2 summarizes these changes.
Table 30-2 Features Specific to Embedded Runtime
UI Control | User Action | Touch Support |
---|---|---|
|
Tap. |
Activates the button. |
|
Tap. |
Switches the selected and deselected states of a check box. |
|
Tap a drop-down icon. |
Displays or hides the active list. The item that is displayed in the text field is selected when the active list is open. |
Tap on the text field. |
For a noneditable combo box: displays the active list. For an editable combo box: places the caret into the text field. | |
Tap on an item in the list. |
Closes the active list and commits the value. | |
Tap outside the list. |
Closes the list. | |
Drag. |
Scrolls the content continuously following the drag gesture. | |
Drop. |
Stops scrolling. | |
Swipe. |
Invokes accelerated scrolling. | |
|
Drag. |
Scrolls the content continuously following the drag gesture. |
Drop. |
Stops scrolling. | |
Swipe. |
Invokes accelerated scrolling. | |
Tap. |
Selects an item and activates the action associated with it. If the content is scrolling, then it stops scrolling. | |
Double-tap. |
If enabled, enters inline editing mode. | |
|
Tap. |
Sets the caret. |
Double-tap. |
Selects the content. | |
Tap and hold. |
Opens a context menu. | |
Swipe. |
Invokes accelerated scrolling. | |
Drag . |
Scrolls the content continuously following the drag gesture. | |
Drop. |
Stops scrolling. When the user "over-scrolls" the content beyond the | |
|
Tap. |
If the radio button is selected, then no action is performed. If it is deselected, then the tap makes it selected and other radio buttons in the group are deselected. |
|
Drag. |
Scrolls the content continuously following the drag gesture. |
Drop. |
Stops scrolling. | |
Swipe. |
Invokes accelerated scrolling. | |
Tap. |
Selects an item and activates the action associated with it. If the content is scrolling, then it stops scrolling. | |
|
Tap. |
Selects the cell or invokes an action if the cell is actionable. Expands or collapses the node for the tree view. |
Double-tap. |
Switches to editing if the selected cell supports editing. | |
Drag. |
Scrolls the content continuously following the drag gesture. | |
Drop. |
Stops scrolling. | |
Swipe. |
Invokes accelerated scrolling of content. | |
|
Short tap a color in the Color field or Custom Colors area in the Color Palette. |
Updates the color in the color chooser. Closes the color palette. Applies the color. |
Tap outside the palette. |
Closes the palette. | |
Tap any place in the Color field of the Custom Color dialog window. |
Updates values in RGB, HSB, and Web panes. Updates New Color in the Color Preview. | |
Tap any place in the Hue bar of the Custom Color dialog window. |
Updates values in RGB, HSB, and Web panes. Updates New Color in the Custom Color dialog window. | |
Tap any place on the slider, or drag the sliders to the left or right. |
Dismisses the dialog window. Closes the color palette. Updates the color chooser. Applies the color. | |
|
Tap a page button. |
Opens the selected page. |
Tap the Next button, Swipe left. |
Opens the next page. | |
Tap the Previous button, Swipe right. |
Opens the previous page. | |
|
Tap the date field. |
Displays caret in the field; virtual keyboard appears. |
Tap the calendar icon. |
Displays and hides the calendar. | |
Tap the LEFT or RIGHT calendar arrows. |
Display previous or next month and year. | |
Tap any date in the calendar. |
Updates the field with selected date and closes the calendar. | |
Tap outside the calendar. |
Closes the calendar without updating the field. |
All the actions mentioned in Table 30-2 are enabled when the application is run on the touch-enabled platform, and they do not require any additional code.
The following list shows some features of UI controls that are currently disabled on embedded platforms:
Column resizing, column rearranging, and data sorting in table views
Multi selection in list views and tree views
Text copy support in password fields
Fhe following JavaFX features are not supported on the embedded platforms:
Functionality of the Stage
class related to desktop interaction is not available on some embedded platforms. For example, on these platforms a Stage
window will have not have a title or decorations and will not be resizable by the user.
Web component – a user interface component that provides a web 3viewer and full browsing functionality through its API. See Adding HTML Content to JavaFX Applications for more information.
Media – media functionality available through the Java APIs for JavaFX. See Incorporating Media Assets Into JavaFX Applications for more information.
This chapter describes the aspects of UI control customization and summarizes some tips and tricks provided by Oracle to help you modify the appearance and behavior of UI controls.
You can learn how to customize the controls from the sample applications in the UI Control Samples project by applying Cascading Style Sheets (CSS), redefining the default behavior, and using cell factories. For more specific cases, when the task of your application requires unique features that cannot be implemented with the classes of the javafx.scene.control
package, extend the Control
class to invent your own control.
You can change the look of UI controls by redefining the style definitions of the JavaFX modena
style sheets. Skinning JavaFX Applications with CSS explains the general concepts and approaches to modifying the styles and enabling them in a JavaFX application.
Consider some of the specific tasks that are frequently requested by developers at the JavaFX forum.
Although the Tooltip
class does not have any properties or methods to change the default color of the tooltip, you can modify the -fx-background-color
property of the .tooltip
CSS class as shown in Example 29-1.
Example 29-1 Changing the Background Color of a Tooltip
.tooltip { -fx-background-color: linear-gradient(#e2ecfe, #99bcfd); } .page-corner { -fx-background-color: linear-gradient(from 0% 0% to 50% 50%,#3278fa,#99bcfd); }
The .page-corner
CSS class defines the color of the right-bottom corner of the tooltip. When you add the code in Example 29-1 to the style sheets of the TooltipSample and apply the style sheets to the scene, the tooltip changes its color to blue. See Figure 29-1 to evaluate the effect.
Note that when you modify the default style of a tooltip, the new look is applied to all the tooltips in your application.
Another popular design task is changing the default marks for the controls. For example, the default style of the CheckBox
class defines the traditional check mark for the selected state. You can redefine the shape of the mark as well as its color as shown in Example 29-2.
Example 29-2 Alternative Mark for a Checkbox
.check-box .mark { -fx-shape: "M2,0L5,4L8,0L10,0L10,2L6,5L10,8L10,10L8,10L5,6L2,10L0,10L0,8L4,5L0,2L0,0Z"; } .check-box:selected .mark { -fx-background-color: #0181e2; }
The -fx-shape
property sets the new SVG path for the mark, and the -fx-background-color
property defines its color. When the modified style sheets are enabled in the CheckBoxSample application, the selected checkboxes contain X marks instead of check marks, as shown in Figure 29-2.
Many developers asked how to overcome the limitation in visual style of the TableView
and ListView
controls. By default, all rows in these controls are shown, whether they are empty or not. With the proper CSS settings, you can set a specific color for all empty rows. Example 29-3 implements this task for a TableView
control.
Example 29-3 Setting Color for Empty Rows in a Table View
.table-row-cell:empty { -fx-background-color: lightyellow; } .table-row-cell:empty .table-cell { -fx-border-width: 0px; }
The first CSS style determines that all empty rows, regardless of whether they are even or odd, should have light yellow backgrounds. When the table-row-cell is empty, the second CSS statement removes the vertical border that is painted on the right-hand side of all table cells.
When the CSS styles from Example 29-3 are enabled in the TableViewSample application, the Address Book table looks as shown Figure 29-3.
You can even set the null
value for the background color of the empty cells. The style sheets will use the default background color of the table view in this case. See Figure 29-4 to evaluate the effect.
You can set more CSS properties for UI Controls to alter their shapes, color schemes, and the applied effects. See the JavaFX CSS Reference Guide for more information about available CSS properties and classes.
Many developers requested a specific API to restrict input in the text field, for example, to allow only number values. Example 29-4 provides a simple application with a numeric text field.
Example 29-4 Prohibiting Letters in the Text Field
import javafx.application.Application; import javafx.event.ActionEvent; import javafx.geometry.Insets; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.layout.GridPane; import javafx.stage.Stage; public class CustomTextFieldSample extends Application { final static Label label = new Label(); @Override public void start(Stage stage) { Group root = new Group(); Scene scene = new Scene(root, 300, 150); stage.setScene(scene); stage.setTitle("Text Field Sample"); GridPane grid = new GridPane(); grid.setPadding(new Insets(10, 10, 10, 10)); grid.setVgap(5); grid.setHgap(5); scene.setRoot(grid); final Label dollar = new Label("$"); GridPane.setConstraints(dollar, 0, 0); grid.getChildren().add(dollar); final TextField sum = new TextField() { @Override public void replaceText(int start, int end, String text) { if (!text.matches("[a-z, A-Z]")) { super.replaceText(start, end, text); } label.setText("Enter a numeric value"); } @Override public void replaceSelection(String text) { if (!text.matches("[a-z, A-Z]")) { super.replaceSelection(text); } } }; sum.setPrefColumnCount(10); GridPane.setConstraints(sum, 1, 0); grid.getChildren().add(sum); Button submit = new Button("Submit"); GridPane.setConstraints(submit, 2, 0); grid.getChildren().add(submit); submit.setOnAction((ActionEvent e) -> { label.setText(null); }); GridPane.setConstraints(label, 0, 1); GridPane.setColumnSpan(label, 3); grid.getChildren().add(label); scene.setRoot(grid); stage.show(); } public static void main(String[] args) { launch(args); } }
To redefine the default implementation of the TextField
class, you must override the replaceText
and replaceSelection
methods inherited from the TextInputControl
class.
When the user tries to enter any letter in the Sum text field, no symbols appear, and the warning message is shown. Figure 29-5 illustrates this situation.
However, when the user attempts to enter the numeric values, they appear in the field as shown in Figure 29-6.
Appearance and even behavior of four UI controls can be entirely customized by using the mechanism of cell factories. You can apply cell factories to TableView
, ListView
, TreeView
, and ComboBox
. A cell factory is used to generate cell instances, which are used to represent any single item of these controls.
The Cell
class extends the Labeled
class, which provides all the required properties and methods to implement the most typical use case — showing and editing text. However, when the task of your application requires showing graphical objects in the lists or tables, you can use the graphic
property and place any Node
in the cell (see the Cell class API specification for more information about custom cells).
For instance, the code fragments in Example 29-5 create a cell factory for the list view and redefine the content of the cells within the updateItem
method, so that the list shows rectangles of different colors.
Example 29-5 Implementing Cell Factories for the ListView Control
list.setCellFactory((ListView<String> l) -> new ColorRectCell()); ... static class ColorRectCell extends ListCell<String> { @Override public void updateItem(String item, boolean empty) { super.updateItem(item, empty); Rectangle rect = new Rectangle(100, 20); if (item != null) { rect.setFill(Color.web(item)); setGraphic(rect); } else { setGraphic(null); } } }
Figure 29-7 shows how this customized list looks in the ListViewSample of the UI Control Samples project.
This tutorial uses the cell factory mechanism extensively to customize UI controls. You can customize these controls by using the cell factory mechanism or use the prefabricated cell editor implementations that provide specific data models underlying the visualization. Table 29-1 lists the corresponding classes available in the JavaFX API.
Table 29-1 Cell Editor Classes for the List View, Tree View, and Table View Controls
Control | Cell Editor Classes |
---|---|
List view |
|
Tree view |
|
Table view |
|
Tree Table View |
|
Each cell editor class draws a specific node inside the cell. For example, the CheckBoxListCell
class draws a CheckBox
node inside the list cell.
To evaluate more cell factory and cell editor use cases, see the Table View, Tree View, and Combo Box chapters.
Related Documentation and Resources
Working with JavaFX UI Components
Release 8
E47848-02
August 2014
In this tutorial, you learn how to build user interfaces in your JavaFX applications with the UI components available through the JavaFX API.
JavaFX Working with JavaFX UI Components Release 8
E47848-02
Copyright © 2011, 2014, Oracle and/or its affiliates. All rights reserved.
Primary Author: Alla Redko, Irina Fedortsova
Primary Author:
This software and related documentation are provided under a license agreement containing restrictions on use and disclosure and are protected by intellectual property laws. Except as expressly permitted in your license agreement or allowed by law, you may not use, copy, reproduce, translate, broadcast, modify, license, transmit, distribute, exhibit, perform, publish, or display any part, in any form, or by any means. Reverse engineering, disassembly, or decompilation of this software, unless required by law for interoperability, is prohibited.
The information contained herein is subject to change without notice and is not warranted to be error-free. If you find any errors, please report them to us in writing.
If this is software or related documentation that is delivered to the U.S. Government or anyone licensing it on behalf of the U.S. Government, the following notice is applicable:
U.S. GOVERNMENT RIGHTS Programs, software, databases, and related documentation and technical data delivered to U.S. Government customers are "commercial computer software" or "commercial technical data" pursuant to the applicable Federal Acquisition Regulation and agency-specific supplemental regulations. As such, the use, duplication, disclosure, modification, and adaptation shall be subject to the restrictions and license terms set forth in the applicable Government contract, and, to the extent applicable by the terms of the Government contract, the additional rights set forth in FAR 52.227-19, Commercial Computer Software License (December 2007). Oracle USA, Inc., 500 Oracle Parkway, Redwood City, CA 94065.
This software or hardware is developed for general use in a variety of information management applications. It is not developed or intended for use in any inherently dangerous applications, including applications that may create a risk of personal injury. If you use this software or hardware in dangerous applications, then you shall be responsible to take all appropriate fail-safe, backup, redundancy, and other measures to ensure its safe use. Oracle Corporation and its affiliates disclaim any liability for any damages caused by use of this software or hardware in dangerous applications.
Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners.
This software and documentation may provide access to or information on content, products, and services from third parties. Oracle Corporation and its affiliates are not responsible for and expressly disclaim all warranties of any kind with respect to third-party content, products, and services. Oracle Corporation and its affiliates will not be responsible for any loss, costs, or damages incurred due to your access to or use of third-party content, products, or services.
In this chapter you learn how to apply single effects and a chain of effects to text nodes. It includes demo samples to illustrate the APIs being used.
The JavaFX SDK provides a wide set of effects that reside in the javafx.scene.effect
package. For a complete set of available effects, see the API documentation. You can see some of the effects in action in the TextEffects
demo application. This application displays text nodes with various effects. Download the TextEffectsSample.zip
file, extract the files, save them on your computer, and open a NetBeans project in the NetBeans IDE.
The PerspectiveTansform
class enables you to imitate a three-dimensional effect for your two-dimensional content. The perspective transformation can map an arbitrary quadrilateral into another quadrilateral. An input for this transformation is your node. To define the perspective transformation, you specify the x and y coordinates of output locations of all four corners. In the TextEffects
application, the PerspectiveTransform effect is set for a group consisting of a rectangle and text as shown in Example 40-1.
Example 40-1
PerspectiveTransform pt = new PerspectiveTransform();
pt.setUlx(10.0f);
pt.setUly(10.0f);
pt.setUrx(310.0f);
pt.setUry(40.0f);
pt.setLrx(310.0f);
pt.setLry(60.0f);
pt.setLlx(10.0f);
pt.setLly(90.0f);
g.setEffect(pt);
g.setCache(true);
Rectangle r = new Rectangle();
r.setX(10.0f);
r.setY(10.0f);
r.setWidth(280.0f);
r.setHeight(80.0f);
r.setFill(Color.BLUE);
Text t = new Text();
t.setX(20.0f);
t.setY(65.0f);
t.setText("Perspective");
t.setFill(Color.YELLOW);
t.setFont(Font.font(null, FontWeight.BOLD, 36));
g.getChildren().add(r);
g.getChildren().add(t);
return g;
Using the setCache(true)
property enables you to improve performance when rendering the node.
You can see the result of the perspective transformation in Figure 40-1.
The GaussianBlur
class provides a blur effect based on a Gaussian convolution kernel.
Example 40-2 shows a text node with an applied Gaussian blur effect implemented in the TextEffects
application.
Example 40-2
Text t2 = new Text(); t2.setX(10.0f); t2.setY(140.0f); t2.setCache(true); t2.setText("Blurry Text"); t2.setFill(Color.RED); t2.setFont(Font.font(null, FontWeight.BOLD, 36)); t2.setEffect(new GaussianBlur()); return t2;
You can see the result of the Gaussian blur effect in Figure 40-2.
To implement a drop shadow effect, use the DropShadow
class. You can specify a color and an offset for the shadow of your text. In the TextEffects
application, the drop shadow effect is applied to the red text and provides a three-pixel gray shadow. You can see the code in Example 40-3.
Example 40-3
DropShadow ds = new DropShadow(); ds.setOffsetY(3.0f); ds.setColor(Color.color(0.4f, 0.4f, 0.4f)); Text t = new Text(); t.setEffect(ds); t.setCache(true); t.setX(10.0f); t.setY(270.0f); t.setFill(Color.RED); t.setText("JavaFX drop shadow..."); t.setFont(Font.font(null, FontWeight.BOLD, 32));
You can see the result of the applied effect in Figure 40-3.
An inner shadow effect renders a shadow inside the edges of your content. For text content, you can specify a color and an offset. See how the inner shadow effect with four-pixel offsets in the x and y directions is applied to a text node in Example 40-4.
Example 40-4
InnerShadow is = new InnerShadow(); is.setOffsetX(4.0f); is.setOffsetY(4.0f); Text t = new Text(); t.setEffect(is); t.setX(20); t.setY(100); t.setText("InnerShadow"); t.setFill(Color.YELLOW); t.setFont(Font.font(null, FontWeight.BOLD, 80)); t.setTranslateX(300); t.setTranslateY(300); return t;
The result of the applied effect is shown in Figure 40-4.
The Reflection
class enables you to display the reflected version of your text below the original text. You can adjust the view of your reflected text by providing additional parameters such as a bottom opacity value, a fraction of the input to be visible in the reflection, an offset between the input text and its reflection, and a top opacity value. For details, see the API documentation.
The reflection effect is implemented in the TextEffects
application as shown in Example 40-5.
Example 40-5
Text t = new Text(); t.setX(10.0f); t.setY(50.0f); t.setCache(true); t.setText("Reflections on JavaFX..."); t.setFill(Color.RED); t.setFont(Font.font(null, FontWeight.BOLD, 30)); Reflection r = new Reflection(); r.setFraction(0.7f); t.setEffect(r); t.setTranslateY(400); return t;
You can see the text node with the reflection effect in Figure 40-5.
In the previous section you learned how to apply a single effect to a text node. To further enrich your text content, you can compose several effects and apply a chain of effects to achieve a specific visual result. Consider the NeonSign
application shown in Figure 40-6.
The graphical scene of the NeonSign
application consists of the following elements:
An image of a brick wall used as a background
A rectangle that provides a radial gradient fill
A text node with a chain of effects
A text field used for entering text data
The background is set in an external .css file.
This application uses a binding mechanism to set the text content on the text node. The text property of the text node is bound to the text property of the text field as shown in Example 40-6.
Example 40-6
Text text = new Text(); TextField textField = new TextField(); textField.setText("Neon Sign"); text.textProperty().bind(textField.textProperty());
You can type in the text field and view the changed contents of the text node.
A chain of effects is applied to the text node. The primary effect is a blend effect that uses the MULTIPLY
mode to mix two inputs together: a drop shadow effect and another blend effect, blend1
. Similarly, the blend1
effect combines a drop shadow effect (ds1
) and a blend effect (blend2
). The blend2
effect combines two inner shadow effects. Using this chain of effects and varying color parameters enables you to apply subtle and sophisticated color patterns to text objects. See the code for the chain of effects in Example 40-7.
Example 40-7
Blend blend = new Blend(); blend.setMode(BlendMode.MULTIPLY); DropShadow ds = new DropShadow(); ds.setColor(Color.rgb(254, 235, 66, 0.3)); ds.setOffsetX(5); ds.setOffsetY(5); ds.setRadius(5); ds.setSpread(0.2); blend.setBottomInput(ds); DropShadow ds1 = new DropShadow(); ds1.setColor(Color.web("#f13a00")); ds1.setRadius(20); ds1.setSpread(0.2); Blend blend2 = new Blend(); blend2.setMode(BlendMode.MULTIPLY); InnerShadow is = new InnerShadow(); is.setColor(Color.web("#feeb42")); is.setRadius(9); is.setChoke(0.8); blend2.setBottomInput(is); InnerShadow is1 = new InnerShadow(); is1.setColor(Color.web("#f13a00")); is1.setRadius(5); is1.setChoke(0.4); blend2.setTopInput(is1); Blend blend1 = new Blend(); blend1.setMode(BlendMode.MULTIPLY); blend1.setBottomInput(ds1); blend1.setTopInput(blend2); blend.setTopInput(blend1); text.setEffect(blend);
In this article, you learned how to add text and apply various effects to text content. For a complete set of available effects, see the API documentation.
If you need to implement a text editing area in your JavaFX application, use the HTMLEditor
component. For more information about the HTMLEditor
control, see HTML Editor.
This chapter explains how to add a pagination control to your JavaFX application. It teaches how to add a pagination control to your application, manage its page items, and style the elements of the control with CSS styles.
The pagination control that is used to navigate through multiple pages of content that are divided into smaller parts. Typical uses include navigating through email messages in a mailbox or choosing among search results. In touch devices, a pagination control can be used to browse through single pages of an article or to navigate between screens. Figure 27-1 shows a pagination control that displays the fonts available in an operating system.
A pagination control consists of the page content and the page navigation areas. The Page content area renders and lays out the content according to the application logic. The Page navigation area contains a prefabricated control to preview a particular part of the content. Figure 27-2 shows the structure and basic elements of the navigation area.
You can navigate through the pages by clicking a particular page indicator or by clicking the Next page and Previous page buttons. When the first page is selected, the Previous page button is disabled. Similarly, when the last navigation indicator is selected, the Next page button is disabled.
The JavaFX SDK API provides the Pagination
class to add the pagination control to your JavaFX application. Example 27-1 shows three available constructors of the Pagination
class.
Example 27-1 Three Constructors of the Pagination Class
//Creates a Pagination control with an INDETERMINATE page count //and the current page index equal to zero Pagination pagination1 = new Pagination(); //Creates a Pagination control with 5 pages //and the current page index equal to zero Pagination pagination2 = new Pagination(5); //Creates a Pagination control with 5 pages //and the current selected index equal to 2 Pagination pagination3 = new Pagination(5, 2);
The pagination3 control from Example 27-1 is displayed in Figure 27-3.
Note that page indexes start with 0. Therefore, to start with the third page selected, you need to set the currentPageIndexProperty
to 2.
The pages of the pagination3 control are empty, because no content is added to the control.
You cannot add any items directly to the pagination control, it requires setting a page factory. Use the setPageFactory
method of the Pagination
class to define the page content by implementing a page factory.
The setPageFactory
is used to define a page factory for the pagination control. The application developer creates a callback method and sets the pagination page factory to use this callback. The callback method is called when a page has been selected. It loads and returns the content of the selected page. The null
value must be returned if the selected page index does not exist. Example 27-2 creates a pagination control with 28 pages and populates the pages with the search results, allocating eight items per page.
Example 27-2 Adding Hyperlinks to a Pagination Control
import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Pagination; import javafx.scene.control.Hyperlink; import javafx.scene.control.Label; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.VBox; import javafx.stage.Stage; public class PaginationSample extends Application { private Pagination pagination; public static void main(String[] args) throws Exception { launch(args); } public int itemsPerPage() { return 8; } public VBox createPage(int pageIndex) { VBox box = new VBox(5); int page = pageIndex * itemsPerPage(); for (int i = page; i < page + itemsPerPage(); i++) { VBox element = new VBox(); Hyperlink link = new Hyperlink("Item " + (i+1)); link.setVisited(true); Label text = new Label("Search results\nfor "+ link.getText()); element.getChildren().addAll(link, text); box.getChildren().add(element); } return box; } @Override public void start(final Stage stage) throws Exception { pagination = new Pagination(28, 0); pagination.setStyle("-fx-border-color:red;"); pagination.setPageFactory((Integer pageIndex) -> createPage(pageIndex)); AnchorPane anchor = new AnchorPane(); AnchorPane.setTopAnchor(pagination, 10.0); AnchorPane.setRightAnchor(pagination, 10.0); AnchorPane.setBottomAnchor(pagination, 10.0); AnchorPane.setLeftAnchor(pagination, 10.0); anchor.getChildren().addAll(pagination); Scene scene = new Scene(anchor); stage.setScene(scene); stage.setTitle("PaginationSample"); stage.show(); } }
The number of pages and the selected page are defined within the constructor of the Pagination
class. Alternatively, you can create a Pagination
control and set the number of pages and the index of the selected page afterward by using the setPageCount
and setCurrentPageIndex
methods.
The content of the Pagination
control is declared within the createPage
method that serves as a page factory and is called by the setPageFactory
method. The createPage
method creates the pairs of hyperlinks and the corresponding labels, and arranges them vertically, setting a five-pixel interval between the elements.
When you compile and run Example 27-2, you should see the output shown in Figure 27-4.
The current implementation of the Pagination
control displays 10 page indicators if the number of pages exceeds 10. To set an alternative value for the displayed page indicators, use the setMaxPageIndicatorCount
method of the Pagination
class. For example, add the following line to Example 27-2 to show seven page indicators: pagination.setMaxPageIndicatorCount(7);
. Figure 27-5 shows the PaginationSample application after the change has been applied.
Example 27-3 shows another use for the pagination control. The application renders the text fragments, one per page. The number of the fragments is five, and the number of the declared pages of the pagination control is 28. To avoid an ArrayIndexOutOfBoundsException
condition, add the page index check (highlighted in bold in Example 27-3) and make the callback method return null
when the number of pages exceeds five.
Example 27-3 Adding Text Snippets to a Pagination Control
import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Pagination; import javafx.scene.control.TextArea; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.VBox; import javafx.stage.Stage; public class PaginationSample extends Application { private Pagination pagination; final String[] textPages = new String[]{ "The apple is the pomaceous fruit of the apple tree, species Malus " + "domestica in the rose family (Rosaceae). It is one of the most " + "widely cultivated tree fruits, and the most widely known of " + "the many members of genus Malus that are used by humans. " + "The tree originated in Western Asia, where its wild ancestor, " + "the Alma, is still found today.", "The hawthorn is a large genus of shrubs and trees in the rose family," + "Rosaceae, native to temperate regions of the Northern Hemisphere " + "in Europe, Asia and North America. The name hawthorn was " + "originally applied to the species native to northern Europe, " + "especially the Common Hawthorn C. monogyna, and the unmodified " + "name is often so used in Britain and Ireland.", "The ivy is a flowering plant in the grape family (Vitaceae) native to " + " eastern Asia in Japan, Korea, and northern and eastern China. " + "It is a deciduous woody vine growing to 30 m tall or more given " + "suitable support, attaching itself by means of numerous small " + "branched tendrils tipped with sticky disks.", "The quince is the sole member of the genus Cydonia and is native to " + "warm-temperate southwest Asia in the Caucasus region. The " + "immature fruit is green with dense grey-white pubescence, most " + "of which rubs off before maturity in late autumn when the fruit " + "changes color to yellow with hard, strongly perfumed flesh.", "Aster (syn. Diplopappus Cass.) is a genus of flowering plants " + "in the family Asteraceae. The genus once contained nearly 600 " + "species in Eurasia and North America, but after morphologic " + "and molecular research on the genus during the 1990s, it was " + "decided that the North American species are better treated in a " + "series of other related genera. After this split there are " + "roughly 180 species within the genus, all but one being confined " + "to Eurasia." }; public static void main(String[] args) throws Exception { launch(args); } public int itemsPerPage() { return 1; } public VBox createPage(int pageIndex) { VBox box = new VBox(5); int page = pageIndex * itemsPerPage(); for (int i = page; i < page + itemsPerPage(); i++) { TextArea text = new TextArea(textPages[i]); text.setWrapText(true); box.getChildren().add(text); } return box; } @Override public void start(final Stage stage) throws Exception { pagination = new Pagination(28, 0); pagination.setStyle("-fx-border-color:red;"); pagination.setPageFactory((Integer pageIndex) -> { if (pageIndex >= textPages.length) { return null; } else { return createPage(pageIndex); } }); AnchorPane anchor = new AnchorPane(); AnchorPane.setTopAnchor(pagination, 10.0); AnchorPane.setRightAnchor(pagination, 10.0); AnchorPane.setBottomAnchor(pagination, 10.0); AnchorPane.setLeftAnchor(pagination, 10.0); anchor.getChildren().addAll(pagination); Scene scene = new Scene(anchor, 400, 250); stage.setScene(scene); stage.setTitle("PaginationSample"); stage.show(); } }
When you compile and run Example 27-3, you will see the output shown in Figure 27-6.
In some cases you cannot set the exact number of items to render and, therefore, the number of pages in a pagination control. In such situations, you can include a line of code that calculates the number of pages within the constructor of the Pagination
object. Example 27-4 outputs a list of system fonts and calculates the number of pages as the length of the fonts array divided by the number of items per page.
Example 27-4 Adding Content of an Undetermined Size
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Pagination;
import javafx.scene.control.Label;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class Test8 extends Application {
private Pagination pagination;
String[] fonts = new String[]{};
public static void main(String[] args) throws Exception {
launch(args);
}
public int itemsPerPage() {
return 15;
}
public VBox createPage(int pageIndex) {
VBox box = new VBox(5);
int page = pageIndex * itemsPerPage();
for (int i = page; i < page + itemsPerPage(); i++) {
Label font = new Label(fonts[i]);
box.getChildren().add(font);
}
return box;
}
@Override
public void start(final Stage stage) throws Exception {
fonts = Font.getFamilies().toArray(fonts);
pagination = new Pagination(fonts.length/itemsPerPage(), 0);
pagination.setStyle("-fx-border-color:red;");
pagination.setPageFactory((Integer pageIndex) -> createPage(pageIndex));
AnchorPane anchor = new AnchorPane();
AnchorPane.setTopAnchor(pagination, 10.0);
AnchorPane.setRightAnchor(pagination, 10.0);
AnchorPane.setBottomAnchor(pagination, 10.0);
AnchorPane.setLeftAnchor(pagination, 10.0);
anchor.getChildren().addAll(pagination);
Scene scene = new Scene(anchor, 400, 450);
stage.setScene(scene);
stage.setTitle("PaginationSample");
stage.show();
}
}
When you compile and run this example, it produces the application window shown in Figure 27-7.
You can customize the pagination control to display bullet page indicators instead of numeric page indicators by setting the style class STYLE_CLASS_BULLET
. In addition, you can modify the default pagination styles in the modena
style sheet.
Example 27-5 shows some alternative styles for the visual elements of the pagination control in Example 27-4.
Example 27-5 Modified Styles of the Pagination Control
.pagination { -fx-border-color: #0E5D79; } .pagination .page { -fx-background-color: #DDF1F8; } .pagination .pagination-control { -fx-background-color: #C8C6C6; } .pagination .pagination-control .bullet-button { -fx-background-color: transparent, #DDF1F8, #0E5D79, white, white; } .pagination .pagination-control .bullet-button:selected { -fx-background-color: transparent, #DDF1F8, #0E5D79, white, #0E5D79; } .pagination .pagination-control .left-arrow, .right-arrow{ -fx-background-color: #DDF1F8, #0E5D79; }
Example 27-6 applies these styles to the pagination control and sets the bullet style for the page indicators.
Example 27-6 Enabling the Modified Pagination Control Style in the PaginationSample Application
import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Pagination; import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.VBox; import javafx.scene.text.Font; import javafx.stage.Stage; import javafx.util.Callback; public class PaginationSample extends Application { private Pagination pagination; String[] fonts = new String[]{}; public static void main(String[] args) throws Exception { launch(args); } public int itemsPerPage() { return 15; } public VBox createPage(int pageIndex) { VBox box = new VBox(5); int page = pageIndex * itemsPerPage(); for (int i = page; i < page + itemsPerPage(); i++) { Label font = new Label(fonts[i]); box.getChildren().add(font); } return box; } @Override public void start(final Stage stage) throws Exception { fonts = Font.getFamilies().toArray(fonts); pagination = new Pagination(fonts.length/itemsPerPage(), 0); pagination.getStyleClass().add(Pagination.STYLE_CLASS_BULLET); pagination.setPageFactory((Integer pageIndex) -> createPage(pageIndex)); AnchorPane anchor = new AnchorPane(); AnchorPane.setTopAnchor(pagination, 10.0); AnchorPane.setRightAnchor(pagination, 10.0); AnchorPane.setBottomAnchor(pagination, 10.0); AnchorPane.setLeftAnchor(pagination, 10.0); anchor.getChildren().addAll(pagination); Scene scene = new Scene(anchor, 400, 450); stage.setScene(scene); stage.setTitle("PaginationSample"); scene.getStylesheets().add("paginationsample/ControlStyle.css"); stage.show(); } }
When you apply the newly defined styles to the PaginationSample application, and compile and run it, you see the application window shown in Figure 27-8.
In addition to the applied styles you can consider the following styles to alter the appearance of the pagination control in your applications:
-fx-max-page-indicator-count
— Sets the maximum number of page indicators.
-fx-arrows-visible
— Toggles visibility of the Next and Previous button arrows, true
by default.
-fx-tooltip-visible
— Toggles visibility of the page indicator tooltip, true
by default.
-fx-page-information-visible
— Toggles visibility of the page information, true
by default.
-fx-page-information-alignment
— Sets the alignment of the page information.
Related API Documentation
This tutorial covers built-in JavaFX UI controls available in the JavaFX API.
The document contains the following chapters:
Each chapter provides code samples and applications to illustrate how a particular UI control functions. You can find the source files of the applications and the corresponding NetBeans projects in the table of contents.
This chapter explains how to add text to your JavaFX applications.
It also includes code samples to illustrate the APIs being used.
The graphical content of JavaFX applications consists of objects organized in a tree-like structure called a scene graph. A single element in the scene graph is called a node. Nodes can handle different types of content, including text. Nodes can be transformed and animated. You can also apply various effects to nodes. Using features common to all node types enables you to provide sophisticated text content that meets the demands of modern rich Internet applications (RIAs).
The JavaFX SDK provides the javafx.scene.text.Text
class that is used to display text. The Text
class inherits from the Node
class. For this reason, you can apply effects, animation, and transformations to text nodes in the same way as to any other nodes. Because the Node
class inherits from the Shape
class, you can set a stroke or apply a fill setting to text nodes in the same way as to any shape.
To add a text object to your application, use any of the constructors shown in Example 39-1 through Example 39-3.
The constructor in Example 39-3 creates a text object at the coordinates specified with the first two parameters.
When adding text, you can also set some of its properties. To set the font, you can use an instance of the javafx.scene.text.Font
class. The Font.font()
method enables you to specify the font family name and size. You can also set the text color as shown in Example 39-4.
Example 39-4
t.setText("This is a text sample"); t.setFont(Font.font ("Verdana", 20)); t.setFill(Color.RED);
Alternatively, you may want to use a system font, which varies depending on the platform. For this purpose, call the Font.getDefault()
method.
In the production code, Oracle recommends that you set the styles using cascading style sheets (CSS). For example, to be able to apply a linear gradient fill to your text objects, add the style with the required rules to your CSS as shown in Example 39-5.
Example 39-5
#fancytext { -fx-font: 100px Tahoma; -fx-fill: linear-gradient(from 0% 0% to 100% 200%, repeat, aqua 0%, red 50%); -fx-stroke: black; -fx-stroke-width: 1; }
In your code, create a text object and apply the style from CSS as shown in Example 39-6.
This code creates the text shown in Figure 39-1.
For more details about using CSS in JavaFX applications, see Skinning JavaFX Applications with CSS.
To make the text look bold, use the FontWeight
constant of the font
method as shown in Example 39-7.
To display text in italic, use the FontPosture
constant as shown in Example 39-8.
If you need to use a unique font that might not be installed on another computer, you can include a TrueType font (.ttf) or an OpenType (.otf) in your JavaFX application.
To include a TrueType or OpenType font as a custom font, use the following procedure:
Create a resources/fonts
folder in your project folder.
Copy your font files to the fonts
subfolder in your project.
In your source code, load the custom font as shown in Example 39-9.
This code provides the font for the text shown in Figure 39-2.
LCD (liquid crystal display) text is an anti-aliased text that takes advantage of the properties of LCD panels to render smoother text. You can take advantage of the LCD text on the text nodes by using the API shown in Example 39-10.
Alternatively, you can provide this setting in a .css file by using the syntax shown in Example 39-11.
You can create several Text
nodes and lay them out in a single text flow by using the TextFlow
layout pane. The TextFlow
object employs the text and font of each Text
node but ignores the wrapping width and the x and y properties of its children. The TextFlow
object uses its own width and text alignment to determine the location of each child. Example 39-12 shows three Text
nodes that have different fonts and text laid out in a TextFlow
pane.
Example 39-12
String family = "Helvetica"; double size = 50; TextFlow textFlow = new TextFlow(); textFlow.setLayoutX(40); textFlow.setLayoutY(40); Text text1 = new Text("Hello "); text1.setFont(Font.font(family, size)); text1.setFill(Color.RED); Text text2 = new Text("Bold"); text2.setFill(Color.ORANGE); text2.setFont(Font.font(family, FontWeight.BOLD, size)); Text text3 = new Text(" World"); text3.setFill(Color.GREEN); text3.setFont(Font.font(family, FontPosture.ITALIC, size)); textFlow.getChildren().addAll(text1, text2, text3); Group group = new Group(textFlow); Scene scene = new Scene(group, 500, 150, Color.WHITE); stage.setTitle("Hello Rich Text"); stage.setScene(scene); stage.show();
This code provides the output shown in Figure 39-3.
You can embed objects such as shapes and images together with the text nodes in a TextFlow
object or create text with bidirectional support as shown in Example 39-13.
Example 39-13
import javafx.application.Application; import static javafx.application.Application.launch; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.paint.Color; import javafx.scene.text.Font; import javafx.scene.text.Text; import javafx.scene.text.TextFlow; import javafx.stage.Stage; public class JavaFXBidiText extends Application { @Override public void start(Stage stage) throws Exception { TextFlow textFlow = new TextFlow(); Font font = new Font("Tahoma", 48); Text text1 = new Text("He said \u0627\u0644\u0633\u0644\u0627\u0645"); text1.setFill(Color.RED); text1.setFont(font); Text text2 = new Text(" \u0639\u0644\u064a\u0643\u0645 to me."); text2.setFill(Color.BLUE); text2.setFont(font); textFlow.getChildren().addAll(text1, text2); Group group = new Group(textFlow); Scene scene = new Scene(group, 650, 150, Color.WHITE); stage.setTitle("Hello Bidi Text"); stage.setScene(scene); stage.show(); }
This code creates the output shown in Figure 39-4.
This example shows how the content of a single Text
node can be divided and placed in different locations on the TextFlow
object due to bidirectional reordering.
The default orientation of the TextFlow
object is left-to-right with Arabic words to be rendered right-to-left. There are two Arabic words to be rendered together: the red word is rendered first at the rightmost position and is followed to the left by the blue word.
You can change the default orientation of the TextFlow
object by calling
textFlow.setNodeOrientation(NodeOrientation.RIGHT_TO_LEFT);
Note that both the Text
and TextFlow
objects support bidirectional reordering as defined by the Unicode Consortium in the Bidirectional Algorithm Annex #9 at
This chapter describes the ColorPicker
control, provides its design overview, and explains how to use it in your JavaFX applications.
The color picker control in the JavaFX SDK is a typical user interface component that enables users to select a particular color from the available range, or set an additional color by specifying an RGB or HSB combination.
The ColorPicker
control consists of the color chooser, color palette, and custom color dialog window. Figure 25-1 shows these elements.
The color chooser element of the color picker is the combo box with the enabled color indicator and the corresponding label shown at the top of Figure 25-1. The color indicator presents the currently selected color.
The implementation of the color picker control allows three looks of the color chooser element: button, split-menu-button, and combo box shown in Figure 25-2.
The button look provides a typical button control with the color indicator and the label. In the split-menu-button look, the button part of the control is separated from the drop-down menu. The combo-box look is the default appearance of the color chooser. It also has a drop-down menu but it is not separated from the button part.
To apply one of the looks, use the corresponding CSS classes. For more information about how to alter the appearance of a color picker, see Changing the Appearance of a Color Picker.
The color palette contains the predefined set of colors and the Custom Color link that points to the Custom Color dialog window. The initial appearance of the color palette is shown in Figure 25-3.
If a custom color is already defined, this color is displayed in the Custom Color area in the color palette, as shown in Figure 25-4.
The color palette supports navigation by using the Up, Down, Left and Right keys.
The set of custom colors cannot be reloaded when the application starts again, unless saved within the application. Each color selected in the palette or in the custom color area is displayed in the color indicator of the color chooser. The color chooser label renders the corresponding hexadecimal web color value.
The Custom Color dialog window is a modal window that can be opened by clicking the corresponding link in the color palette. When the Custom Color window opens, it displays values of the color that is currently shown in the color indicator of the color chooser. Users can define a new color by moving the mouse cursor over the color area or over the vertical color bar, shown in Figure 25-5. Note that any time a user manipulates with the circle in the color area or with the rectangle in the color bar, the color value is automatically assigned to the corresponding property of the ColorPicker
control.
Another way to define a new color is to set the HSB (Hue Saturation Brightness) or RGB (Red Green Blue) values, or explicitly enter the web color value in the corresponding field. Figure 25-6 shows three panes for the custom color settings.
Users can also set the transparency of the custom color by moving the Opacity slider or typing the value from 0 to 100.
When all the settings are done and the new color is specified, users can click Use to apply it, or Save to save the color to the custom color area.
Use the ColorPicker
class of the JavaFX SDK to build a color picker in your JavaFX application. You can add a color picker directly to the application scene, to a layout container, or to the application toolbar. Example 25-1 shows three ways to declare a ColorPicker
object.
Example 25-1 Creating a Color Picker Control
//Empty contructor, the control appears with the default color, which is WHITE ColorPicker colorPicker1 = new ColorPicker(); //Color constant set as the currently selected color ColorPicker colorPicker2 = new ColorPicker(Color.BLUE); //Web color value set as the currently selected color ColorPicker colorPicker3 = new ColorPicker(Color.web("#ffcce6"));
Try the sample shown in Example 25-2 to evaluate the ColorPicker
control in action.
Example 25-2 Using the ColorPicker Control to Alter the Color of the Text Component
import javafx.application.Application; import javafx.event.*; import javafx.scene.Scene; import javafx.scene.control.ColorPicker; import javafx.geometry.Insets; import javafx.scene.layout.HBox; import javafx.scene.paint.Color; import javafx.scene.text.*; import javafx.stage.Stage; public class ColorPickerSample extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) { stage.setTitle("ColorPicker"); Scene scene = new Scene(new HBox(20), 400, 100); HBox box = (HBox) scene.getRoot(); box.setPadding(new Insets(5, 5, 5, 5)); final ColorPicker colorPicker = new ColorPicker(); colorPicker.setValue(Color.CORAL); final Text text = new Text("Try the color picker!"); text.setFont(Font.font ("Verdana", 20)); text.setFill(colorPicker.getValue()); colorPicker.setOnAction((ActionEvent t) -> { text.setFill(colorPicker.getValue()); }); box.getChildren().addAll(colorPicker, text); stage.setScene(scene); stage.show(); } }
This example creates a color picker and defines its behavior when the color is changed. The Color
value obtained through the getValue
method of the ColorPicker
class is passed to the setFill
method of the Text
object. This is how the selected color is applied to the "Try the color picker!" text.
When you compile and run this sample, it produces the output shown in Figure 25-7. The figure captures the moment when a newly created custom color is applied to the Text
component.
Similarly, you can apply the selected color to the graphical Node
. Example 25-3 shows how to use a color picker to model a T-shirt.
Example 25-3 Using ColorPicker to Alter the Color of a Graphical Object
import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.ColorPicker; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.event.ActionEvent; import javafx.scene.control.ComboBox; import javafx.scene.control.ToolBar; import javafx.scene.effect.DropShadow; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.scene.shape.SVGPath; import javafx.stage.Stage; public class ColorPickerSample extends Application { ImageView logo = new ImageView( new Image(getClass().getResourceAsStream("OracleLogo.png")) ); public static void main(String[] args) { launch(args); } @Override public void start(Stage stage) { stage.setTitle("ColorPicker"); Scene scene = new Scene(new VBox(20), 300, 300); VBox box = (VBox) scene.getRoot(); ToolBar tb = new ToolBar(); box.getChildren().add(tb); final ComboBox logoSamples = new ComboBox(); logoSamples.getItems().addAll( "Oracle", "Java", "JavaFX", "Cup"); logoSamples.setValue("Oracle"); logoSamples.valueProperty().addListener(new ChangeListener<String>() { @Override public void changed(ObservableValue ov, String t, String t1) { logo.setImage( new Image(getClass().getResourceAsStream(t1+"Logo.png")) ); } }); final ColorPicker colorPicker = new ColorPicker(); tb.getItems().addAll(logoSamples, colorPicker); StackPane stack = new StackPane(); box.getChildren().add(stack); final SVGPath svg = new SVGPath(); svg.setContent("M70,50 L90,50 L120,90 L150,50 L170,50" + "L210,90 L180,120 L170,110 L170,200 L70,200 L70,110 L60,120 L30,90" + "L70,50"); svg.setStroke(Color.DARKGREY); svg.setStrokeWidth(2); svg.setEffect(new DropShadow()); svg.setFill(colorPicker.getValue()); stack.getChildren().addAll(svg, logo); colorPicker.setOnAction((ActionEvent t) -> { svg.setFill(colorPicker.getValue()); }); stage.setScene(scene); stage.show(); } }
In this example, the color selected in the color picker and obtained through the getValue
method is applied to the SVGPath
object. This sample produces the output shown in Figure 25-8
When you work with a color picker, you can obtain the created custom colors by calling the getCustomColors()
method. It returns an ObservableList
of the Color
objects corresponding to the created colors. You cannot upload them to a color picker on the application start. However, you can set one of the custom colors as a ColorPicker
value (shown in Example 25-4).
The default appearance of the color picker control is defined by the com.sun.javafx.scene.control.skin.ColorPickerSkin
class. To apply an alternative skin to the color pickers in your JavaFX application, redefine the -fx-skin
property of the color-picker
CSS class as shown in Example 25-5.
Example 25-5 Setting an Alternative Skin for a Color Picker
.color-picker { -fx-skin: "CustomSkin"; }
Use the split-button
and arrow-button
CSS classes to change the appearance of a color picker control in the JavaFX code, as shown in Example 25-6.
Example 25-6 Setting Appearance for a Color Picker
//Sets the split-menu-button colorPicker.getStyleClass().add("split-button"); //Sets the button colorPicker.getStyleClass().add("button");
You can also modify the default style of a color picker and customize its elements with various CSS classes defined in the modena
style sheet. To view this file, go to the \rt\lib\ext
directory under the directory in which the JavaFX SDK is installed. Use the following command to extract the style sheet from the JAR file: jar -xf jfxrt.jar com/sun/javafx/scene/control/skin/modena/modena.css
. See Skinning JavaFX Applications with CSS for more information about CSS classes and properties. Example 25-7 shows how to alter the default background and the label of a color picker.
Example 25-7 Modifying the Default Appearance of a Color Picker
.color-picker { -fx-background-color: #669999; -fx-background-radius: 0 15 15 0; } .color-picker .color-picker-label .text { -fx-fill: #ccffcc; }
Add these styles to the ControlStyle.css
file and enable the style sheets in the JavaFX application by using the following code line: scene.getStylesheets().add("colorpickersample/ControlStyle.css");
, then compile and the run the ColorPickerSample. The color picker should change its appearance as shown in Figure 25-9.
Note that the ColorPicker
class is an extension of the ComboBoxBase
class and inherits its CSS properties. You can define new styles for the combo-box-base
CSS style to unify the combo box and the color picker in the ColorPickerSample application. Replace the styles in the ControlStyle.css file with the styles shown in Example 25-8.
Example 25-8 Setting the Combo-Box-Base Styles
.tool-bar:horizontal { -fx-background-color: #b3e6b3; } .combo-box-base { -fx-background-color: null; } .combo-box-base:hover { -fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.6) , 8, 0.0 , 0 , 0 ); }
When you compile and run the ColorPickerSample application with the applied styles, the combo box and the color picker look as shown in Figure 25-10.
Related API Documentation
The following tables list the demo applications in this document with their associated source code files.
UI Control Samples
Chapter | Source File | NetBeans Project File |
---|---|---|
Label |
| |
Button |
| |
Radio Button |
| |
Toggle Button |
| |
Checkbox |
| |
Choice Box |
| |
Text Field |
| |
Password Field |
| |
Scrollbar |
| |
Scroll Pane |
| |
List View |
| |
Table View |
| |
Tree View |
| |
Tree Table View |
| |
Combo Box |
| |
Separator |
| |
Slider |
| |
Progress Bar, Progress Indicator |
| |
Hyperlink |
| |
Hyperlink |
| |
HTML Editor |
| |
Tooltip |
| |
Accordion and Titled Pane |
| |
Menu Controls |
| |
Color Picker |
| |
Date Picker |
| |
Pagination |
| |
File Chooser |
|
Chart Samples
Chapter | Source File | NetBeans Project Files |
---|---|---|
Pie Chart |
| |
Line Chart |
| |
Area Chart |
| |
Bubble Chart |
| |
Scatter Chart |
| |
Bar Chart |
|
CSS Samples
Chapter | Source Files | NetBeans Project Files |
---|---|---|
Styling UI Controls with CSS |
| |
Styling UI Controls with CSS |
| |
Styling UI Controls with CSS |
| |
Styling UI Controls with CSS |
| |
Styling UI Controls with CSS |
| |
Styling UI Controls with CSS |
| |
Styling UI Controls with CSS |
|
Text Samples
Chapter | Source File | NetBeans Project Files |
---|---|---|
Applying Effects to Text |
| |
Applying Effects to Text |
|
In this chapter, you learn how to edit text in your JavaFX applications by using the embedded HTML editor.
The HTMLEditor
control is a full functional rich text editor. Its implementation is based on the document editing feature of HTML5 and includes the following editing functions:
Text formatting including bold, italic, underline, and strike though styles
Paragraph settings such as format, font family, and font size
Foreground and background colors
Text indent
Bulleted and numbered lists
Text alignment
Adding a horizontal rule
Copying and pasting text fragments
Figure 21-1 shows a rich text editor added to a JavaFX application.
The HTMLEditor
class presents the editing content in the form of an HTML string. For example, the content typed in the editor in Figure 21-1 is presented by the following string: "<html><head></head><body contenteditable="true"><h1>Heading</h1><div><u>Text</u>, some text</div></body></html>
."
Because the HTMLEditor
class is an extension of the Node
class, you can apply visual effects or transformations to its instances.
Like any other UI control, the HTMLEditor
component must be added the scene so that it can appear in your application. You can add it directly to the scene as shown in Example 21-1 or through a layout container as done in other examples.
Example 21-1 Adding an HTML Editor to a JavaFX Application
import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.web.HTMLEditor; import javafx.stage.Stage; public class HTMLEditorSample extends Application { @Override public void start(Stage stage) { stage.setTitle("HTMLEditor Sample"); stage.setWidth(650650); stage.setHeight(300); final HTMLEditor htmlEditor = new HTMLEditor(); htmlEditor.setPrefHeight(245); Scene scene = new Scene(htmlEditor); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(args); } }
Compiling and running this code fragment produces the window shown in Figure 21-2.
The HTMLEditor
class provides a method to define the content that appears in the editing area when the application starts. Use the setHtmlText
method, as shown in Example 21-2 to set the initial text for the editor.
Example 21-2 Setting the Text Content
private final String INITIAL_TEXT = "<html><body>Lorem ipsum dolor sit " + "amet, consectetur adipiscing elit. Nam tortor felis, pulvinar " + "in scelerisque cursus, pulvinar at ante. Nulla consequat" + "congue lectus in sodales. Nullam eu est a felis ornare " + "bibendum et nec tellus. Vivamus non metus tempus augue auctor " + "ornare. Duis pulvinar justo ac purus adipiscing pulvinar. " + "Integer congue faucibus dapibus. Integer id nisl ut elit " + "aliquam sagittis gravida eu dolor. Etiam sit amet ipsum " + "sem.</body></html>"; htmlEditor.setHtmlText(INITIAL_TEXT);
Figure 21-3 demonstrates the text editor with the text set by using the setHTMLText
method.
You can use the HTML tags in this string to apply specific formatting for the initially rendered content.
You can use the HTMLEditor
control to implement typical user interfaces (UIs) in your JavaFX applications. For example, you can implement instant messenger services, email clients, or even content management systems.
presents the user interface of a message composing window that you can find in many email client applications.
Example 21-3 HTMLEditor Added to the Email Client UI
import javafx.application.Application; import javafx.collections.FXCollections; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.layout.GridPane; import javafx.scene.layout.VBox; import javafx.scene.web.HTMLEditor; import javafx.stage.Stage; public class HTMLEditorSample extends Application { @Override public void start(Stage stage) { stage.setTitle("Message Composing"); stage.setWidth(650); stage.setHeight(500); Scene scene = new Scene(new Group()); final VBox root = new VBox(); root.setPadding(new Insets(8, 8, 8, 8)); root.setSpacing(5); root.setAlignment(Pos.BOTTOM_LEFT); final GridPane grid = new GridPane(); grid.setVgap(5); grid.setHgap(10); final ChoiceBox sendTo = new ChoiceBox(FXCollections.observableArrayList( "To:", "Cc:", "Bcc:") ); sendTo.setPrefWidth(100); GridPane.setConstraints(sendTo, 0, 0); grid.getChildren().add(sendTo); final TextField tbTo = new TextField(); tbTo.setPrefWidth(400); GridPane.setConstraints(tbTo, 1, 0); grid.getChildren().add(tbTo); final Label subjectLabel = new Label("Subject:"); GridPane.setConstraints(subjectLabel, 0, 1); grid.getChildren().add(subjectLabel); final TextField tbSubject = new TextField(); tbTo.setPrefWidth(400); GridPane.setConstraints(tbSubject, 1, 1); grid.getChildren().add(tbSubject); root.getChildren().add(grid); final HTMLEditor htmlEditor = new HTMLEditor(); htmlEditor.setPrefHeight(370); root.getChildren().addAll(htmlEditor, new Button("Send")); final Label htmlLabel = new Label(); htmlLabel.setWrapText(true); scene.setRoot(root); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(args); } }
The user interface includes a choice box to select a type of recipient, two text fields to enter the email address and the subject of the message, a label to indicate the subject field, the editor, and the Send button.
The UI controls are arranged on the application scene by using the Grid
and VBox
layout containers. When you compile and run this application, the window shown in Figure 21-4 shows the output of this application when a user is composing a weekly report.
You can set the specific width and height values for the HTMLEditor
object by calling the setPrefWidth
or setPrefHeight
methods, or you can leave its width and height unspecified. Example 21-3 specifies the height of the component. Its width is defined by the VBox
layout container. When the text content exceeds the height of the editing area, the vertical scroll bar appears.
With the JavaFX HTMLEditor
control, you can edit text and set the initial content. In addition, you can obtain the entered and edited text in HTML format. The application shown in Example 21-4 implements this task.
Example 21-4 Retrieving HTML Code
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.scene.web.HTMLEditor;
import javafx.stage.Stage;
public class HTMLEditorSample extends Application {
private final String INITIAL_TEXT = "Lorem ipsum dolor sit "
+ "amet, consectetur adipiscing elit. Nam tortor felis, pulvinar "
+ "in scelerisque cursus, pulvinar at ante. Nulla consequat"
+ "congue lectus in sodales. Nullam eu est a felis ornare "
+ "bibendum et nec tellus. Vivamus non metus tempus augue auctor "
+ "ornare. Duis pulvinar justo ac purus adipiscing pulvinar. "
+ "Integer congue faucibus dapibus. Integer id nisl ut elit "
+ "aliquam sagittis gravida eu dolor. Etiam sit amet ipsum "
+ "sem.";
@Override
public void start(Stage stage) {
stage.setTitle("HTMLEditor Sample");
stage.setWidth(650);
stage.setHeight(500);
Scene scene = new Scene(new Group());
VBox root = new VBox();
root.setPadding(new Insets(8, 8, 8, 8));
root.setSpacing(5);
root.setAlignment(Pos.BOTTOM_LEFT);
final HTMLEditor htmlEditor = new HTMLEditor();
htmlEditor.setPrefHeight(245);
htmlEditor.setHtmlText(INITIAL_TEXT);
final TextArea htmlCode = new TextArea();
htmlCode.setWrapText(true);
ScrollPane scrollPane = new ScrollPane();
scrollPane.getStyleClass().add("noborder-scroll-pane");
scrollPane.setContent(htmlCode);
scrollPane.setFitToWidth(true);
scrollPane.setPrefHeight(180);
Button showHTMLButton = new Button("Produce HTML Code");
root.setAlignment(Pos.CENTER);
showHTMLButton.setOnAction((ActionEvent arg0) -> {
htmlCode.setText(htmlEditor.getHtmlText());
});
root.getChildren().addAll(htmlEditor, showHTMLButton, scrollPane);
scene.setRoot(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The getHTMLText
method called on the HTMLEditor
object derives the edited content and presents it as an HTML string. This information is passed to the text area, so that you can observe, copy, and paste the produced HTML code. Figure 21-5 shows an HTML code of the text is being edited in the HTMLEditor sample.
Similarly, you can obtain HTML code and save it in the file or send it to the WebView
object to synchronize content in the editor and the embedded browser. See how this task is implemented in Example 21-5.
Example 21-5 Rendering Edited HTML Content in a Browser
import javafx.application.Application; import javafx.event.ActionEvent; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.layout.VBox; import javafx.scene.web.HTMLEditor; import javafx.scene.web.WebEngine; import javafx.scene.web.WebView; import javafx.stage.Stage; public class HTMLEditorSample extends Application { private final String INITIAL_TEXT = "Lorem ipsum dolor sit " + "amet, consectetur adipiscing elit. Nam tortor felis, pulvinar " + "in scelerisque cursus, pulvinar at ante. Nulla consequat" + "congue lectus in sodales. Nullam eu est a felis ornare " + "bibendum et nec tellus. Vivamus non metus tempus augue auctor " + "ornare. Duis pulvinar justo ac purus adipiscing pulvinar. " + "Integer congue faucibus dapibus. Integer id nisl ut elit " + "aliquam sagittis gravida eu dolor. Etiam sit amet ipsum " + "sem."; @Override public void start(Stage stage) { stage.setTitle("HTMLEditor Sample"); stage.setWidth(650); stage.setHeight(500); Scene scene = new Scene(new Group()); VBox root = new VBox(); root.setPadding(new Insets(8, 8, 8, 8)); root.setSpacing(5); root.setAlignment(Pos.BOTTOM_LEFT); final HTMLEditor htmlEditor = new HTMLEditor(); htmlEditor.setPrefHeight(245); htmlEditor.setHtmlText(INITIAL_TEXT); final WebView browser = new WebView(); final WebEngine webEngine = browser.getEngine(); ScrollPane scrollPane = new ScrollPane(); scrollPane.getStyleClass().add("noborder-scroll-pane"); scrollPane.setStyle("-fx-background-color: white"); scrollPane.setContent(browser); scrollPane.setFitToWidth(true); scrollPane.setPrefHeight(180); Button showHTMLButton = new Button("Load Content in Browser"); root.setAlignment(Pos.CENTER); showHTMLButton.setOnAction((ActionEvent arg0) -> { webEngine.loadContent(htmlEditor.getHtmlText()); }); root.getChildren().addAll(htmlEditor, showHTMLButton, scrollPane); scene.setRoot(root); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(args); } }
HTML code received from the htmlEditor
component is loaded in the WebEngine
object that specifies the content for the embedded browser. Each time a user clicks the Load Content in Browser button, the edited content is updated in the browser. Figure 21-6 demonstrates Example 21-5 in action.
You can use the Text
component to add non-editing text content to your UI. See Using Text in JavaFX for more information about the Text
component.
Related API Documentation
This chapter explains how to use the Label
class that resides in the javafx.scene.control
package of the JavaFX API to display a text element. Learn how to wrap a text element to fit the specific space, add a graphical image, or apply visual effects.
Figure 2-1 shows three common label usages. The label at the left is a text element with an image, the label in the center represents rotated text, and the label at the right renders wrapped text.
The JavaFX API provides three constructors of the Label
class for creating labels in your application, as shown in Example 2-1.
Example 2-1 Creating Labels
//An empty label Label label1 = new Label(); //A label with the text element Label label2 = new Label("Search"); //A label with the text element and graphical icon Image image = new Image(getClass().getResourceAsStream("labels.jpg")); Label label3 = new Label("Search", new ImageView(image));
Once you have created a label in your code, you can add textual and graphical content to it by using the following methods of the Labeled
class.
The setText(String text)
method – specifies the text caption for the label
setGraphic(Node graphic)
– specifies the graphical icon
The setTextFill
method specifies the color to paint the text element of the label. Study Example 2-2. It creates a text label, adds an icon to it, and specifies a fill color for the text.
Example 2-2 Adding an Icon and Text Fill to a Label
Label label1 = new Label("Search"); Image image = new Image(getClass().getResourceAsStream("labels.jpg")); label1.setGraphic(new ImageView(image)); label1.setTextFill(Color.web("#0076a3"));
When this code fragment is added to the application, it produces the label shown in Figure 2-2.
When defining both text and graphical content for your button, you can use the setGraphicTextGap
method to set the gap between them.
Additionally, you can vary the position of the label content within its layout area by using the setTextAlignment
method. You can also define the position of the graphic relative to the text by applying the setContentDisplay
method and specifying one of the following ContentDisplay
constant: LFFT
, RIGHT
, CENTER
, TOP
, BOTTOM
.
Compare the Search labels in Figure 2-1 and Figure 2-2. Notice that the label in Figure 2-1 has a larger font size. This is because the code fragment shown in Example 2-2 does not specify any font settings for the label. It is rendered with the default font size.
To provide a font text size other than the default for your label use the setFont
method of the Labeled
class. The code fragment in Example 2-3 sets the size of the label1 text to 30 points and the font name to Arial. For label2 sets the text size to 32 points and the font name to Cambria.
When you create a label, sometimes you must fit it within a space that is smaller than you need to render. To break up (wrap) the text so that is can fit into the layout area, set the true
value for the setWrapText
method, as shown in Example 2-4.
Example 2-4 Enable Text Wrapping
Label label3 = new Label("A label that needs to be wrapped"); label3.setWrapText(true);
When label3 is added to the content of an application, it is rendered as shown in Figure 2-3.
Suppose that the layout area of the label is limited not only by its width, but also by its height. You can specify the behavior of a label when it is impossible to render the entire required text string. Use the setTextOverrun
method of the Labeled
class and one of the available OverrunStyle
types to define how to process the part of the text string that cannot be rendered properly. See the API documentation for more information about OverrunStyle
types.
Although a label is static content and cannot be edited, you can apply visual effects or transformations to it. The code fragment in Example 2-5 rotates label2 270 degrees and translates its position vertically.
Example 2-5 Rotating a Label
Label label2 = new Label ("Values"); label2.setFont(new Font("Cambria", 32)); label2.setRotate(270); label2.setTranslateY(50);
Rotation and translation are typical transformations available in the JavaFX API. Additionally, you can set up an effect that zooms (magnifies) the label when a user hovers the mouse cursor over it.
The code fragment shown in Example 2-6 applies the zoom effect to label3. When the MOUSE_ENTERED
event is fired on the label, the scaling factor of 1.5 is set for the setScaleX
and setScaleY
methods. When a user moves the mouse cursor off of the label and the MOUSE_EXITED
event occurs, the scale factor is set to 1.0, and the label is rendered in its original size.
Example 2-6 Applying the Zoom Effect
label3.setOnMouseEntered((MouseEvent e) -> { label3.setScaleX(1.5); label3.setScaleY(1.5); }); label3.setOnMouseExited((MouseEvent e) -> { label3.setScaleX(1); label3.setScaleY(1); });
Figure 2-4 shows the two states of label3.
Related API Documentation
This appendix lists code for the following JavaFX samples:
For the descriptions of the source files, see Applying Effects to Text.
/* * Copyright (c) 2011, 2014, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the distribution. * - Neither the name of Oracle nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package texteffects; import javafx.application.Application; import javafx.collections.ObservableList; import javafx.geometry.VPos; import javafx.scene.Group; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.effect.*; import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; import javafx.scene.text.Font; import javafx.scene.text.FontWeight; import javafx.scene.text.Text; import javafx.stage.Stage; public class TextEffects extends Application { Stage stage; Scene scene; @Override public void start(Stage stage) { stage.show(); scene = new Scene(new Group(), 900, 600); ObservableList content = ((Group)scene.getRoot()).getChildren(); /// Perspective content.add(perspective()); /// DropShadow content.add(dropShadow()); /// Bloom content.add(bloom()); /// BoxBlur content.add(boxBlur()); /// DisplacementMap content.add(displacementMap()); /// InnerShadow content.add(innerShadow()); /// Lighting content.add(lighting()); /// MotionBlur content.add(motionBlur()); /// Reflection content.add(reflection()); /// GaussianBlur content.add(gaussianBlur()); /// DistantLight content.add(distantLight()); stage.setScene(scene); stage.setTitle("Text Effects"); } static Node distantLight() { Light.Distant light = new Light.Distant(); light.setAzimuth(-135.0f); light.setElevation(30.0f); Lighting l = new Lighting(); l.setLight(light); l.setSurfaceScale(5.0f); final Text t = new Text(); t.setText("DistantLight"); t.setFill(Color.RED); t.setFont(Font.font(null, FontWeight.BOLD, 70)); t.setX(10.0f); t.setY(10.0f); t.setTextOrigin(VPos.TOP); t.setEffect(l); final Rectangle r = new Rectangle(); r.setFill(Color.BLACK); Group g = new Group(); g.getChildren().add(r); g.getChildren().add(t); g.setTranslateY(470); return g; } static Node perspective() { Group g = new Group(); PerspectiveTransform pt = new PerspectiveTransform(); pt.setUlx(10.0f); pt.setUly(10.0f); pt.setUrx(310.0f); pt.setUry(40.0f); pt.setLrx(310.0f); pt.setLry(60.0f); pt.setLlx(10.0f); pt.setLly(90.0f); g.setEffect(pt); g.setCache(true); Rectangle r = new Rectangle(); r.setX(10.0f); r.setY(10.0f); r.setWidth(280.0f); r.setHeight(80.0f); r.setFill(Color.BLUE); Text t = new Text(); t.setX(20.0f); t.setY(65.0f); t.setText("Perspective"); t.setFill(Color.YELLOW); t.setFont(Font.font(null, FontWeight.BOLD, 36)); g.getChildren().add(r); g.getChildren().add(t); return g; } static Node gaussianBlur() { Text t2 = new Text(); t2.setX(10.0f); t2.setY(140.0f); t2.setCache(true); t2.setText("Blurry Text"); t2.setFill(Color.RED); t2.setFont(Font.font(null, FontWeight.BOLD, 36)); t2.setEffect(new GaussianBlur()); return t2; } static Node reflection() { Text t = new Text(); t.setX(10.0f); t.setY(50.0f); t.setCache(true); t.setText("Reflections on JavaFX..."); t.setFill(Color.RED); t.setFont(Font.font(null, FontWeight.BOLD, 30)); Reflection r = new Reflection(); r.setFraction(0.7f); t.setEffect(r); t.setTranslateY(400); return t; } static Node motionBlur() { Text t = new Text(); t.setX(100.0f); t.setY(100.0f); t.setText("Motion"); t.setFill(Color.RED); t.setFont(Font.font(null, FontWeight.BOLD, 60)); MotionBlur mb = new MotionBlur(); mb.setRadius(15.0f); mb.setAngle(-30.0f); t.setEffect(mb); t.setTranslateX(300); t.setTranslateY(150); return t; } static Node lighting() { Light.Distant light = new Light.Distant(); light.setAzimuth(-135.0f); Lighting l = new Lighting(); l.setLight(light); l.setSurfaceScale(5.0f); Text t = new Text(); t.setText("Lighting!"); t.setFill(Color.RED); t.setFont(Font.font(null, FontWeight.BOLD, 70)); t.setX(10.0f); t.setY(10.0f); t.setTextOrigin(VPos.TOP); t.setEffect(l); t.setTranslateX(0); t.setTranslateY(320); return t; } static Node innerShadow() { InnerShadow is = new InnerShadow(); is.setOffsetX(4.0f); is.setOffsetY(4.0f); Text t = new Text(); t.setEffect(is); t.setX(20); t.setY(100); t.setText("InnerShadow"); t.setFill(Color.YELLOW); t.setFont(Font.font(null, FontWeight.BOLD, 80)); t.setTranslateX(350); t.setTranslateY(300); return t; } static Node displacementMap() { int w = 220; int h = 100; FloatMap map = new FloatMap(); map.setWidth(w); map.setHeight(h); for (int i = 0; i < w; i++) { double v = (Math.sin(i / 50.0 * Math.PI) - 0.5) / 40.0; for (int j = 0; j < h; j++) { map.setSamples(i, j, 0.0f, (float) v); } } Group g = new Group(); DisplacementMap dm = new DisplacementMap(); dm.setMapData(map); Rectangle r = new Rectangle(); r.setX(80.0f); r.setY(40.0f); r.setWidth(w); r.setHeight(h); r.setFill(Color.BLUE); g.getChildren().add(r); Text t = new Text(); t.setX(100.0f); t.setY(80.0f); t.setText("Wavy Text"); t.setFill(Color.YELLOW); t.setFont(Font.font(null, FontWeight.BOLD, 36)); g.getChildren().add(t); g.setEffect(dm); g.setCache(true); g.setTranslateX(300); g.setTranslateY(180); return g; } static Node boxBlur() { Text t = new Text(); t.setText("Blurry Text!"); t.setFill(Color.RED); t.setFont(Font.font(null, FontWeight.BOLD, 36)); t.setX(10); t.setY(40); BoxBlur bb = new BoxBlur(); bb.setWidth(15); bb.setHeight(15); bb.setIterations(3); t.setEffect(bb); t.setTranslateX(350); t.setTranslateY(100); return t; } static Node dropShadow() { DropShadow ds = new DropShadow(); ds.setOffsetY(3.0f); ds.setColor(Color.color(0.4f, 0.4f, 0.4f)); Text t = new Text(); t.setEffect(ds); t.setCache(true); t.setX(10.0f); t.setY(270.0f); t.setFill(Color.RED); t.setText("JavaFX drop shadow..."); t.setFont(Font.font(null, FontWeight.BOLD, 32)); return t; } static Node bloom() { Group g = new Group(); Rectangle r = new Rectangle(); r.setX(10); r.setY(10); r.setWidth(160); r.setHeight(80); r.setFill(Color.DARKBLUE); Text t = new Text(); t.setText("Bloom!"); t.setFill(Color.YELLOW); t.setFont(Font.font(null, FontWeight.BOLD, 36)); t.setX(25); t.setY(65); g.setCache(true); g.setEffect(new Bloom()); g.getChildren().add(r); g.getChildren().add(t); g.setTranslateX(350); return g; } /** * @param args the command line arguments */ public static void main(String[] args) { Application.launch (args); } }
/* * Copyright (c) 2011, 2014, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the distribution. * - Neither the name of Oracle nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package neonsign; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.TextField; import javafx.scene.effect.Blend; import javafx.scene.effect.BlendMode; import javafx.scene.effect.DropShadow; import javafx.scene.effect.InnerShadow; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; import javafx.scene.text.Text; import javafx.stage.Stage; public class NeonSign extends Application { /** * @param args the command line arguments */ public static void main(String[] args) { Application.launch(args); } @Override public void start(Stage primaryStage) { primaryStage.setTitle("Text Effects: Neon Sign"); double width = 600; double height = 300; Pane root = new Pane(); Scene scene = new Scene(root, width, height); Rectangle rect = new Rectangle (width, height); rect.getStyleClass().add("myrect"); Text text = new Text(); text.setId("fancytext"); text.setX(20); text.setY(150); Blend blend = new Blend(); blend.setMode(BlendMode.MULTIPLY); DropShadow ds = new DropShadow(); ds.setColor(Color.rgb(254, 235, 66, 0.3)); ds.setOffsetX(5); ds.setOffsetY(5); ds.setRadius(5); ds.setSpread(0.2); blend.setBottomInput(ds); DropShadow ds1 = new DropShadow(); ds1.setColor(Color.web("#f13a00")); ds1.setRadius(20); ds1.setSpread(0.2); Blend blend2 = new Blend(); blend2.setMode(BlendMode.MULTIPLY); InnerShadow is = new InnerShadow(); is.setColor(Color.web("#feeb42")); is.setRadius(9); is.setChoke(0.8); blend2.setBottomInput(is); InnerShadow is1 = new InnerShadow(); is1.setColor(Color.web("#f13a00")); is1.setRadius(5); is1.setChoke(0.4); blend2.setTopInput(is1); Blend blend1 = new Blend(); blend1.setMode(BlendMode.MULTIPLY); blend1.setBottomInput(ds1); blend1.setTopInput(blend2); blend.setTopInput(blend1); text.setEffect(blend); TextField textField = new TextField(); textField.setText("Neon Sign"); text.textProperty().bind(textField.textProperty()); textField.setPrefColumnCount(40); textField.setLayoutX(50); textField.setLayoutY(260); root.getChildren().addAll(rect,text,textField); primaryStage.setScene(scene); scene.getStylesheets().add(NeonSign.class.getResource("brickStyle.css").toExternalForm()); primaryStage.show(); } }
This preface describes the document accessibility features and conventions used in this tutorial - Working with JavaFX UI Components.
This document is intended for JavaFX developers.
For information about Oracle's commitment to accessibility, visit the Oracle Accessibility Program website at http://www.oracle.com/pls/topic/lookup?ctx=acc&id=docacc
.
Access to Oracle Support
Oracle customers have access to electronic support through My Oracle Support. For information, visit http://www.oracle.com/pls/topic/lookup?ctx=acc&id=info
or visit http://www.oracle.com/pls/topic/lookup?ctx=acc&id=trs
if you are hearing impaired.
For more information, see the following documents in the JavaFX documentation set:
The following text conventions are used in this document:
Convention | Meaning |
---|---|
boldface |
Boldface type indicates graphical user interface elements associated with an action, or terms defined in text or the glossary. |
italic |
Italic type indicates book titles, emphasis, or placeholder variables for which you supply particular values. |
|
Monospace type indicates commands within a paragraph, URLs, code in examples, text that appears on the screen, or text that you enter. |
This chapter explains how to use separator to organize UI components of you JavaFX applications.
The Separator
class that is available in the JavaFX API represents a horizontal or vertical separator line. It serves to divide elements of the application user interface and does not produce any action. However, you can style it, apply visual effects to it, or even animate it. By default, the separator is horizontal. You can change its orientation by using the setOrientation
method.
The code fragment in Example 17-1 creates one horizontal separator and one vertical separator.
Example 17-1 Vertical and Horizontal Separators
//Horizontal separator Separator separator1 = new Separator(); //Vertical separator Separator separator2 = new Separator(); separator2.setOrientation(Orientation.VERTICAL);
The Separator
class is an extension of the Node
class. Therefore, the separator inherits all the instance variables of the Node
class.
Typically, separators are used to divide groups of the UI controls. Study the code fragment shown in Example 17-2. It separates the spring month checkboxes from the summer month checkboxes.
Example 17-2 Using a Separator Between Checkbox Categories
final String[] names = new String[]{"March", "April", "May", "June", "July", "August"}; final CheckBox[] cbs = new CheckBox[names.length]; final Separator separator = new Separator(); final VBox vbox = new VBox(); for (int i = 0; i < names.length; i++) { cbs[i] = new CheckBox(names[i]); } separator.setMaxWidth(40); separator.setHalignment(HPos.LEFT); vbox.getChildren().addAll(cbs); vbox.setSpacing(5); vbox.getChildren().add(3, separator);
When this code fragment is added to an application, it produces the controls shown in Figure 17-1.
A separator occupies the full horizontal or vertical space allocated to it. The setMaxWidth
method is applied to define a particular width. The setValignment
method specifies the vertical position of the separator within the allocated layout space. Similarly, you can set the horizontal position of the separator line by applying the setHalignment
method.
In Example 17-2, the separator is added to the vertical box by using a dedicated method add(index, node)
. You can use this approach in your application to include separators after the UI is created or when the UI is dynamically changed.
As previously mentioned, separators can be used to divide groups of UI controls. You can also use them to structure a user interface. Consider the task of rendering the weather forecast data shown in Figure 17-2.
For the application shown in Figure 17-2, separators are used to divide Label
and ImageView
objects. Study the source code of this application shown in Example 17-3.
Example 17-3 Using Separators in a Weather Forecast Application
import javafx.application.Application; import javafx.geometry.Insets; import javafx.geometry.Orientation; import javafx.geometry.VPos; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.GridPane; import javafx.scene.text.Font; import javafx.stage.Stage; public class SeparatorSample extends Application { Label caption = new Label("Weather Forecast"); Label friday = new Label("Friday"); Label saturday = new Label("Saturday"); Label sunday = new Label("Sunday"); @Override public void start(Stage stage) { Group root = new Group(); Scene scene = new Scene(root, 350, 150); stage.setScene(scene); stage.setTitle("Separator Sample"); GridPane grid = new GridPane(); grid.setPadding(new Insets(10, 10, 10, 10)); grid.setVgap(2); grid.setHgap(5); scene.setRoot(grid); Image cloudImage = new Image(getClass().getResourceAsStream("cloud.jpg")); Image sunImage = new Image(getClass().getResourceAsStream("sun.jpg")); caption.setFont(Font.font("Verdana", 20)); GridPane.setConstraints(caption, 0, 0); GridPane.setColumnSpan(caption, 8); grid.getChildren().add(caption); final Separator sepHor = new Separator(); sepHor.setValignment(VPos.CENTER); GridPane.setConstraints(sepHor, 0, 1); GridPane.setColumnSpan(sepHor, 7); grid.getChildren().add(sepHor); friday.setFont(Font.font("Verdana", 18)); GridPane.setConstraints(friday, 0, 2); GridPane.setColumnSpan(friday, 2); grid.getChildren().add(friday); final Separator sepVert1 = new Separator(); sepVert1.setOrientation(Orientation.VERTICAL); sepVert1.setValignment(VPos.CENTER); sepVert1.setPrefHeight(80); GridPane.setConstraints(sepVert1, 2, 2); GridPane.setRowSpan(sepVert1, 2); grid.getChildren().add(sepVert1); saturday.setFont(Font.font("Verdana", 18)); GridPane.setConstraints(saturday, 3, 2); GridPane.setColumnSpan(saturday, 2); grid.getChildren().add(saturday); final Separator sepVert2 = new Separator(); sepVert2.setOrientation(Orientation.VERTICAL); sepVert2.setValignment(VPos.CENTER); sepVert2.setPrefHeight(80); GridPane.setConstraints(sepVert2, 5, 2); GridPane.setRowSpan(sepVert2, 2); grid.getChildren().add(sepVert2); sunday.setFont(Font.font("Verdana", 18)); GridPane.setConstraints(sunday, 6, 2); GridPane.setColumnSpan(sunday, 2); grid.getChildren().add(sunday); final ImageView cloud = new ImageView(cloudImage); GridPane.setConstraints(cloud, 0, 3); grid.getChildren().add(cloud); final Label t1 = new Label("16"); t1.setFont(Font.font("Verdana", 20)); GridPane.setConstraints(t1, 1, 3); grid.getChildren().add(t1); final ImageView sun1 = new ImageView(sunImage); GridPane.setConstraints(sun1, 3, 3); grid.getChildren().add(sun1); final Label t2 = new Label("18"); t2.setFont(Font.font("Verdana", 20)); GridPane.setConstraints(t2, 4, 3); grid.getChildren().add(t2); final ImageView sun2 = new ImageView(sunImage); GridPane.setConstraints(sun2, 6, 3); grid.getChildren().add(sun2); final Label t3 = new Label("20"); t3.setFont(Font.font("Verdana", 20)); GridPane.setConstraints(t3, 7, 3); grid.getChildren().add(t3); stage.show(); } public static void main(String[] args) { launch(args); } }
This application uses both horizontal and vertical separators and makes the separators span rows and columns in the GridPane
container. In your application, you can also set the preferred length for a separator (width for a horizontal separator and height for a vertical separator), so that it can change dynamically when the user interface resizes. You can also alter the visual appearance of a separator by applying the CSS classes available for Separator
objects.
To apply the same style to all the separators in Example 17-3, you create CSS file (for example, controlStyle.css) and save this file in the same package as the main class of your application. Example 17-4 demonstrates the CSS classes that you can add to the controlStyle file.
Example 17-4 Using CSS Classes to Style Separators
/*controlStyle.css */ .separator .line { -fx-border-color: #e79423; -fx-border-width: 2; }
You can enable the separator style in the application through the getStylesheets
method of the Scene
class, as shown in Example 17-5.
Example 17-5 Enabling Style Sheets in a JavaFX Application
scene.getStylesheets().add("separatorsample/controlStyle.css");
Figure 17-3 shows how the separators in the weather forecast look when the modified application is compiled and run.
Related API Documentation