Can anyone please help me with this:
I have some TextFields that i want to use as a timer (or a clock) so i set the text in a thread that i call inside my controller class.
package application;
import java.net.URL;
import java.util.ResourceBundle;
import org.joda.time.DateTime;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyEvent;
import jdk.nashorn.internal.runtime.FindProperty;
public class MainWindowController extends Thread implements Initializable{
#FXML
private TextField dayText;
#FXML
private TextField monthText;
#FXML
private TextField yearText;
#FXML
private TextField hoursText;
#FXML
private TextField minutesText;
#FXML
private TextField secondsText;
#FXML
private TextField julianDayText;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
this.start();
}
#Override
public void run() {
while(true){
DateTime d = new DateTime(System.currentTimeMillis());
dayText.setText(String.valueOf(d.getDayOfMonth()));
monthText.setText(String.valueOf(d.getMonthOfYear()));
yearText.setText(String.valueOf(d.getYear()));
hoursText.setText(String.valueOf(d.getHourOfDay()));
minutesText.setText(String.valueOf(d.getMinuteOfHour()));
secondsText.setText(String.valueOf(d.getSecondOfMinute()));
}
}
}
I don't know why i get a NullPointerException after running my code (it works for a little bit then it crashes) :
Exception in thread "Thread-4" java.lang.NullPointerException
at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:339)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
at javafx.scene.control.TextInputControl$TextProperty.fireValueChangedEvent(TextInputControl.java:1116)
at javafx.scene.control.TextInputControl$TextProperty.markInvalid(TextInputControl.java:1120)
at javafx.scene.control.TextInputControl$TextProperty.set(TextInputControl.java:1056)
at javafx.scene.control.TextInputControl.setText(TextInputControl.java:279)
at application.MainWindowController.run(MainWindowController.java:208)
Please Help and thanks in advance
You can use TimeLine And KeyFrames. Just replace showTime() with your own code.
and do not extend thread.
public class FXMLTimeController implements Initializable {
#FXML
private TextField txtTime;
//timeline
private Timeline timeline;
private void showTime() {
txtTime.setText((new Date()).toString());
}
#Override
public void initialize(URL url, ResourceBundle rb) {
timeline = new Timeline();
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.setAutoReverse(false);
timeline.getKeyFrames().add(
new KeyFrame(Duration.seconds(1),
new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
showTime();
}
}));
timeline.play();
}
}
I would suggest calling Platform.runLater(Runnable runnable) in initialize() while passing in JavaFX's TextField fields, instead of making your controller extend Thread, since JavaFX uses the controller for itself.
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
Platform.runLater(
new someThread(dayText, monthText, yearText,
hoursText, minutesText, secondsText));
}
public class someThread implements Runnable {
public someThread(TextField... textFields) {
// Create local variables
}
#Override
public void run() {
// while (true) loop goes here
}
}
You could also use this to pass in your controller as a parameter for someThread and make your TextFields public so they can be accessed by someThread.
Related
So I've got a method called 'popup' in a javaFX controller class which opens a small popup window on top of the actual application window. This method runs without problem if it's assigned to a button in fxml and the button is clicked, but this is not the way I want to use it.
I've got an other class called 'Timer' with a new task (new thread) which is counting down from a certain number, and at a point it will open a popup window with a message. My purpose is to call and run the 'popup' method from this 'Timer' class. When I call the 'popup' method from here, it starts executing, but the popup window doesn't appear at all. (The method call happens as I get the message "in popup" on console from 'popup' method. )
So why does it work when a button click calls 'popup' method from the fxml file and why not when I call it from an other class? Thanks.
Please see the controller class with 'popup' method and the Timer class below (using Gradle in project):
"SceneController" controller class:
package GradleFX;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
//import java.awt.event.ActionEvent;
public class SceneController implements Initializable {
public static String password = "";
protected static int timercount = 20;
#FXML
private Label PWLabel;
#FXML
private Label bottomLabel;
#FXML
private PasswordField PWField;
#FXML
private Label showPWLabel;
protected static Label myBottomLabel;
private static PasswordField myPWField;
private static Label myShowPWLabel;
private static int tries;
#Override
public void initialize(URL location, ResourceBundle resources) {
Timer timerTask = new Timer();
myBottomLabel = bottomLabel;
myPWField = PWField;
myShowPWLabel = showPWLabel;
new Thread(timerTask).start();
}
**/***********************************************************************
/*This method runs if button is pressed in main application,
but can't make it work by calling it from Timer Class */
public void popup() {
System.out.println("in popup");
Stage dialogStage = new Stage();
dialogStage.initModality(Modality.WINDOW_MODAL);
VBox vbox = new VBox(new Text("Hi"), new Button("Ok."));
vbox.setAlignment(Pos.CENTER);
vbox.setPadding(new Insets(15));
dialogStage.setScene(new Scene(vbox));
dialogStage.show();
}
//****************************************************************************
public void showPW() {
myShowPWLabel.setText(myPWField.getText());
}
public void hidePW() {
myShowPWLabel.setText("");
}
public void exit() {
System.exit(0);
}
public void write() {
PWLabel.setText("Mukodik");
}
public void writeInput(String in) {
password = in;
System.out.println("final password text text: " + password);
writeFinally();
}
public void writeFinally() {
System.out.println("This is 'password' : " + password);
//bottomLabel.setText(password);
}
public void bottomLabelWrite() {
bottomLabel.setText(myPWField.getText());
}
public static void setLabel() throws InterruptedException {
myBottomLabel.setText("");
myBottomLabel.setText("Database has been permanently erased.");
//Thread.sleep(3000);
//System.exit(0);
}
public static void noKeyEnteredNote() {
myBottomLabel.setTextFill(Color.BLACK);
myBottomLabel.setText("No key entered. Type Main Key.");
}
public static void rightKey() {
myBottomLabel.setText("Yes, this is the right key.");
}
public static void wrongKey() throws InterruptedException {
tries = MasterKey.numOfTryLeft;
if (tries > 0) {
myBottomLabel.setTextFill(Color.RED);
myBottomLabel.setText("!!!Wrong key!!! You've got " + tries + " tries left!");
}
}
public void simpleTest(String in) {
System.out.println("in simpleTest and in is: " + in);
}
public void getMainKey() throws IOException, InterruptedException {
MasterKey masterKey = new MasterKey();
System.out.println("Inside SceneController");
masterKey.requestKey(myPWField.getText());
}
public void changeScreen(ActionEvent event) throws IOException, InterruptedException {
getMainKey();
if (MasterKey.isRightKey) {
Parent tableViewParent = FXMLLoader.load(getClass().getResource("Menu.fxml"));
Scene tableViewScene = new Scene(tableViewParent);
Stage window = (Stage) ((Node) event.getSource()).getScene().getWindow();
window.setScene(tableViewScene);
window.show();
}
}
}
This is Timer class:
package GradleFX;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
public class Timer extends Task {
private ActionEvent actionEvent;
#Override
protected Integer call() throws Exception {
boolean notCalled = true;
while (SceneController.timercount > 0) {
SceneController sceneController = new SceneController();
System.out.println(SceneController.timercount);
Thread.sleep(1000);
SceneController.timercount--;
if (SceneController.timercount < 19) {
System.out.println("Less than 5");
if(notCalled) {
sceneController.popup();
notCalled = false;
}
}
}
System.exit(0);
return null;
}
}
Add this to your code:
#Override
public void initialize(URL location, ResourceBundle resources) {
Timer timerTask = new Timer();
myBottomLabel = bottomLabel;
myPWField = PWField;
myShowPWLabel = showPWLabel;
new Thread(timerTask).start();
timerTask.setOnFinished(e->{
popup();
});
}
I am dealing with JavaFX, as you can see in the picture below, I have a menu (A,B,C,D,E) on the left, all I want to do is that when I click on the menu item, the content of the page change (the red form).
I implemented my code, it works fine visually, but at each time it creates a new controller for each new form. And when it does so I loose the data of my main frame. Here is my Code to explain better.
MainFrame.fxml
<ScrollPane xmlns="http://javafx.com/javafx/8.0.172-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Controller.Inhumer.MainController" >
<BorderPane fx:id="myContent" >
<center >
<fx:include fx:id="demandeur" source="Demandeur.fxml" />
</center>
<left>
<fx:include fx:id="menu" source="SideBar_Inhumer.fxml" />
</left>
</BorderPane>
</ScrollPane>
MainController.java
package Controller.Inhumer;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.layout.BorderPane;
import java.io.IOException;
import java.util.HashMap;
public class MainController {
HashMap<String, Parent> menuItems;
#FXML
public BorderPane myContent ;
#FXML
DemandeurController demandeurController;
#FXML
MenuController menuController;
#FXML public void initialize() throws IOException {
/*load the content of my forms */
Parent rootDemandeur = new FXMLLoader(getClass().getResource("../../View/Inhumer/Demandeur.fxml")).load();
Parent rootDefunt = new FXMLLoader(getClass().getResource("../../View/Inhumer/Defunt.fxml")).load();
Parent rootEmplacement = new FXMLLoader(getClass().getResource("../../View/Inhumer/Emplacement.fxml")).load();
Parent rootPrestataire = new FXMLLoader(getClass().getResource("../../View/Inhumer/Prestataire.fxml")).load();
Parent rootOperation = new FXMLLoader(getClass().getResource("../../View/Inhumer/Operation.fxml")).load();
menuItems = new HashMap<>();
menuItems.put("demandeur",rootDemandeur); //A
menuItems.put("defunt",rootDefunt); //B
menuItems.put("emplacement",rootEmplacement); //C
menuItems.put("prestataire",rootPrestataire); //D
menuItems.put("operation",rootOperation); //E
System.out.println("Application started");
demandeurController.init(this);
menuController.init(this);
}
}
MenuController.java
package Controller.Inhumer;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.layout.BorderPane;
import java.io.IOException;
public class MenuController {
private MainController main;
/*methods called onClick on each item of my menu*/
#FXML
public void goToDemandeur() throws IOException {
main.myContent.setCenter(main.menuItems.get("demandeur"));
}
#FXML
public void goToDefunt() throws IOException {
main.myContent.setCenter(main.menuItems.get("defunt"));
}
#FXML
public void goToEmplacement() throws IOException {
main.myContent.setCenter(main.menuItems.get("emplacement"));
}
#FXML
public void goToPrestataire() throws IOException {
main.myContent.setCenter(main.menuItems.get("prestataire"));
}
#FXML
public void goToOperation() throws IOException {
main.myContent.setCenter(main.menuItems.get("operation"));
}
public void init(MainController mainController) {
main = mainController;
}
}
DemandeurController.java (the controller of the red form)
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
public class DemandeurController implements Initializable {
public MainController mainController;
/*Onclick action on the button called "suivant"*/
#FXML
public void next() throws IOException {
System.out.println(mainController);
/*print null, because when loading this form mainController will get
null value, and I want to always get the value of mainController
so I can access to its content from this controller*/
}
public void init(MainController mainController) {
this.mainController = mainController;
}
#Override
public void initialize(URL location, ResourceBundle resources) {
}
}
I hope that I clarified well the problem, any help? I am blocked here for two days :v
I am trying to display different content in a certain scene based on the button the user selects in the previous scene. I have tried using public static void main(String[] args) and timers to get this to work, but I just can't.
How do I get contentSelect() to run upon the opening of the scene?
I know this should be simple, but I cannot get it to work for the life of me.
package application;
import java.time.Duration;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.image.ImageView;
public class GrammarTestController {
private static int picSelect=0;
#FXML
private Label title;
#FXML
private Label info;
#FXML
private ImageView image;
//Will decide which type of content to display
private void contentSelect(){
}
}
Implement Initializable:
public class GrammarTestController implements Initializable{
private static int picSelect=0;
#FXML
private Label title;
#FXML
private Label info;
#FXML
private ImageView image;
//This method is called upon fxml load
public void initialize(URL location, ResourceBundle resources) {
contentSelect();
}
//Will decide which type of content to display
private void contentSelect(){
}
}
I'm writing a simple JavaFX application that has three Stages: Login, Register (Anmeldung) and Welcome (Anwendung). Sorry for the German namings!
I have created each Stage and it's Scene in an App class and the handling events in a Controller class and the designs in fxml files. I need to implement a MainApp class which has to manage the communication between Login, Anmeldung and Anwendung windows.
The MainApp should initially launch a Login window and then in there if the checkbox is selected, the MainApp should be notified and order the launch of Anmeldung window. After successful registration, the MainApp should close the Anmeldung window and show the Login window again. There, when user logs in, the MainApp should again close the Login window and order a launch for Anwendung window.
I have done the transition between Login and Anmeldung windows by modifying the LoginController, which shouldn't be done there and has to be accomplished via the MainApp.
Additionally the task wants the whole thing to be done with only one launch(args).
LoginController.java:
package controller;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.stage.Modality;
import javafx.stage.Stage;
import resources.Benutzer;
public class LoginController {
// private static MainApp mainApp;
#FXML
public TextField textFieldUserId;
#FXML
public PasswordField passwordFieldPasswort;
#FXML
public CheckBox checkBoxNeuAnmeldung;
#FXML
public Button buttonEinloggen;
private boolean neuAnmeldung = false;
// public void setCallBack(MainApp mainApp) {
// LoginController.mainApp = mainApp;
// }
#FXML
public void handleButtonEinloggenAction(ActionEvent event) throws Exception {
// Stage stage = (Stage) buttonEinloggen.getScene().getWindow();
if (neuAnmeldung == false) {
Benutzer benutzer = new Benutzer(textFieldUserId.getText(),
passwordFieldPasswort.getText());
Parent anwendungsScene = FXMLLoader
.load(getClass().getResource("/design/Anwendung.fxml"));
Stage anwendungsStage = new Stage();
((Node) (event.getSource())).getScene().getWindow().hide();
anwendungsStage.setScene(new Scene(anwendungsScene));
anwendungsStage.setTitle("Anmeldung");
anwendungsStage.show();
System.out.println(benutzer);
}
// stage.close();
System.out.println("Eingeloggt!");
}
#FXML
public void handleCheckBoxNeuAnmeldungAction(ActionEvent event)
throws Exception {
if (checkBoxNeuAnmeldung.isSelected()) {
neuAnmeldung = true;
Parent anmeldungsScene = FXMLLoader
.load(getClass().getResource("/design/Anmeldung.fxml"));
Stage anmeldungsStage = new Stage();
anmeldungsStage.initModality(Modality.WINDOW_MODAL);
anmeldungsStage
.initOwner(((Node) (event.getSource())).getScene().getWindow());
anmeldungsStage.setScene(new Scene(anmeldungsScene));
anmeldungsStage.setTitle("Anmeldung");
anmeldungsStage.show();
} else
neuAnmeldung = false;
System.out.println("Neu-Anmeldung? " + neuAnmeldung);
}
}
AnmeldungsController.java:
package controller;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import resources.Benutzer;
public class AnmeldungsController {
// private static MainApp mainApp;
#FXML
public TextField textFieldUserId;
#FXML
public PasswordField passwordFieldPasswort;
#FXML
public PasswordField passwordFieldWiederholung;
#FXML
public Button buttonAnmelden;
// public void setCallBack(MainApp mainApp) {
// AnmeldungsController.mainApp = mainApp;
// }
#FXML
public void handleButtonAnmeldenAction(ActionEvent event) {
Stage stage = (Stage) buttonAnmelden.getScene().getWindow();
if (passwordFieldPasswort.getText()
.equals(passwordFieldWiederholung.getText())) {
Benutzer benutzer = new Benutzer(textFieldUserId.getText(),
passwordFieldPasswort.getText());
System.out.println(benutzer);
System.out.println("Angemeldet!");
stage.close();
} else {
textFieldUserId.setText("Passwörter stimmen nicht überein!");
System.out.println("Passwörter stimmen nicht überein!");
System.out.println(passwordFieldPasswort.getText() + " != "
+ passwordFieldWiederholung.getText());
}
}
}
AnwendungsController.java:
package controller;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.stage.Stage;
public class AnwendungsController {
// private static MainApp mainApp;
#FXML
public Button buttonSchliessen;
// public void setCallBack(MainApp mainApp) {
// AnwendungsController.mainApp = mainApp;
// }
#FXML
public void handleButtonAbbrechenAction(ActionEvent event) {
Stage stage = (Stage) buttonSchliessen.getScene().getWindow();
stage.close();
System.out.println("Fenster Geschlossen!");
}
}
MainApp.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 MainApp extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(
getClass().getResource("/design/Login.fxml"));
Parent root = loader.load();
primaryStage.setTitle("Benutzerverwaltung");
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
Of course the other aspects of the program like Exceptions and creating/reading/deleting users etc is out of the scope of this question!
Here is the basic idea that should get you started:
public class LoginController {
private final ReadOnlyBooleanWrapper loggedIn = new ReadOnlyBooleanWrapper();
public ReadOnlyBooleanProperty loggedInProperty() {
return loggedIn.getReadOnlyProperty() ;
}
public final boolean isLoggedIn() {
return loggedInProperty().get();
}
#FXML
public TextField textFieldUserId;
#FXML
public PasswordField passwordFieldPasswort;
#FXML
public CheckBox checkBoxNeuAnmeldung;
#FXML
public Button buttonEinloggen;
private boolean neuAnmeldung = false;
#FXML
public void handleButtonEinloggenAction(ActionEvent event) throws Exception {
// assuming you verify the login credentials...
loggedIn.set(true);
System.out.println("Eingeloggt!");
}
}
and now in your MainApp you can do:
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class MainApp extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(
getClass().getResource("/design/Login.fxml"));
Parent root = loader.load();
LoginController loginController = loader.getController();
loginController.loggedInProperty().addListener((obs, wasLoggedIn, isNowLoggedIn) -> {
if (isNowLoggedIn) {
// user is now logged in, show welcome screen...
}
});
primaryStage.setTitle("Benutzerverwaltung");
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
Using Scenebuilder I created a TableView and I insert several items in to it from a local Database. The items are type of a Class Symptom I've created.
package javafxapplication4;
import javafx.scene.control.Button;
public class Symptom {
private String name,category,symptomId;
private Button symptom;
public Symptom(String name,String category,String symptomId){
this.name = name;
this.category = category;
this.symptomId = symptomId;
this.symptom = new Button("Select Symptom");
//setGraphic(add_symptom);
}
public String getName(){
return this.name;
}
public String getCategory(){
return this.category;
}
public void setName(String name){
this.name = name;
}
public void setCategory(String category){
this.category = category;
}
public void setSymptom(Button button){
symptom = button;
}
public Button getSymptom(){
return symptom;
}
public void setSymptomId(String symptomId){
this.symptomId = symptomId;
}
public String getSymptomId(){
return this.symptomId;
}
}
I've given 3 columns to the TableView. Name,Category and an action column where the symptom button appears to perform a certain action.
TableView
This is my FXML Controller.
package javafxapplication4;
import java.net.URL;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import java.util.ResourceBundle;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
public class Symptom_DataController implements Initializable {
/**
* Initializes the controller class.
*/
#FXML
private TableView<Symptom> symptomsTable;
#FXML
private TableColumn<Symptom,String> nameColumn;
#FXML
private TableColumn<Symptom,String> categoryColumn;
#FXML
private TableColumn<Symptom,String> actionColumn;
#FXML
private Button cancel;
#FXML
private Button diagnose;
public LoginModel loginModelSymptomsTable = new LoginModel();
#FXML
private void cancelAction(ActionEvent e) throws Exception{
Stage stage;
Scene scene;
Parent root;
if ( e.getSource() == cancel ) {
stage = (Stage) cancel.getScene().getWindow();
root = FXMLLoader.load(getClass().getResource("Menu.fxml"));
scene = new Scene(root);
stage.setX(0);
stage.setY(0);
stage.setMinWidth(800);
stage.setMinHeight(600);
stage.setWidth(1024);
stage.setHeight(768);
stage.setScene(scene);
stage.show();
}
}
#Override
public void initialize(URL url, ResourceBundle rb) {
nameColumn.setCellValueFactory(new PropertyValueFactory<>("Name"));
categoryColumn.setCellValueFactory(new PropertyValueFactory<>("Category"));
actionColumn.setCellValueFactory(new PropertyValueFactory<Symptom,String>("symptom"));
symptomsTable.setItems(loginModelSymptomsTable.selectSymptomValue());
}
}
Using an ObservableList I fill the TableView. Now i want to create an action for every button according to the row it's placed in the TableView. I can perform an action to the Button as long as I've selected a row in the TableView (cause that gives me access to the Symptom object). How can I perform an action with the button just by clicking on it and without selecting a row?
P.S: Sorry for my bad English. If this is a duplicate post, please direct me to the right way of doing this.
The button should not be part of the model class Symptom: instead you should create a TableCell that displays the button.
So the table setup should be something like:
#FXML
private TableView<Symptom> symptomsTable;
#FXML
private TableColumn<Symptom,String> nameColumn;
#FXML
private TableColumn<Symptom,String> categoryColumn;
// value for the action column is just going to be the entire symptom,
// so the type of the column is TableColumn<Symptom, Symptom>
#FXML
private TableColumn<Symptom,Symptom> actionColumn;
#Override
public void initialize(URL url, ResourceBundle rb) {
nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
categoryColumn.setCellValueFactory(new PropertyValueFactory<>("category"));
// just provide the entire row as the value for cells in the actionColumn:
actionColumn.setCellValueFactory(cellData -> new SimpleObjectProperty<>(cellData.getValue()));
// cell factory which provides cell which display a button:
actionColumn.setCellFactory(column -> new TableCell<Symptom, Symptom>() {
private final Button button = new Button("Select Symptom");
{
button.setOnAction(e -> {
Symptom symptom = getItem();
// do whatever you need with symptom..
});
}
#Override
protected void updateItem(Symptom item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setGraphic(null);
} else {
setGraphic(button);
}
}
});
symptomsTable.setItems(loginModelSymptomsTable.selectSymptomValue());
}
And then just remove the button entirely from the Symptom class.