Im trying to get an update on AreaChart everytime user chooses different item from choicebox, but theres something wrong and i dont know why. Here's the first picture when the first value is given as default(everything looks fine):
And here's what happens to the numbers columns(Y) when i change the combobox item:
Here's the code:
FXMLDocumentController.java
package javafxapplication17;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.chart.AreaChart;
import javafx.scene.chart.XYChart;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
public class FXMLDocumentController implements Initializable {
#FXML
private AreaChart fitimetChart;
#FXML
private ComboBox box;
#Override
public void initialize(URL url, ResourceBundle rb) {
fitimetChart.getXAxis().setAutoRanging(true);
fitimetChart.getYAxis().setAutoRanging(true);
box.getItems().addAll("Viti Paraprak","Nentor");
box.setValue("Viti Paraprak");
fitimetBox();
}
public void addFitimetNentorToChart()
{
double nje=10;
double dy=0;
double tre=20;
double kater=0;
double pes=2;
double gjashte=0;
double shtate=0;
double tete=10;
double nente=0;
double dhjete=0;
double njembedhjet=100;
double dymbedhjet=120;
double trembedhjet=40;
double katermbedhjet=40;
double pesembedhjet=80;
double gjashtembedhjet=87;
double shtatembedhjet=0;
double tetembedhjet=0;
double nentembedhjet=0;
double njezet=0;
double njezetenje=0;
double njezetedy=0;
double njezetetre=120;
double njezetekater=0;
double njezetepes=102;
double njezetegjashte=0;
double njezeteshtate=40;
double njezetetete=50;
double njezetenente=90;
double tridhjete=0;
XYChart.Series series = new XYChart.Series<>();
series.setName("Selami Hasani");
series.getData().add(new XYChart.Data("1", nje));
series.getData().add(new XYChart.Data("2", dy));
series.getData().add(new XYChart.Data("3", tre));
series.getData().add(new XYChart.Data("4", kater));
series.getData().add(new XYChart.Data("5", pes));
series.getData().add(new XYChart.Data("6", gjashte));
series.getData().add(new XYChart.Data("7", shtate));
series.getData().add(new XYChart.Data("8", tete));
series.getData().add(new XYChart.Data("9", nente));
series.getData().add(new XYChart.Data("10", dhjete));
series.getData().add(new XYChart.Data("11", njembedhjet));
series.getData().add(new XYChart.Data("12", dymbedhjet));
series.getData().add(new XYChart.Data("13", trembedhjet));
series.getData().add(new XYChart.Data("14", katermbedhjet));
series.getData().add(new XYChart.Data("15", pesembedhjet));
series.getData().add(new XYChart.Data("16", gjashtembedhjet));
series.getData().add(new XYChart.Data("17", shtatembedhjet));
series.getData().add(new XYChart.Data("18", tetembedhjet));
series.getData().add(new XYChart.Data("19", nentembedhjet));
series.getData().add(new XYChart.Data("20", njezet));
series.getData().add(new XYChart.Data("21", njezetenje));
series.getData().add(new XYChart.Data("22", njezetedy));
series.getData().add(new XYChart.Data("23", njezetetre));
series.getData().add(new XYChart.Data("24", njezetekater));
series.getData().add(new XYChart.Data("25", njezetepes));
series.getData().add(new XYChart.Data("26", njezetegjashte));
series.getData().add(new XYChart.Data("27", njezeteshtate));
series.getData().add(new XYChart.Data("28", njezetetete));
series.getData().add(new XYChart.Data("29", njezetenente));
series.getData().add(new XYChart.Data("30", tridhjete));
fitimetChart.getData().clear();
fitimetChart.getData().add(series);
fitimetChart.setTitle("Fitimet e Muajit Nentor");
}
public void addFitimetVjetoreToChart()
{
double janar=20;
double shkurt=30;
double mars=40;
double prill=0;
double maj=10;
double qershor=100;
double korrik=50;
double gusht=12;
double shtator=85;
double tetor=0;
double nentor=10;
double dhjetor=50;
XYChart.Series series = new XYChart.Series<>();
series.setName("Selami Hasani");
series.getData().add(new XYChart.Data("Janar", janar));
series.getData().add(new XYChart.Data("Shkurt", shkurt));
series.getData().add(new XYChart.Data("Mars", mars));
series.getData().add(new XYChart.Data("Prill", prill));
series.getData().add(new XYChart.Data("Maj", maj));
series.getData().add(new XYChart.Data("Qershor", qershor));
series.getData().add(new XYChart.Data("Korrik", korrik));
series.getData().add(new XYChart.Data("Gusht", gusht));
series.getData().add(new XYChart.Data("Shtator", shtator));
series.getData().add(new XYChart.Data("Tetor", tetor));
series.getData().add(new XYChart.Data("Nentor", nentor));
series.getData().add(new XYChart.Data("Dhjetor", dhjetor));
fitimetChart.getData().clear();
fitimetChart.getData().add(series);
}
public void clearChart()
{
fitimetChart.getData().clear();
}
#FXML
public void fitimetBox()
{
if(box.getSelectionModel().isSelected(0))
{
clearChart();
addFitimetVjetoreToChart();
}
if(box.getSelectionModel().isSelected(1))
{
clearChart();
addFitimetNentorToChart();
}
}
}
FXMLDocument.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.chart.AreaChart?>
<?import javafx.scene.chart.CategoryAxis?>
<?import javafx.scene.chart.NumberAxis?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication17.FXMLDocumentController">
<center>
<VBox alignment="CENTER" BorderPane.alignment="CENTER">
<children>
<ComboBox fx:id="box" onAction="#fitimetBox" prefWidth="150.0" />
<AreaChart fx:id="fitimetChart" title="Fitimet">
<xAxis>
<CategoryAxis side="BOTTOM" />
</xAxis>
<yAxis>
<NumberAxis side="LEFT" />
</yAxis>
</AreaChart>
</children>
</VBox>
</center>
</BorderPane>
JavaFXApplication17.java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class JavaFXApplication17 extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
p.s. i made clearChart() method , because if i dont clear chart first it just keep writing new XData after , without clearing first ones.
Also is it possible that , when users click on those little circle on top of every line chart , to show the number value(Y)
I just needed to update the Java Version , and everything looks fine now.
What happens if you replace:
public void clearChart()
{
fitimetChart.getData().clear();
}
With:
public void clearChart()
{
if(fittimeChart.getData().size() > 0)//check to see if the .size() part is correct.
{
fitimetChart.getData().remove(0);
}
}
This is the output I got from running your original code with zero
changes:
Related
I'm trying to build a replica of MineSweeper, to do this I'm trying to setup some premade images (100x100 pixels made in Photoshop) into an imageview and then when clicked hiding it (to reveal the number below). Without much complexity -- Just the image going visible and invisible I am finding a lot of issues and difficulties.
It is likely due to a complete lack of knowledge on Javafx in general but I even following tutorials to the t I am still unable to implement this feature. I will attach to this my Main.Java code, my sample.fxml code (although it's not called anymore), and then the image I'm trying to hide when clicked.
I have done a lot of research on this (past couple of days) and haven't found anything that solves my problems.
Main.java:
package sample;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
ImageView button;
#Override
public void start(Stage primaryStage) throws Exception{
primaryStage.setTitle("MineSweeper XP");
button = new ImageView();
button.setOnMouseClicked(new EventHandler<MouseEvent>)(){
public void handle(MouseEvent event){
}
}
StackPane layout = new StackPane();
layout.getChildren().add(button);
Scene scene = new Scene(layout,1000, 1000);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
sample.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="100.0" prefWidth="100.0" xmlns="http://javafx.com/javafx/8.0.121" xmlns:fx="http://javafx.com/fxml/1">
<children>
<ImageView fx:id="button" fitHeight="150.0" fitWidth="100.0" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="#../textures/Button_Custom.png" />
</image>
</ImageView>
</children>
</AnchorPane>
The "Button" that will be used for all the MineSweeper keys
My singular goal at this given moment is to create a window of any size where I can put the button anywhere and then when the user clicks that button it disappears.
One possible solution is to create your own ImageView that holds the code for mouse clicks within.
Here's a sample TileButton class:
class TileButton extends ImageView {
public TileButton() {
// Set parameters for the image
Image graphic = new Image("minesweeper/tile.png");
setImage(graphic);
setFitWidth(24);
setFitHeight(24);
// When this button is click, set its visibility to false.
setOnMouseClicked(e -> {
setVisible(false);
});
}
}
Using this custom class, all the logic for the "button" is contained within the TileButton itself.
Now, you can populate your mine field with a GridPane of StackPane containers in each cell. The StackPane allows you to stack nodes on top of each other. So, place a Label with the number you want into each StackPane and then add your new TileButton on top of it.
Below is a complete sample application to demonstrate. Note, I am not implementing any of the actual game logic here, just providing a sample you can copy/paste and see in action. I also have not spent any time formatting and styling the ImageView, but your could use CSS to make it act like a standard button as well.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
VBox root = new VBox(5);
root.setPadding(new Insets(10));
root.setAlignment(Pos.CENTER);
// GridPane to hold the cells
GridPane gridPane = new GridPane();
gridPane.setGridLinesVisible(true);
gridPane.setHgap(2);
gridPane.setVgap(2);
// Populate the Gridpane with a 10x10 grid
int number = 0;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
// Add a new StackPane for each grid cell. The Stackpane will hold a number and the
// TileButton. When the TileButton is clicked, it disappears, revealing the number below
StackPane pane = new StackPane();
pane.getChildren().add(new Label(String.valueOf(number)));
pane.getChildren().add(new TileButton());
gridPane.add(pane, j, i);
// Just increment our sample number
number++;
}
}
root.getChildren().add(gridPane);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
class TileButton extends ImageView {
public TileButton() {
// Set parameters for the image
Image graphic = new Image("minesweeper/tile.png");
setImage(graphic);
setFitWidth(24);
setFitHeight(24);
// When this button is click, set its visibility to false.
setOnMouseClicked(e -> {
setVisible(false);
});
}
}
The above sample application produces this:
Note I am not using FXML for this as creating a grid of multiple custom objects is much simpler in Java than FXML.
Per kleopatra's suggestion, this can be accomplished using a custom Button instead. With the new TileButton class below, you can add the buttons in our loop using gridPane.add(new TileButton(String.valueOf(number)), j, i);:
class TileButton extends Button {
public TileButton(String text) {
// Set the button's size
setPrefSize(24,24);
setStyle("-fx-padding: 0");
// Set the graphic to our tile.png image
setGraphic(new ImageView("sample/done/minesweeper/tile.png"){{
setFitWidth(24);
setFitHeight(24);
}});
// Set the button to display only our graphic initially
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
// Set the action to remove the graphic when the button is clicked
setOnAction(event -> {
setGraphic(new Label(text));
});
}
}
I'm new to JavaFX (Java for that matter) and I want to be able to click a GridPane and have it display my room attributes in a side-panel(or to console at this point). I've managed to setup a mouse event but I don't think it's the right tool for the job. When I click anywhere in the grid, it returns "null" and won't give me the cell coordinates(maybe there's better or more useful info to gather here?).
I'm using Scene Builder as well.
Controller Class
import Data.Area;
import Model.Grid;
import Model.TileSet;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import java.io.IOException;
import java.io.InputStream;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
public class Controller {
InputStream rinput = getClass().getResourceAsStream("/red.png");
Image red = new Image(rinput);
ImageView redimg = new ImageView(red);
InputStream binput = getClass().getResourceAsStream("/black.png");
Image black = new Image(binput);
InputStream pinput = getClass().getResourceAsStream("/pink.png");
Image pink = new Image(binput);
#FXML
public GridPane gridmane;
public void genSmall(ActionEvent actionEvent) throws IOException {
Grid grid = new Grid(new Area(40, 40));
grid.getPathfinder().shufflePartitions();
grid.getPathfinder().fillPartitions();
grid.getPathfinder().generateHallways();
grid.getPathfinder().placeWalls();
importGrid(gridmane, grid);
gridmane.getScene().getWindow().sizeToScene();
}
public void genMed(ActionEvent actionEvent) throws IOException {
Grid grid = new Grid(new Area(60, 60));
grid.getPathfinder().shufflePartitions();
grid.getPathfinder().fillPartitions();
grid.getPathfinder().generateHallways();
grid.getPathfinder().placeWalls();
gridmane.getScene().getWindow().sizeToScene();
gridmane.getScene().getWindow().setHeight(600);
gridmane.getScene().getWindow().setWidth(600);
importGrid(gridmane, grid);
}
public void genLarge(ActionEvent actionEvent) throws IOException {
Grid grid = new Grid(new Area(80, 80));
grid.getPathfinder().shufflePartitions();
grid.getPathfinder().fillPartitions();
grid.getPathfinder().generateHallways();
grid.getPathfinder().placeWalls();
gridmane.getScene().getWindow().sizeToScene();
gridmane.getScene().getWindow().setHeight(800);
gridmane.getScene().getWindow().setWidth(800);
importGrid(gridmane, grid);
}
private void importGrid(GridPane gridPane, Grid grid) {
gridPane.getChildren().clear(); // remove old children
for (int i = 0; i < grid.getSize().height; i++) {
for (int j = 0; j < grid.getSize().width; j++) {
if (grid.getContent()[j + (i * grid.getSize().width)] == TileSet.floorTile) {
changeSquare(gridPane, i, j, Color.WHITE, red);
}
else if (grid.getContent()[j + (i * grid.getSize().width)] == TileSet.wallTile) {
changeSquare(gridPane, i, j, Color.GRAY, black);
}
else {
changeSquare(gridPane, i, j, Color.BLACK, pink);
}
}
}
}
private void changeSquare(GridPane gridPane, int xCoordinate, int yCoordinate, Color color, Image image) {
Rectangle rect = new Rectangle();
ImageView fimage = new ImageView(image);
rect.setStroke(Color.BLACK);
rect.setFill(color);
rect.setWidth(10);
rect.setHeight(10);
gridPane.add(fimage, xCoordinate, yCoordinate);
}
public void clickGrid(javafx.scene.input.MouseEvent event) {
Node source = (Node)event.getSource() ;
Integer colIndex = gridmane.getColumnIndex(source);
Integer rowIndex = gridmane.getRowIndex(source);
System.out.println("Mouse clicked cell: " + colIndex + "And: " + rowIndex);
}
}
Main Class
public class Main extends Application {
Stage stage = new Stage();
int val = 40;
#Override
public void start(Stage primaryStage) throws Exception {
this.stage = primaryStage;
setVal(val);
}
public static void main(String[] args) {
launch(args);
}
public void setVal(int i) throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("/view.fxml"));
stage.setTitle("Dungeon Generator");
stage.setScene(new Scene(root, 450, 450));
//primaryStage.setResizable(false);
stage.sizeToScene();
stage.show();
}
}
FXML File
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.image.Image?>
<VBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="258.0" prefWidth="332.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Controller">
<children>
<MenuBar>
<menus>
<Menu mnemonicParsing="false" text="File">
<items>
<MenuItem mnemonicParsing="false" text="Close" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Edit">
<items>
<MenuItem mnemonicParsing="false" text="Delete" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Help">
<items>
<MenuItem mnemonicParsing="false" text="About" />
</items>
</Menu>
</menus>
</MenuBar>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0">
<children>
<Button alignment="CENTER" mnemonicParsing="false" onAction="#genSmall" prefHeight="27.0" prefWidth="64.0" text="Small" HBox.hgrow="ALWAYS">
<HBox.margin>
<Insets left="20.0" />
</HBox.margin>
</Button>
<Button alignment="CENTER" mnemonicParsing="false" onAction="#genMed" text="Medium" HBox.hgrow="ALWAYS">
<HBox.margin>
<Insets left="20.0" />
</HBox.margin>
</Button>
<Button alignment="CENTER" mnemonicParsing="false" onAction="#genLarge" prefHeight="27.0" prefWidth="66.0" text="Large" HBox.hgrow="ALWAYS">
<HBox.margin>
<Insets left="20.0" />
</HBox.margin>
</Button>
</children>
</HBox>
<StackPane>
<children>
<GridPane fx:id="gridmane" maxHeight="-Infinity" maxWidth="-Infinity" onMouseClicked="#clickGrid" VBox.vgrow="NEVER" />
</children>
</StackPane>
</children>
</VBox>
Since you register the event handler at the GridPane, the source of the event is the GridPane itself. You did not set the row/column index for the GridPane and it wouldn't contain any useful information.
In this case you need to get the node that was actually clicked from the MouseEvent:
public void clickGrid(javafx.scene.input.MouseEvent event) {
Node clickedNode = event.getPickResult().getIntersectedNode();
if (clickedNode != gridmane) {
// click on descendant node
Integer colIndex = GridPane.getColumnIndex(clickedNode);
Integer rowIndex = GridPane.getRowIndex(clickedNode);
System.out.println("Mouse clicked cell: " + colIndex + " And: " + rowIndex);
}
}
In this case the code works like this since the children of your GridPane don't have children of their own that could be the intersected node.
If you want to add children that could have children of their own you need to go up through the node hierarchy until you find a child of the GridPane:
if (clickedNode != gridmane) {
// click on descendant node
Node parent = clickedNode.getParent();
while (parent != gridmane) {
clickedNode = parent;
parent = clickedNode.getParent();
}
Integer colIndex = GridPane.getColumnIndex(clickedNode);
Integer rowIndex = GridPane.getRowIndex(clickedNode);
System.out.println("Mouse clicked cell: " + colIndex + " And: " + rowIndex);
}
package javafx_tipcalc;
import javafx.application.Application;
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.ChoiceBox;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import java.text.NumberFormat;
public class JavaFX_TipCalc extends Application {
// declare interface controls
// declare labels
Label titleLabel, checkAmtLabel, tipPercentLabel, splitLabel;
Label tipAmtLabel, totalLabel, amtPerPersonLabel;
// declare text fields
TextField checkAmtText, tipAmtText, totalText, amtPerPersonText;
// declare a slider
Slider tipPercentSlider;
// declare a choice box
ChoiceBox splitChoiceBox;
// declare a button
Button calcTipButton;
// declare currency and percent formatter
NumberFormat currency = NumberFormat.getCurrencyInstance();
NumberFormat persent = NumberFormat.getCurrencyInstance();
// declare a grid pane (8 rows and 2 columns)
GridPane grid;
#Override
public void start(Stage primaryStage) {
// instantiate labels and their properties
titleLabel = new Label("Tip Calculator");
titleLabel.setMaxWidth(Double.MAX_VALUE);
titleLabel.setAlignment(Pos.CENTER);
checkAmtLabel = new Label("Check Amount");
checkAmtLabel.setMaxWidth(Double.MAX_VALUE);
checkAmtLabel.setAlignment(Pos.CENTER_RIGHT);
tipPercentLabel = new Label("Tip Percent: 15%");
tipPercentLabel.setMaxWidth(Double.MAX_VALUE);
tipPercentLabel.setAlignment(Pos.CENTER_RIGHT);
splitLabel = new Label("Split");
splitLabel.setMaxWidth(Double.MAX_VALUE);
splitLabel.setAlignment(Pos.CENTER_RIGHT);
tipAmtLabel = new Label("Tip Amount");
tipAmtLabel.setMaxWidth(Double.MAX_VALUE);
tipAmtLabel.setAlignment(Pos.CENTER_RIGHT);
totalLabel = new Label("Total");
totalLabel.setMaxWidth(Double.MAX_VALUE);
totalLabel.setAlignment(Pos.CENTER_RIGHT);
amtPerPersonLabel = new Label("Amount per Person");
amtPerPersonLabel.setMaxWidth(Double.MAX_VALUE);
amtPerPersonLabel.setAlignment(Pos.CENTER_RIGHT);
// instantiate text fileds and their properties
double textFieldWidth = 100;
checkAmtText = new TextField();
checkAmtText.setOnMouseClicked(e -> ResetFields());
checkAmtText.setPrefWidth(textFieldWidth);
checkAmtText.setAlignment(Pos.CENTER_RIGHT);
tipAmtText = new TextField();
tipAmtText.setFocusTraversable(false);
tipAmtText.setPrefWidth(textFieldWidth);
tipAmtText.setAlignment(Pos.CENTER_RIGHT);
tipAmtText.setEditable(false);
totalText = new TextField();
totalText.setFocusTraversable(false);
totalText.setPrefWidth(textFieldWidth);
totalText.setAlignment(Pos.CENTER_RIGHT);
totalText.setEditable(false);
amtPerPersonText = new TextField();
amtPerPersonText.setFocusTraversable(false);
amtPerPersonText.setPrefWidth(textFieldWidth);
amtPerPersonText.setAlignment(Pos.CENTER_RIGHT);
amtPerPersonText.setEditable(false);
// instantiate a slider and its properties
tipPercentSlider = new Slider();
tipPercentSlider.setPrefWidth(10);
tipPercentSlider = new javafx.scene.control.Slider();
tipPercentSlider.setPrefWidth(150);
tipPercentSlider.setMin(0);
tipPercentSlider.setMax(25);
tipPercentSlider.setMajorTickUnit(5);
tipPercentSlider.setMinorTickCount(1);
tipPercentSlider.setBlockIncrement(1);
tipPercentSlider.setShowTickLabels(true);
tipPercentSlider.setShowTickMarks(true);
tipPercentSlider.setSnapToTicks(true);
tipPercentSlider.setValue(15);
tipPercentSlider.setOrientation(Orientation.HORIZONTAL);
tipPercentSlider.valueProperty().addListener(
(observable, oldvalue, newvalue) ->
{
tipPercentLabel.setText(String.format("Tip Percent:
%2d%s",newvalue.intValue(),"%"));
} );
// instantiate a choice box and its properties
splitChoiceBox = new ChoiceBox();
splitChoiceBox.getItems().addAll("1 Way", "2 Ways", "3 Ways", "4
Ways", "5 Ways");
splitChoiceBox.setValue("1 Way");
splitChoiceBox.setPrefWidth(150);
// instantiate a button and its properties
calcTipButton = new Button("Calculate Tip");
calcTipButton.setMaxWidth(Double.MAX_VALUE);
calcTipButton.setOnAction(e -> CalcButtonClick()) ;
// instantiate a grid pane and its properties
grid = new GridPane();
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(10));
grid.add(titleLabel, 0, 0, 2, 1);
grid.addRow(1, checkAmtLabel, checkAmtText);
grid.addRow(2, tipPercentLabel, tipPercentSlider);
grid.addRow(3, splitLabel, splitChoiceBox);
grid.add(calcTipButton, 0, 4, 2, 1);
grid.addRow(5, tipAmtLabel, tipAmtText);
grid.addRow(6, totalLabel, totalText);
grid.addRow(7, amtPerPersonLabel, amtPerPersonText);
// instantiate the grid pane and put items in in grid
Scene scene = new Scene(grid);
scene.getRoot().setStyle("-fx-font: 20 'Comic Sans MS'");
primaryStage.setTitle("Tip Calculator");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
private void CalcButtonClick() {
/*
Get the check amount from checkAmtText
Get the tip percent from the slider
Get the split the choice box
Tip amount = check amount * tip percent
Total amount = check amount + tip amount
Amount per person = total amount / split
Print the tip amount in tipAmtText
Print the total amount in total Text
Print the split amtPerPerson Text
*/
double tipAmnt, checkAmnt, tipPercent,
totalAmnt, AmntPerPerson;
tipAmnt = Double.parseDouble(tipAmtText.getText());
AmntPerPerson =
Double.parseDouble(amtPerPersonText.getText());
checkAmnt = Double.parseDouble(checkAmtText.getText());
totalAmnt = Double.parseDouble(totalText.getText());
tipPercent = Double.parseDouble(tipPercentLabel.getText());
tipAmnt = checkAmnt * tipPercent;
totalAmnt = checkAmnt + tipAmnt;
tipAmtText.setText(currency.format(tipAmnt));
totalText.setText(currency.format(totalAmnt));
}
private void ResetFields() {
/*
Clear the check amount
Clear the tip amount
Clear the total amount
Clear the amount per person
Set the tip percent slider to 15%
Set the split choice box to “1 Way”
*/
}
}
You should use eventListener or FXML. Second, I think, would be easier. Need example?
UPDATE: (I added an example)
Main.java
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation((getClass().getResource("Sample.fxml")));
Parent panel = fxmlLoader.load();
Scene scene = new Scene(panel, 300, 150);
primaryStage.setTitle("Sum of two numbers");
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Sample.fxml
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="150.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.SampleController">
<children>
<AnchorPane prefHeight="55.0" prefWidth="300.0">
<children>
<HBox prefHeight="25.0" prefWidth="200.0" AnchorPane.leftAnchor="50.0" AnchorPane.rightAnchor="50.0">
<children>
<Label text="x">
<HBox.margin>
<Insets left="31.0" />
</HBox.margin>
</Label>
<Label text="y">
<HBox.margin>
<Insets left="62.0" />
</HBox.margin>
</Label>
<Label text="z">
<HBox.margin>
<Insets left="62.0" />
</HBox.margin>
</Label>
</children>
</HBox>
<HBox prefHeight="100.0" prefWidth="200.0" AnchorPane.bottomAnchor="20.0" AnchorPane.leftAnchor="50.0" AnchorPane.rightAnchor="50.0" AnchorPane.topAnchor="20.0">
<children>
<TextField fx:id="xTextField" />
<TextField fx:id="yTextField" />
<TextField fx:id="zTextField" />
</children>
</HBox>
</children>
</AnchorPane>
<AnchorPane prefHeight="200.0" prefWidth="200.0">
<HBox AnchorPane.bottomAnchor="20.0" AnchorPane.leftAnchor="50.0" AnchorPane.rightAnchor="50.0" AnchorPane.topAnchor="20.0">
<children>
<Button mnemonicParsing="false" onAction="#sumIt" text="Sum it!" AnchorPane.bottomAnchor="20.0" AnchorPane.leftAnchor="50.0" AnchorPane.rightAnchor="50.0" AnchorPane.topAnchor="20.0" />
</children>
<children>
<Button mnemonicParsing="false" onAction="#clearAll" text="Clear all!" AnchorPane.bottomAnchor="20.0" AnchorPane.leftAnchor="50.0" AnchorPane.rightAnchor="50.0" AnchorPane.topAnchor="20.0" />
</children>
</HBox>
</AnchorPane>
</children>
</VBox>
SampleController.java
package application;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
public class SampleController {
#FXML
TextField xTextField, yTextField, zTextField;
public void sumIt() {
int sum;
sum = Integer.parseInt(xTextField.getText()) + Integer.parseInt(yTextField.getText());
zTextField.setText(Integer.toString(sum));
}
public void clearAll() {
xTextField.setText("");
yTextField.setText("");
zTextField.setText("");
}
}
I have a GUI that interpolates between at least three nodes drawing a Spline. A left click on the pane leads to setting a new (draggable) node at the clicked position. If at least three nodes are present a button push "Draw Spline" will draw the interpolated Spline between the nodes. Everything works just fine except for that the Spline is not redrawn dynamically when one of the nodes is dragged up or down. Instead, after I moved a node I need to push the button "Draw Spline" and the new Spline appears. But I want to see the effect of a node shifted in its position immediately and not always hit the button. I developed this JavaFX project in NetBeans 8.1.
Here is my code:
Main.java
package InterpolationMinimal;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
AnchorPane root = FXMLLoader.load(getClass().getResource("gui.fxml"));
Scene scene = new Scene(root);
stage.setTitle("Interpolation using cubic splines");
stage.setScene(scene);
stage.show();
// this is needed to resize object if window/scene is resized
root.prefWidthProperty().bind(scene.widthProperty());
root.prefHeightProperty().bind(scene.heightProperty());
}
}
Controller.java
package InterpolationMinimal;
//import DraggableNodesGraph.MyAlerts;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Separator;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
public class Controller implements Initializable {
#FXML private AnchorPane anchorPane;
#FXML private Pane pane;
#FXML private Separator separator;
#FXML private ChoiceBox choseBoundaryConditions;
#FXML private ChoiceBox choseSolverTechnique;
private ObservableList boundaryConditionsToChose; //!< List of boundary condtions type
private ObservableList solvingTechniqueToChose; //!< List of techniques for solving a linear system of equations
public static Spline mySpline = new Spline();
#Override
public void initialize(URL url, ResourceBundle rb) {
// Set choices, default and event handling for the boundary conditions ChoiceBox
boundaryConditionsToChose = FXCollections.observableArrayList();
boundaryConditionsToChose.add("Natural");
boundaryConditionsToChose.add("Periodic");
choseBoundaryConditions.setItems(boundaryConditionsToChose);
choseBoundaryConditions.setValue(boundaryConditionsToChose.get(0));
// Set choices, default and event handling for the type of solving technique ChoiceBox
solvingTechniqueToChose = FXCollections.observableArrayList();
solvingTechniqueToChose.add("Jacobi");
solvingTechniqueToChose.add("Gauss-Seidel");
choseSolverTechnique.setItems(solvingTechniqueToChose);
choseSolverTechnique.setValue(solvingTechniqueToChose.get(0));
// Add listeners to the window size and redraw in case the window's size is changed.
// Subtract canvas.layout* to make sure the center of drawing is in the center
// of the canvas and not in the center of the whole window.
anchorPane.prefWidthProperty().addListener((ov, oldValue, newValue) -> {
pane.setPrefWidth(newValue.doubleValue() - pane.layoutXProperty().doubleValue());
rescaleObjects('x', oldValue.doubleValue(), newValue.doubleValue());
});
anchorPane.prefHeightProperty().addListener((ov, oldValue, newValue) -> {
pane.setPrefHeight(newValue.doubleValue() - pane.layoutYProperty().doubleValue());
separator.setPrefHeight(pane.getPrefHeight());
rescaleObjects('y', oldValue.doubleValue(), newValue.doubleValue());
});
MyMouseEvents.paneMouseEvents(pane);
}
#FXML public void handleButtonDrawSpline() {
if ( mySpline.getNodeList().size() >= 3 ) {
mySpline.calculate(String.valueOf(choseBoundaryConditions.getValue()),
String.valueOf(choseSolverTechnique.getValue()));
mySpline.draw(pane);
} /*else {
MyAlerts.displayAlert("You need at least 3 points for the interpolation!");
}*/
}
#FXML private void handleButtonClearCanvas(ActionEvent event) {
pane.getChildren().clear();
mySpline.getNodeList().clear();
}
private void rescaleObjects (char xOrY, double oldVal, double newVal) {
double scaleFactor = newVal / oldVal;
for (Node node: mySpline.getNodeList()) {
if ( xOrY == 'x' ) {
node.setX(node.getX()*scaleFactor);
node.relocate(node.getX(), node.getY());
} else if ( xOrY == 'y' ) {
node.setY(node.getY()*scaleFactor);
node.relocate(node.getX(), node.getY());
}
}
}
}
gui.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.canvas.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.text.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane fx:id="anchorPane" prefHeight="700.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="InterpolationMinimal.Controller">
<children>
<HBox alignment="CENTER_LEFT" layoutX="0.0" prefHeight="40.0" prefWidth="250.0">
<children>
<ChoiceBox fx:id="choseBoundaryConditions" prefWidth="210.0">
<HBox.margin>
<Insets left="10.0" />
</HBox.margin>
</ChoiceBox>
</children>
</HBox>
<HBox alignment="CENTER_LEFT" layoutX="0.0" layoutY="40.0" prefHeight="40.0" prefWidth="250.0">
<children>
<ChoiceBox fx:id="choseSolverTechnique" prefWidth="210.0">
<HBox.margin>
<Insets left="10.0" />
</HBox.margin>
</ChoiceBox>
</children>
</HBox>
<HBox alignment="CENTER_LEFT" layoutY="80.0" prefHeight="40.0" prefWidth="250.0">
<children>
<HBox alignment="CENTER_LEFT" prefHeight="40.0" prefWidth="250.0">
<children>
<Button fx:id="drawSpline" mnemonicParsing="false" onAction="#handleButtonDrawSpline" text="Draw Spline">
<HBox.margin>
<Insets left="10.0" />
</HBox.margin>
<font>
<Font size="15.0" />
</font>
</Button>
</children>
</HBox>
</children>
</HBox>
<HBox alignment="CENTER_LEFT" layoutY="120.0" prefHeight="40.0" prefWidth="250.0">
<children>
<HBox alignment="CENTER_LEFT" prefHeight="40.0" prefWidth="250.0">
<children>
<Button fx:id="clearCanvas" mnemonicParsing="false" onAction="#handleButtonClearCanvas" text="Clear Canvas">
<font>
<Font size="15.0" />
</font>
<HBox.margin>
<Insets left="10.0" />
</HBox.margin>
</Button>
</children>
</HBox>
</children>
</HBox>
<Pane fx:id="pane" layoutX="250.0" prefHeight="700.0" prefWidth="750.0"/>
<Separator fx:id="separator" layoutX="250.0" layoutY="0.0" orientation="VERTICAL" prefHeight="700.0" />
</children>
</AnchorPane>
Node.java
package InterpolationMinimal;
import javafx.scene.shape.Circle;
public class Node extends Circle implements Comparable<Node> {
private double x;
private double y;
public Node () {
}
public Node (double x, double y)
{
this.x = x;
this.y = y;
// also initialize the Circle properties:
this.setCenterX(x);
this.setCenterY(y);
}
#Override
public int compareTo(Node n) {
return this.x<n.getX()?-1:this.x>n.getX()?1:0;
}
public double getX ()
{
return this.x;
}
public double getY ()
{
return this.y;
}
public void setX (double x)
{
this.x = x;
}
public void setY (double y)
{
this.y = y;
}
}
Spline.java
package InterpolationMinimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Objects;
import javafx.scene.shape.Polyline;
import javafx.scene.layout.Pane;
public class Spline {
private ArrayList<Node> nodeList = new ArrayList<>();
private Polyline polyline = new Polyline();
private double[][] coefficients;
public ArrayList<Node> getNodeList()
{
return this.nodeList;
}
public void calculate (String boundaryConditions, String solverTechnique)
{
int numberOfNodes = this.nodeList.size();
int n = numberOfNodes - 1; // = number of splines
this.coefficients = new double[n][4];
Collections.sort(this.nodeList);
// System of equations to solve: Mc = m
// Fill the matrix M:
double[][] M = new double[n-1][n-1];
int i, j;
double h, hPlus1;
for (i=0; i<n-1; i++) {
j = i;
h = this.nodeList.get(i+1).getX() - this.nodeList.get(i).getX();
hPlus1 = this.nodeList.get(i+2).getX() - this.nodeList.get(i+1).getX();
M[i][j] = 2 * ( h + hPlus1 ); // Diagonale der Matrix mit i=j
if ( j > 0 ) M[i][j-1] = h;
if ( j < n-2 ) M[i][j+1] = hPlus1;
}
// Fill m
double[] m = new double[n-1];
for (i=0; i<n-1; i++) {
h = this.nodeList.get(i+1).getX() - this.nodeList.get(i ).getX();
hPlus1 = this.nodeList.get(i+2).getX() - this.nodeList.get(i+1).getX();
m[i] = 3 * ( this.nodeList.get(i+2).getY() - this.nodeList.get(i+1).getY() ) / hPlus1 -
3 * ( this.nodeList.get(i+1).getY() - this.nodeList.get(i ).getY() ) / h;
// Use the boundary conditions
if ( i==0 ) {
m[i] -= h * 0.0;
} else if ( i==n-2 ) {
m[i] -= h * 0.0;
}
}
// Iterative Lösung für c
double[] cTemp = new double[n-1];
double tolerance = 0.0001;
if ( Objects.equals(solverTechnique,"Jacobi") ) {
cTemp = jacobiVerfahren(M, m, tolerance);
} /*else if ( Objects.equals(solverTechnique,"Gauss-Seidel") ) {
cTemp = Matrix.gaussSeidelVerfahren(M, m, tolerance);
}*/
double[] c = new double[n];
c[0] = 0.0; // As in the left boundary condition
for (i=1; i<n; i++) {
c[i] = cTemp[i-1];
}
// Determine the other three coefficients
double[] a = new double[n];
double[] b = new double[n];
double[] d = new double[n];
for (i=0; i<n; i++) {
a[i] = this.nodeList.get(i).getY();
h = this.nodeList.get(i+1).getX() - this.nodeList.get(i).getX();
if ( i < n-1 ) {
b[i] = ((this.nodeList.get(i+1).getY()-this.nodeList.get(i).getY()) / h) -
h * (2*c[i] + c[i+1] ) / 3.0;
d[i] = (c[i+1] - c[i]) / (3.0 * h);
} else {
// c[i+1] = 0.0 wegen der natürlichen Randbedingungen
d[i] = (0.0 - c[i]) / (3.0 * h);
b[i] = ((this.nodeList.get(i+1).getY()-this.nodeList.get(i).getY()) / h) -
h * (2*c[i] + 0.0 ) / 3.0;
}
}
for (i=0; i<n; i++) {
this.coefficients[i][0] = a[i];
this.coefficients[i][1] = b[i];
this.coefficients[i][2] = c[i];
this.coefficients[i][3] = d[i];
}
}
/**
* Solve a linear system of equations (LSE) Ax=b
* #param A Matrix giving the coefficients of the LSE
* #param b Right-hand side of the equations
* #return Solution vector x of the LSE
*/
public static double[] jacobiVerfahren(double[][] A, double[] b, double tolerance)
{
int i, j;
int n = A.length; // A.lenght = A[0].length, da quadratisch
double[] x = new double[n];
double startwert = 0.0; // willkürlicher Startwert
double summe;
double[] xOld = new double[n];
for (i=0; i<n; i++) {
xOld[i] = startwert;
}
double[] genauigkeit = new double[n];
int howManyTimesUntilSmallerEpsilon = 0;
int maxIterations = 100;
boolean genauigkeitErreicht = false;
while ( ! genauigkeitErreicht ) {
howManyTimesUntilSmallerEpsilon++;
if ( howManyTimesUntilSmallerEpsilon > maxIterations ) break;
for (i=0; i<n; i++) {
summe = 0.0;
for (j=0; j<n; j++) {
if ( j != i ) {
summe += A[i][j] * xOld[j];
}
}
x[i] = ( b[i] - summe ) / A[i][i];
}
for (i=0; i<n; i++) {
genauigkeit[i] = Math.abs(x[i] - xOld[i]);
}
// Die Genauigkeit von epsilon muss für jedes Element erreicht sein, so dass
// bereits ein Element, auf das das nicht zutrifft, ausreicht, um die Iteration
// weiter zu führen.
for (i=0; i<n; i++) {
if ( genauigkeit[i] > tolerance ) {
genauigkeitErreicht = false;
break;
} else {
genauigkeitErreicht = true;
}
}
for (i=0; i<n; i++) {
xOld[i] = x[i];
}
}
return x;
}
public void draw (Pane pane)
{
int i, j, s;
int n = this.nodeList.size();
double oneStep;
double nSteps = 12.0;
double x, y, a, b, c, d;
double xDifference;
polyline.getPoints().clear();
for (i=0; i<n-1; i++) {
// Calculate the increment by means of the distance between two neighboring points
oneStep = Math.abs(this.nodeList.get(i).getX() - this.nodeList.get(i+1).getX()) / nSteps;
a = this.coefficients[i][0];
b = this.coefficients[i][1];
c = this.coefficients[i][2];
d = this.coefficients[i][3];
for (s=0; s<nSteps; s++) {
x = this.nodeList.get(i).getX() + s * oneStep;
// to calculate y we need the formula of a third-order cubic spline of the form
// y(x) = a(i) + b(i)(x-x(i))^1 + c(i)(x-x(i))^2 + d(i)(x-x(i))^3
xDifference = x - this.nodeList.get(i).getX();
y = a + (b*xDifference) + (c*Math.pow(xDifference,2.0)) + (d*Math.pow(xDifference,3.0));
polyline.getPoints().addAll(x, y);
}
}
polyline.getPoints().addAll(this.nodeList.get(n-1).getX(), this.nodeList.get(n-1).getY());
polyline.toBack();
pane.getChildren().add(polyline);
}
}
MyMouseEvents.java
package InterpolationMinimal;
import javafx.event.EventHandler;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
public class MyMouseEvents {
public static void paneMouseEvents(Pane pane) {
MouseGestures mg = new MouseGestures();
pane.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if (mouseEvent.getTarget() == pane) {
Node node = new Node(mouseEvent.getSceneX()-pane.getLayoutX(), mouseEvent.getSceneY());
node.setRadius(10.0);
mg.makeDraggable(node);
Controller.mySpline.getNodeList().add(node);
pane.getChildren().add(node);
}
mouseEvent.consume();
}
});
}
}
MouseGestures.java
package InterpolationMinimal;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.input.MouseEvent;
public class MouseGestures {
double orgSceneX, orgSceneY;
double orgTranslateX, orgTranslateY;
public void makeDraggable(Node node) {
node.setOnMousePressed(circleOnMousePressedEventHandler);
node.setOnMouseDragged(circleOnMouseDraggedEventHandler);
}
EventHandler<MouseEvent> circleOnMousePressedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent me) {
//orgSceneX = me.getSceneX();
orgSceneY = me.getSceneY();
Node p = (Node) me.getSource();
//orgTranslateX = p.getCenterX();
orgTranslateY = p.getCenterY();
}
};
EventHandler<MouseEvent> circleOnMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent me) {
//double offsetX = me.getSceneX() - orgSceneX;
double offsetY = me.getSceneY() - orgSceneY;
//double newTranslateX = orgTranslateX + offsetX;
double newTranslateY = orgTranslateY + offsetY;
Node p = (Node) me.getSource();
System.out.println();
//p.setCenterX(newTranslateX);
p.setCenterY(newTranslateY);
//p.setX(newTranslateX);
p.setY(newTranslateY);
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("gui.fxml"));
loader.load();
Controller controller = loader.getController();
controller.handleButtonDrawSpline();
} catch (Exception e) {
e.printStackTrace();
}
}
};
}
The recalculate and redraw action is realized in a way that via MouseGestures.circleOnMouseDraggedEventHandler the Controller.handleButtonDrawSpline method is accessed. But in doing so pane.getChildren() is empty (it should at least contain the node objects that were already added to the pane) while it is not empty when I hit the "Draw Spline" button. Both times the handleButtonDrawSpline method is executed but the pane object is different. I have the "feeling" that the problem lies somewhere there. Any help is greatly appreciated.
You create a new gui control with a new pane each time your circleOnMouseDraggedEventHandler method is invoked. This means that the call controller.handleButtonDrawSpline() will draw on a new pane instance each time.
The simplest way to avoid this would be to pass the controller as argument.
In Controller:
#Override
public void initialize(URL url, ResourceBundle rb) {
[...]
MyMouseEvents.paneMouseEvents(pane, this);
}
In MyMouseEvents:
public static void paneMouseEvents(Pane pane, Controller controller) {
MouseGestures mg = new MouseGestures(controller);
[...]
}
In MouseGestures:
private final Controller controller;
public MouseGestures(Controller controller) {
this.controller = controller;
}
[...]
EventHandler<MouseEvent> circleOnMouseDraggedEventHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent me) {
// double offsetX = me.getSceneX() - orgSceneX;
double offsetY = me.getSceneY() - orgSceneY;
// double newTranslateX = orgTranslateX + offsetX;
double newTranslateY = orgTranslateY + offsetY;
Node p = (Node) me.getSource();
System.out.println();
// p.setCenterX(newTranslateX);
p.setCenterY(newTranslateY);
// p.setX(newTranslateX);
p.setY(newTranslateY);
controller.handleButtonDrawSpline();
}
};
Because the draw method is drawing on the same instance of pane now, you have to avoid adding the polyline more than once to the pane in Spline#draw:
polyline.getPoints().addAll(this.nodeList.get(n - 1).getX(), this.nodeList.get(n - 1).getY());
polyline.toBack();
if (!pane.getChildren().contains(polyline)) {
pane.getChildren().add(polyline);
}
Result:
I'm new to JavaFX and I wonder if there is some equivalent to Android Toast?
I have seen the class Notification, but it doesn't look like it can be displayed only in the application. I also found I could user a Timer and make shading of a Label, but if there's some class to use, I'd be better!
Thanks!
I know it has been a long time since you post this, but I have just made an android-like toast message for javafx, so I post it here in case that someone need this kind of code.
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Duration;
public final class Toast
{
public static void makeText(Stage ownerStage, String toastMsg, int toastDelay, int fadeInDelay, int fadeOutDelay)
{
Stage toastStage=new Stage();
toastStage.initOwner(ownerStage);
toastStage.setResizable(false);
toastStage.initStyle(StageStyle.TRANSPARENT);
Text text = new Text(toastMsg);
text.setFont(Font.font("Verdana", 40));
text.setFill(Color.RED);
StackPane root = new StackPane(text);
root.setStyle("-fx-background-radius: 20; -fx-background-color: rgba(0, 0, 0, 0.2); -fx-padding: 50px;");
root.setOpacity(0);
Scene scene = new Scene(root);
scene.setFill(Color.TRANSPARENT);
toastStage.setScene(scene);
toastStage.show();
Timeline fadeInTimeline = new Timeline();
KeyFrame fadeInKey1 = new KeyFrame(Duration.millis(fadeInDelay), new KeyValue (toastStage.getScene().getRoot().opacityProperty(), 1));
fadeInTimeline.getKeyFrames().add(fadeInKey1);
fadeInTimeline.setOnFinished((ae) ->
{
new Thread(() -> {
try
{
Thread.sleep(toastDelay);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
Timeline fadeOutTimeline = new Timeline();
KeyFrame fadeOutKey1 = new KeyFrame(Duration.millis(fadeOutDelay), new KeyValue (toastStage.getScene().getRoot().opacityProperty(), 0));
fadeOutTimeline.getKeyFrames().add(fadeOutKey1);
fadeOutTimeline.setOnFinished((aeb) -> toastStage.close());
fadeOutTimeline.play();
}).start();
});
fadeInTimeline.play();
}
}
You can make a toast message from any class with this code:
String toastMsg = "some text...";
int toastMsgTime = 3500; //3.5 seconds
int fadeInTime = 500; //0.5 seconds
int fadeOutTime= 500; //0.5 seconds
Toast.makeText(primarystage, toastMsg, toastMsgTime, fadeInTime, fadeOutTime);
Try the third party ControlsFX Notifications or Notification Pane.
"The NotificationPane control allows you to notify your users of something without requiring their immediate input (which you can do with the ControlsFX dialogs API). The NotificationPane will animate in and out of view"
Notifications "will show a notification message to users in one of nine locations on the screen ... After a set duration, the notification will fade out."
To create own toast by using own fxml layout:
package controllers;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Control;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Duration;
import java.io.IOException;
public class ToastController {
public static final int TOAST_SUCCESS = 11;
public static final int TOAST_WARN = 12;
public static final int TOAST_ERROR = 13;
#FXML
private HBox containerToast;
#FXML
private Label textToast;
private void setToast(int toastType, String content){
textToast.setText(content);
switch (toastType){
case TOAST_SUCCESS:
containerToast.setStyle("-fx-background-color: #9FFF96");
break;
case TOAST_WARN:
containerToast.setStyle("-fx-background-color: #FFCF82");
break;
case TOAST_ERROR:
containerToast.setStyle("-fx-background-color: #FF777C");
break;
}
}
public static void showToast(int toastTyoe, Control control, String text){
Stage dialog = new Stage();
dialog.initOwner(control.getScene().getWindow());
dialog.initModality(Modality.APPLICATION_MODAL);
dialog.setResizable(false);
dialog.initStyle(StageStyle.UNDECORATED);
double dialogX = dialog.getOwner().getX();
double dialogY = dialog.getOwner().getY();
double dialogW = dialog.getOwner().getWidth();
double dialogH = dialog.getOwner().getHeight();
double posX = dialogX + dialogW/2;
double posY = dialogY + dialogH/6 *5;
dialog.setX(posX);
dialog.setY(posY);
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(ToastController.class.getResource("/fxml/toastPopup.fxml"));
loader.load();
ToastController ce = loader.getController();
ce.setToast(toastTyoe,text);
dialog.setScene(new Scene(loader.getRoot()));
dialog.show();
new Timeline(new KeyFrame(
Duration.millis(1500),
ae -> {
dialog.close();
})).play();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.text.Font?>
<HBox fx:id="containerToast" alignment="CENTER" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controllers.ToastController">
<children>
<Label fx:id="textToast" text="TEST" textAlignment="CENTER">
<font>
<Font name="System Bold" size="16.0" />
</font>
</Label>
</children>
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</padding>
</HBox>
to run toast just use:
One of 3 types of toast:
ToastController.TOAST_ERROR
ToastController.TOAST_SUCCESS
ToastController.TOAST_WARN
#FXML
private Label idOfAnyControl;
showToast(ToastController.TOAST_WARN,idOfAnyControl,"ERROR TEXT")
if you want you can extract 2 additional options by dependency injection (time and position of toast)
#alcoolis equivalent in kotlin
Toast.makeText(primaryStage, "No data selected!")
class Toast {
companion object {
fun makeText(stage: Stage, message: String, displayTime: Int = 3000, fadeInDelay: Int = 500, fadeOutDelay: Int = 500, size: Double = 15.0, opacity: Double = 5.0) {
val toastStage = Stage()
toastStage.initOwner(stage)
toastStage.isResizable = false
toastStage.initStyle(StageStyle.TRANSPARENT)
val text = Text(message)
text.font = Font.font("Verdana", size)
text.fill = Color.RED
val root = StackPane(text)
root.style = "-fx-background-radius: 20; -fx-background-color: rgba(0, 0, 0, 0.2); -fx-padding: 50px;"
root.opacity = opacity
val scene = Scene(root)
scene.fill = Color.TRANSPARENT
toastStage.scene = scene
toastStage.show()
val fadeInTimeline = Timeline()
val fadeInKey1 =
KeyFrame(Duration.millis(fadeInDelay.toDouble()), KeyValue(toastStage.scene.root.opacityProperty(), 1))
fadeInTimeline.keyFrames.add(fadeInKey1)
fadeInTimeline.setOnFinished {
Thread {
try {
Thread.sleep(displayTime.toLong())
} catch (e: InterruptedException) {
// TODO Auto-generated catch block
e.printStackTrace()
}
val fadeOutTimeline = Timeline()
val fadeOutKey1 =
KeyFrame(
Duration.millis(fadeOutDelay.toDouble()),
KeyValue(toastStage.scene.root.opacityProperty(), 0)
)
fadeOutTimeline.keyFrames.add(fadeOutKey1)
fadeOutTimeline.setOnFinished { toastStage.close() }
fadeOutTimeline.play()
}.start()
}
fadeInTimeline.play()
}
}
}
#acoolis version with up-to-date JavaFX transitions as suggested by #jewelsea.
public final class ToastHelper {
public static void makeText(Stage ownerStage, String toastMsg, int toastDelay, int fadeInDelay, int fadeOutDelay) {
final Stage toastStage = new Stage();
toastStage.initOwner(ownerStage);
toastStage.setResizable(false);
toastStage.initStyle(StageStyle.TRANSPARENT);
final Text text = new Text(toastMsg);
final StackPane root = new StackPane(text);
root.getStyleClass().add("my-dialog-toast");
final Scene scene = new Scene(root);
scene.setFill(Color.TRANSPARENT);
scene.getStylesheets().add("style.css");
toastStage.setScene(scene);
toastStage.show();
// make effect: fade-in, pause, then fade-out effect varying the opacity of the whole window
final javafx.animation.FadeTransition inTransition = new FadeTransition(new Duration(fadeInDelay),
toastStage.getScene().getRoot());
inTransition.setFromValue(0.0);
inTransition.setToValue(1);
final javafx.animation.FadeTransition outTransition = new FadeTransition(new Duration(fadeOutDelay),
toastStage.getScene().getRoot());
outTransition.setFromValue(1.0);
outTransition.setToValue(0);
final javafx.animation.PauseTransition pauseTransition = new PauseTransition(new Duration(toastDelay));
final javafx.animation.SequentialTransition mainTransition = new SequentialTransition(
inTransition, pauseTransition, outTransition);
mainTransition.setOnFinished(ae -> toastStage.close());
mainTransition.play();
}
}