I want to change the text under Progress Indicator. By default, when the ProgressIndicator has completed its Progress the text is Done, I want to be able to edit this text with any user-defined text or text depending on the locale.
When I run the program output shows that text has been changed, but on the GUI it doesn't change. Please look at the following pictures :
MCVE
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Main extends Application {
Task copyWorker;
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Background Processes");
Group root = new Group();
Scene scene = new Scene(root, 330, 120, Color.WHITE);
BorderPane mainPane = new BorderPane();
root.getChildren().add(mainPane);
final Label label = new Label("Files Transfer:");
final ProgressIndicator progressIndicator = new ProgressIndicator(0);
progressIndicator.progressProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> ov, Number t, Number newValue) {
progressIndicator .applyCss();
// If progress is 100% then show Text
if (newValue.doubleValue() >= 1.0) {
// Apply CSS so you can lookup the text
Text text = (Text) progressIndicator .lookup(".percentage");//also I checked .lookup(.text.percentage) version
System.out.println(text.getText());
// This text replaces "Done"
text.setText("some text");
//for testing
Text x= (Text) progressIndicator .lookup(".percentage");
System.out.println(x.getText());//output shows that the text under progress indicator is changed
}
}});
final HBox hb = new HBox();
hb.setSpacing(5);
hb.setAlignment(Pos.CENTER);
hb.getChildren().addAll(label, progressIndicator);
mainPane.setTop(hb);
final Button startButton = new Button("Start");
final Button cancelButton = new Button("Cancel");
final HBox hb2 = new HBox();
hb2.setSpacing(5);
hb2.setAlignment(Pos.CENTER);
hb2.getChildren().addAll(startButton, cancelButton);
mainPane.setBottom(hb2);
startButton.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
startButton.setDisable(true);
progressIndicator.setProgress(0);
cancelButton.setDisable(false);
copyWorker = createWorker();
progressIndicator.progressProperty().unbind();
progressIndicator.progressProperty().bind(copyWorker.progressProperty());
copyWorker.messageProperty().addListener(new ChangeListener<String>() {
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
System.out.println(newValue);
}
});
new Thread(copyWorker).start();
}
});
cancelButton.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
startButton.setDisable(false);
cancelButton.setDisable(true);
copyWorker.cancel(true);
progressIndicator.progressProperty().unbind();
progressIndicator.setProgress(0);
System.out.println("cancelled.");
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
public Task createWorker() {
return new Task() {
#Override
protected Object call() throws Exception {
for (int i = 0; i < 10; i++) {
Thread.sleep(100);
updateMessage("100 milliseconds");
updateProgress(i + 1, 10);
}
return true;
}
};
}
}
You need to change the text of the ProgressIndicator as well as set the width of the ProgressIndicator to the new width of the Text.
progressIndicator.progressProperty().addListener((ov, oldValue, newValue) -> {
Text text = (Text) progressIndicator.lookup(".percentage");
if(text!=null && text.getText().equals("Done")){
text.setText("New Text");
progressIndicator.setPrefWidth(text.getLayoutBounds().getWidth());
}
});
Complete Code
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Main extends Application {
Task copyWorker;
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Background Processes");
Group root = new Group();
Scene scene = new Scene(root, 330, 120, Color.WHITE);
BorderPane mainPane = new BorderPane();
root.getChildren().add(mainPane);
final Label label = new Label("Files Transfer:");
final ProgressIndicator progressIndicator = new ProgressIndicator(0);
final HBox hb = new HBox();
hb.setSpacing(5);
hb.setAlignment(Pos.CENTER);
hb.getChildren().addAll(label, progressIndicator);
mainPane.setTop(hb);
final Button startButton = new Button("Start");
final Button cancelButton = new Button("Cancel");
final HBox hb2 = new HBox();
hb2.setSpacing(5);
hb2.setAlignment(Pos.CENTER);
hb2.getChildren().addAll(startButton, cancelButton);
mainPane.setBottom(hb2);
startButton.setOnAction(event -> {
startButton.setDisable(true);
progressIndicator.setProgress(0);
cancelButton.setDisable(false);
copyWorker = createWorker();
progressIndicator.progressProperty().unbind();
progressIndicator.progressProperty().bind(copyWorker.progressProperty());
new Thread(copyWorker).start();
});
cancelButton.setOnAction(event -> {
startButton.setDisable(false);
cancelButton.setDisable(true);
copyWorker.cancel(true);
progressIndicator.progressProperty().unbind();
progressIndicator.setProgress(0);
});
primaryStage.setScene(scene);
primaryStage.show();
progressIndicator.progressProperty().addListener((observable, oldValue, newValue) -> {
Text text = (Text) progressIndicator.lookup(".percentage");
if (text != null && text.getText().equals("Done")) {
text.setText("New Text");
progressIndicator.setPrefWidth(text.getLayoutBounds().getWidth());
}
});
}
public Task createWorker() {
return new Task() {
#Override
protected Object call() throws Exception {
for (int i = 0; i < 10; i++) {
Thread.sleep(500);
updateMessage("2000 milliseconds");
updateProgress(i + 1, 10);
}
return true;
}
};
}
public static void main(String[] args) {
Application.launch(args);
}
}
It's posiible to change ProgressIndicator label (and all other default labels, like TextField context menu labels) by overwrite controls bundle file. In this file you have field:
ProgressIndicator.doneString=YourTextHere
How to overwrite ResourceBundle file you'll find here:
https://stackoverflow.com/a/48773353/7746751
Related
So I have written a Controller that is supposed to navigate between multiple scenes, however, when the second scene is instantiated java.lang.NullPointerException. Here is my Controller below with a View1() and View2() in a single file mre so you can understand what is happening. My goal is just to have multiple screens and multiple models and using a switch case set different scenes on the stage.
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.canvas.*;
import javafx.scene.image.Image;
public class Controller extends Application {
private Scene scene1, scene2;
public static void main(String[] args) {
launch(args);
}
public void start(Stage theStage) {
this.scene1 = new Scene(new Group(new View1()));
this.scene2 = new Scene(new Group(new View2()));
new AnimationTimer() {
int page = 2;
#Override
public void handle(long currentNanoTime){
// System.out.println(currentNanoTime);
switch (page){
case 2:
page = 1;
theStage.setScene(scene1);
break;
case 1:
page = 2;
theStage.setScene(scene2);
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
theStage.show();
}
}
class View1 extends Group {
public View1() {
Image img = new Image("https://i.imgur.com/8tcxHWh.jpg");
Canvas canvas = new Canvas(img.getWidth(), img.getHeight());
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.drawImage(img, 0, 0, canvas.getWidth(), canvas.getHeight());
getChildren().add(canvas);
}
}
class View2 extends Group {
public View2() {
Image img = new Image("https://i.imgur.com/BF3ty6o.jpg");
Canvas canvas = new Canvas(img.getWidth(), img.getHeight());
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.drawImage(img, 0, 0, canvas.getWidth(), canvas.getHeight());
getChildren().add(canvas);
}
}
I would suggest against using AnimationTimer. I would suggest you use Buttons to load different displays. This app demos one way of using Buttons to switch between displays.
Main
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
final StackPane mainDisplay = new StackPane();
final ViewOne viewOne = new ViewOne();
final ViewTwo viewTwo = new ViewTwo();
mainDisplay.getChildren().add(viewOne);//Load first view.
Button btnStageOne = new Button("View One");
Button btnStageTwo = new Button("View Two");
btnStageOne.setOnAction((event) -> {
if(!mainDisplay.getChildren().get(0).equals(viewOne))//If sceneone is not loaded, load it.
{
mainDisplay.getChildren().set(0, viewOne);
}
});
btnStageTwo.setOnAction((event) -> {
if(!mainDisplay.getChildren().get(0).equals(viewTwo))//If scenetwo is not loaded, load it.
{
mainDisplay.getChildren().set(0, viewTwo);
}
});
HBox hbButtonPanel = new HBox(btnStageOne, btnStageTwo);
VBox root = new VBox(mainDisplay, hbButtonPanel);
Scene scene = new Scene(root);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
ViewOne
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
/**
*
* #author sedrick
*/
public final class ViewOne extends StackPane{
Label label = new Label();
public ViewOne() {
label.setText("Scene One!");
getChildren().add(label);
}
}
ViewTwo
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
/**
*
* #author sedrick
*/
public final class ViewTwo extends StackPane{
Label label = new Label();
public ViewTwo() {
label.setText("Scene Two!");
getChildren().add(label);
}
}
I need some help if you can spare a few minutes.
I am in a bit of a pickle as I try to make this work.
I have a javaFX class like this
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class FoorballTeam extends Application {
int i1=0;
int i3=0;
String[] PlayerNames = new String[12];
int[] goals = new int[12];
#Override
public void start(Stage primaryStage) {
player[] playerData = new player[12];
Button btn = new Button();
btn.setText("add Player");
GridPane root = new GridPane();
root.add(btn,0,0);
int i2;
for (i2=0;i2<=11;i2++)
{playerData[i2]=new player();}
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
playerData[i3].player(root, i3);
i3++;
}
});
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public String[] getPlayerNames() {
return PlayerNames;
}
public void setPlayerNames(String[] PlayerNames) {
this.PlayerNames = PlayerNames;
}
public int[] getGoals() {
return goals;
}
public void setGoals(int[] goals) {
this.goals = goals;
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
and a second class named player like this
import javafx.event.EventType;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
public class player {
String NameOfPlayer = new String();
int goalsOfPlayer;
public void player (GridPane root,int numberOfPlayer)
{
TextField name = new TextField();
TextField goals = new TextField();
GridPane grid = new GridPane();
grid.add(name,0,0);
grid.add(goals,1,0);
root.add(grid,0,numberOfPlayer+1);
System.out.println("player " + numberOfPlayer + " added");
name.textProperty().addListener((observable, oldValue, newValue) -> {
NameOfPlayer=newValue;
});
goals.textProperty().addListener((observable, oldValue, newValue) -> {
goalsOfPlayer=Integer.parseInt(newValue);
});
}
}
I want every time that I make a change to a players name or goals to pass this change on the two arrays PlayerNames[] and goals[] of the main class.
for example if player1 changes goals from 1 to 2 I want the goals[1]=2.
Also is it possible to put a listener to this two arrays so when a player changes name or goals to trigger the listener.
Any help will be appreciated.
One simple solution is to warp the int[] array with an observable list, and listen to changes in this list :
import java.util.Arrays;
import java.util.Random;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class ListenToArrayCahnges extends Application {
private final int SIZE = 12;
private final Integer[] goals = new Integer[SIZE];
private final Random rand = new Random();
private int counter = 0;
#Override
public void start(Stage primaryStage) {
Arrays.fill(goals, 0); //initial values
//Warp array with an observable list. list back by array so it is of fixed length
ObservableList<Integer> goalsList = FXCollections.observableArrayList(Arrays.asList(goals));
//add listener to list
goalsList.addListener((ListChangeListener<Integer>) c ->{
//respond to list changes
System.out.println("Goals changed to : "+ goalsList);
});
//button to change the list
Button btn = new Button();
btn.setText("Add goal");
btn.setOnAction(event -> {
goalsList.set(counter, rand.nextInt(100));
counter = ++counter % SIZE ; //increment counter 0,1,2....11 and back to 0
});
GridPane root = new GridPane();
root.add(btn,0,0);
Scene scene = new Scene(root, 150, 50);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
Note that the posted mcve represents the problem that needs to be solved (or in this case a solution to it), and not the specific application or use case.
I'm using this example http://docs.oracle.com/javafx/2/ui_controls/table-view.htm where tableview is based on MapValueFactory.
Now I have the situation when edited values are not saved. My actions:
Press "Enter" -> type new value -> press "Enter" again -> Result: New value is shown in the cell.
But when I press "Enter" in this cell again the old value is shown instead of New value.
Please help me to understand how can I save changes after editing the cell?
Example 12-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 table_view = new TableView<>(generateDataInMap());
table_view.setEditable(true);
table_view.getSelectionModel().setCellSelectionEnabled(true);
table_view.getColumns().setAll(firstDataColumn, secondDataColumn);
Callback<TableColumn<Map, String>, TableCell<Map, String>>
cellFactoryForMap = new Callback<TableColumn<Map, String>,
TableCell<Map, String>>() {
#Override
public TableCell call(TableColumn p) {
return 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, table_view);
((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;
}
}
You need to define the cellEditEvent with setOnEditCommit:
firstDataColumn.setOnEditCommit((CellEditEvent<Map<String, String>, String> t) -> {
((Map<String, String>) t.getTableView().getItems().get(t.getTablePosition().getRow())).put(Column1MapKey, t.getNewValue());
});
I am struggling to make a countdown timer in javaFX. I want the value of secondenColumn to be used as a timer. So for example when i add row with 'seconden'=200. The timer has to run for 200 seconds (until 0). I don't know how to begin with the code for the timer. This is what i have at the moment...
import java.util.regex.Pattern;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
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.stage.Stage;
import javafx.beans.property.IntegerProperty;
import javafx.collections.ListChangeListener.Change;
import javafx.animation.Timeline;
import javafx.animation.KeyFrame;
import javafx.util.Duration;
public class RuniteOre extends Application {
Stage window;
TableView<Product> table;
TextField rockInput, worldInput, aantalSpelers;
int seconden;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
window = primaryStage;
window.setTitle("Runite Ore - Calculator");
//Rock column
TableColumn<Product, String> rockColumn = new TableColumn<>("Rock");
rockColumn.setMinWidth(200);
rockColumn.setCellValueFactory(new PropertyValueFactory<>("rock"));
//World column
TableColumn<Product, Integer> worldColumn = new TableColumn<>("World");
worldColumn.setMinWidth(100);
worldColumn.setCellValueFactory(new PropertyValueFactory<>("world"));
//Aantal spelers column
TableColumn<Product, Integer> aantalSpelersColumn = new TableColumn<>("Aantal Spelers");
aantalSpelersColumn.setMinWidth(100);
aantalSpelersColumn.setCellValueFactory(new PropertyValueFactory<>("aantalSpelers"));
//Seconden column
//TableColumn<Product, Integer> secondenColumn = new TableColumn<>("Seconden");
//secondenColumn.setMinWidth(200);
//secondenColumn.setCellValueFactory(new PropertyValueFactory<>("seconden"));
TableView<Product> table = new TableView<>();
TableColumn<Product, Integer> secondenColumn = new TableColumn<>("Seconden");
table.getColumns().add(secondenColumn);
secondenColumn.setCellValueFactory(cellData -> cellData.getValue().secondsProperty().asObject());
table.getItems().addListener((Change<? extends Product> c) -> {
while (c.next()) {
if (c.wasAdded()) {
for (Product item : c.getAddedSubList()) {
int startValue = item.getSeconden() ;
Timeline countdown = new Timeline(new KeyFrame(Duration.seconds(1), e ->
item.setSeconden(item.getSeconden() - 1)
));
countdown.setCycleCount(startValue);
countdown.play();
}
}
}
});
//Rock input
rockInput = new TextField();
rockInput.setPromptText("Rocks");
rockInput.setMinWidth(100);
//World input
worldInput= new TextField();
worldInput.setPromptText("World");
//Aantal spelers input
aantalSpelers = new TextField();
aantalSpelers.setPromptText("Aantal Spelers");
//Button
Button addButton = new Button("Add");
addButton.setOnAction(e -> addButtonClicked());
Button deleteButton = new Button("Delete");
deleteButton.setOnAction(e -> deleteButtonClicked());
HBox hBox = new HBox();
hBox.setPadding(new Insets(10,10,10,10));
hBox.setSpacing(10);
hBox.getChildren().addAll(rockInput, worldInput, aantalSpelers, addButton, deleteButton);
table = new TableView<>();
table.getColumns().addAll(rockColumn, worldColumn, aantalSpelersColumn,secondenColumn);
VBox vBox = new VBox();
vBox.getChildren().addAll(table, hBox);
Scene scene = new Scene(vBox);
window.setScene(scene);
window.show();
}
//Add button clicked
public void addButtonClicked(){
Product product = new Product();
product.setRock(rockInput.getText());
product.setWorld(Integer.parseInt(worldInput.getText()));
product.setAantalSpelers(Integer.parseInt(aantalSpelers.getText()));
//TESTBEREKENING seconden=(Integer.parseInt(aantalSpelers.getText())*10);
seconden=(Integer.parseInt(aantalSpelers.getText())*10);
product.setSeconden(seconden);
table.getItems().add(product);
rockInput.clear();
worldInput.clear();
aantalSpelers.clear();
}
//Delete button clicked
public void deleteButtonClicked(){
ObservableList<Product> productSelected, allProducts;
allProducts = table.getItems();
productSelected = table.getSelectionModel().getSelectedItems();
productSelected.forEach(allProducts::remove);
}
}
and this is the code from class Product:
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
public class Product {
private String rock;
private int world;
private int aantalSpelers;
//private int seconden;
private int timer;
private final IntegerProperty seconden = new SimpleIntegerProperty() ;
public Product(){
this.rock = "";
this.world = 0;
this.aantalSpelers = 0;
}
public Product(String rock, int world, int aantalSpelers){
this.rock = rock;
this.world = world;
this.aantalSpelers = aantalSpelers;
}
public String getRock() {
return rock;
}
public void setRock(String rock) {
this.rock = rock;
}
public int getWorld() {
return world;
}
public void setWorld(int world) {
this.world = world;
}
public int getAantalSpelers() {
return aantalSpelers;
}
public void setAantalSpelers(int aantalSpelers) {
this.aantalSpelers = aantalSpelers;
}
public final int getSeconden() {
return secondsProperty().get();
}
public final void setSeconden(int seconden) {
secondsProperty().set(seconden);
}
public int getTimer() {
return timer;
}
public void setTimer(int timer) {
this.timer = timer;
}
public Product(int seconden) {
setSeconden(seconden);
}
public IntegerProperty secondsProperty() {
return seconden ;
}
}
Just create a Timeline and decrease the value once per second every time a new row is added to the table:
public void start(Stage primaryStage) throws Exception {
// existing code...
// this just needs to be executed before any rows are added to the table:
table.getItems().addListener((Change<? extends Product> c) -> {
while (c.next()) {
if (c.wasAdded()) {
for (Product p : c.getAddedSubList()) {
int startValue = p.getSeconden();
Timeline countdown = new Timeline(new KeyFrame(Duration.seconds(1),
e -> p.setSeconden(p.getSeconden() - 1)));
countdown.setCycleCount(startValue);
countdown.play();
}
}
}
});
}
This assumes your Product class follows the JavaFX properties pattern, i.e. it has a public IntegerProperty secondenProperty() { ... } method.
Here is a SSCCE:
import java.util.regex.Pattern;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.ListChangeListener.Change;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class CountdownTable extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
TableView<Item> table = new TableView<>();
TableColumn<Item, Integer> secondsCol = new TableColumn<>("Seconds");
table.getColumns().add(secondsCol);
secondsCol.setCellValueFactory(cellData -> cellData.getValue().secondsProperty().asObject());
table.getItems().addListener((Change<? extends Item> c) -> {
while (c.next()) {
if (c.wasAdded()) {
for (Item item : c.getAddedSubList()) {
int startValue = item.getSeconds() ;
Timeline countdown = new Timeline(new KeyFrame(Duration.seconds(1), e ->
item.setSeconds(item.getSeconds() - 1)
));
countdown.setCycleCount(startValue);
countdown.play();
}
}
}
});
TextField textField = new TextField();
textField.setPromptText("Type a time in seconds and press enter");
Pattern integerPattern = Pattern.compile("\\d*");
TextFormatter<Integer> formatter = new TextFormatter<Integer>( (TextFormatter.Change c) -> {
String newText = c.getControlNewText();
if (integerPattern.matcher(newText).matches()) {
return c ;
} else {
return null ;
}
});
textField.setTextFormatter(formatter);
textField.setOnAction(e -> {
if (! textField.getText().isEmpty())
table.getItems().add(new Item(Integer.parseInt(textField.getText())));
textField.clear();
});
BorderPane root = new BorderPane(table, null, null, textField, null);
primaryStage.setScene(new Scene(root, 600, 600));
primaryStage.show();
}
public static class Item {
private final IntegerProperty seconds = new SimpleIntegerProperty() ;
public Item(int seconds) {
setSeconds(seconds);
}
public IntegerProperty secondsProperty() {
return seconds ;
}
public final int getSeconds() {
return secondsProperty().get();
}
public final void setSeconds(int seconds) {
secondsProperty().set(seconds);
}
}
public static void main(String[] args) { launch(args); }
}
I am adding button to grid pane dynamically but after giving them function they all show same function and i don't knows why?
import java.awt.Panel;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
public class TestController implements Initializable {
#FXML
Panel mpanel;
#FXML
GridPane gpnael;
int x=0,y=0,i=0,y1=0;
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
#FXML
private void add(ActionEvent event) {
y1++;
Button temp = new Button("Button " + i);
temp.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
System.out.println("button"+y1);
}
});
gpnael.add(temp,i++,1);
}
}
now i have added three button to grid pane when i click on each button they show same output.
I want that they all show different output as assigned .
You are not defining it in the buttons, you are always using a non final int to express your values you should try do make them with unique values or to set an id for each button and get the value from the id:
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class ButtonsOnGPanel extends Application {
private int i = 0;
private GridPane gpnael = new GridPane();
#Override
public void start(Stage stage) throws Exception {
Pane root = new Pane();
while(i<3){
addButton();
}
root.getChildren().add(gpnael);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
private void addButton() {
i++;
final Button temp = new Button("Button " + i);
final int numButton= i;
temp.setId("" + i);
temp.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
System.out.println("id(" + temp.getId() + ") = " + numButton);
}
});
gpnael.add(temp, i, 1);
}
public static void main(String[] args) {
launch(args);
}
}
And if you want to use lambda expressions:
temp.setOnAction((ActionEvent e) -> {
System.out.println("id(" + temp.getId() + ") = " + numButton);
});