I'm writing a simple quiz-game application using JavaFx. I want set text on label and buttons with data from a list and table.
I created three class: Main, Controller and Quiz.fxml. I have problem with communication classes and method.
In 'Main.java' I created object "Controller controller = new Controller()" and I call method "controller.setText" from 'Controller.java' class.
Now, I have a few options:
-if declate list(ArrayList questions) and tab(String[][] answers) in constuctor 'public Controller(){}' app doesnt work, I get error in this line " "
-if declare everything in 'Text()' method(list, tab and setting text on label) application run but values buttons and label are not changed
-if I declare list and tab in 'setData()', and next I want to change the buttons and label value from 'Text()' method, I cant see the list and I have to declare the same list 'questions' in 'Text()'
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
try {
Parent root = FXMLLoader.load(getClass().getResource("/Controller/Quiz.fxml"));
Scene scene = new Scene(root,500,400)
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
Controller controller = new Controller();
controller.Text();}
Controller class:
public class Controller {
#FXML private Label questionLabel;
#FXML private RadioButton answer_btn1;
#FXML private RadioButton answer_btn2;
#FXML private RadioButton answer_btn3;
#FXML private RadioButton answer_btn4;
#FXML private Button nextBtn;
public void Text() {
List <String> questions = new ArrayList<String>();
questions.add("pytanie1");
questions.add("pytanie2");
questions.add("pytanie3");
questions.add("pytanie4");
questions.add("pytanie5");
questions.add("pytanie6");
questions.add("pytanie7");
questions.add("pytanie8");
questions.add("pytanie9");
questions.add("pytanie10");
String[][] answers = new String[1][4];
answers[1][1] = "a) odp";
answers[1][2] = "b) odp";
answers[1][3] = "c) odp";
answers[1][4] = "d) odp";
questionLabel.setText("");
questionLabel.setText(questionLabel.getText()+answers[1][1]);
answer_btn1.setText("aaa");
}
What do I have to do to change name buttons and label?
You have a couple problems with your code.
Array index starts at zero and if necessary should end at arrayLength - 1. Your code: answers[1][1] = "a) odp"; ... answers[1][4] = "d) odp";. What it should be: answers[0][0] = "a) odp"; ... answers[0][3] = "d) odp";
Initialzing the Controller should be done in the Controller's initialize method.
The sample code below:
Main:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
*
* #author blj0011
*/
public class JavaFXApplication226 extends Application
{
#Override
public void start(Stage stage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("Controller/Quiz.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
Controller:
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
/**
*
* #author blj0011
*/
public class FXMLDocumentController implements Initializable
{
#FXML
private Label questionLabel;
#FXML
private RadioButton answer_btn1;
#FXML
private RadioButton answer_btn2;
#FXML
private RadioButton answer_btn3;
#FXML
private RadioButton answer_btn4;
#FXML
private Button nextBtn;
#Override
public void initialize(URL url, ResourceBundle rb)
{
// TODO
List<String> questions = new ArrayList();
questions.add("pytanie1");
questions.add("pytanie2");
questions.add("pytanie3");
questions.add("pytanie4");
questions.add("pytanie5");
questions.add("pytanie6");
questions.add("pytanie7");
questions.add("pytanie8");
questions.add("pytanie9");
questions.add("pytanie10");
String[][] answers = new String[1][4];
answers[0][0] = "a) odp";
answers[0][1] = "b) odp";
answers[0][2] = "c) odp";
answers[0][3] = "d) odp";
questionLabel.setText("");
questionLabel.setText(questionLabel.getText() + answers[0][0]);
answer_btn1.setText("aaa");
}
}
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication226.FXMLDocumentController">
<children>
<Button fx:id="nextBtn" layoutX="118.0" layoutY="161.0" text="Click Me!" />
<Label fx:id="questionLabel" layoutX="124.0" layoutY="36.0" minHeight="16" minWidth="69" />
<RadioButton fx:id="answer_btn1" layoutX="47.0" layoutY="92.0" mnemonicParsing="false" text="RadioButton" />
<RadioButton fx:id="answer_btn2" layoutX="193.0" layoutY="92.0" mnemonicParsing="false" text="RadioButton" />
<RadioButton fx:id="answer_btn3" layoutX="47.0" layoutY="128.0" mnemonicParsing="false" text="RadioButton" />
<RadioButton fx:id="answer_btn4" layoutX="183.0" layoutY="128.0" mnemonicParsing="false" text="RadioButton" />
</children>
</AnchorPane>
I am assuming your project structure looks like the following from your code.
Your issue is that you are instantiating a new controller which is not bound to the view.
You get the controller from the loader like this:
FXMLLoader loader = new FXMLLoader(getClass().getResource("/Controller/Quiz.fxml"));
Parent root = loader.load();
Controller controller = loader.getController();
This way everything will be wired up properly.
Related
I'm working on a project and I need to display the information of a Medicine object from a ListView to another Scene.
The user would select a Medicine from the ListView and press the Button to see it's details in the next Scene that would be displayed by Labels. The problem now is, I have transferred the info, and the text property of the Label has changed (observed through println and debugging), but the Label just won't display the changed text.
this is the main
package app;
import app.data.MedicineData;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
import org.w3c.dom.events.Event;
import java.io.IOException;
public class Pharmachine extends Application {
public static final double WIDTH = 480;
public static final double HEIGHT = 720;
#Override
public void start(Stage stage) throws Exception{
Parent homePage = FXMLLoader.load(getClass().getResource("homepage.fxml"));
Scene homePageScene = newDefaultScene(homePage);
stage.setResizable(false);
stage.setScene(homePageScene);
stage.setTitle("Pharmachine");
stage.show();
}
#Override
public void init(){
MedicineData.getInstance().loadData();
}
public static Scene newDefaultScene(Parent parent){
Scene scene = new Scene(parent, WIDTH, HEIGHT);
scene.addEventHandler(MouseEvent.MOUSE_CLICKED,
(mouseEvent) -> {
Node targetNode = scene.getFocusOwner();
if(targetNode != null && targetNode.isFocused()) {
if(targetNode.getParent() != null)
targetNode.getParent().requestFocus();
mouseEvent.consume();
}
}
);
return scene;
}
public static void navigateTo(ActionEvent actionEvent, String filename){
Stage mainWindow = (Stage) ((Node) actionEvent.getSource()).getScene().getWindow();
Scene currentScene = ((Node) actionEvent.getSource()).getScene();
try {
currentScene.setRoot(FXMLLoader.load(Pharmachine.class.getResource(filename)));
mainWindow.setScene(currentScene);
} catch(IOException e){
e.printStackTrace();
}
}
public static void main(String[] args){ launch(args); }
}
this is the fxml for the list's page
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ListView?>
<BorderPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
fx:controller="app.ListpageController" fx:id="pageRoot"
stylesheets="#styles/main.css">
<left>
<GridPane fx:id="listArea" alignment="CENTER">
<ListView fx:id="medicineList"
GridPane.rowIndex="1"/>
</GridPane>
</left>
<center>
<GridPane fx:id="buttonsArea" alignment="CENTER"
hgap="10" vgap="10">
<Button text="Back"
onAction="#displayHomePage"
GridPane.halignment="CENTER"/>
<Button text="Details"
onAction="#displayDetailsPage"
GridPane.rowIndex="1"
GridPane.halignment="CENTER"/>
</GridPane>
</center>
</BorderPane>
here is the controller for the list's page
package app;
import app.data.Medicine;
import app.data.MedicineData;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.util.Callback;
import java.io.IOException;
public class ListpageController {
#FXML
private BorderPane pageRoot;
#FXML
private GridPane listArea;
#FXML
private GridPane buttonsArea;
#FXML
private ListView<Medicine> medicineList;
#FXML public void initialize(){
listArea.setMaxWidth(Pharmachine.WIDTH * 3/5);
medicineList.setMinWidth(listArea.getMaxWidth());
medicineList.setMinHeight(Pharmachine.HEIGHT);
medicineList.setItems(MedicineData.getInstance().getMedicines());
medicineList.setCellFactory(new Callback<>() {
#Override
public ListCell<Medicine> call(ListView<Medicine> medicineListView) {
ListCell<Medicine> listCell = new ListCell<>(){
#Override
public void updateItem(Medicine medicine, boolean empty){
super.updateItem(medicine, empty);
if(!empty){
HBox medName = new HBox(new Label(medicine.getName()));
HBox.setHgrow(medName, Priority.ALWAYS);
HBox medPrice = new HBox(new Label(String.format("RM%.2f", medicine.getPrice())));
HBox medLabel = new HBox(medName, medPrice);
setGraphic(medLabel);
}
}
};
listCell.setOnMouseClicked(
(mouseEvent) -> {
if(mouseEvent.getClickCount() == 2){
medicineList.getSelectionModel().clearSelection();
}
}
);
return listCell;
}
});
}
#FXML private void displayHomePage(ActionEvent actionEvent){
Pharmachine.navigateTo(actionEvent, "homepage.fxml");
}
#FXML private void displayDetailsPage(ActionEvent actionEvent){
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("detailspage.fxml"));
try {
loader.load();
} catch(IOException e){
e.printStackTrace();
}
DetailspageController controller = loader.getController();
Medicine selectedMedicine = medicineList.getSelectionModel().getSelectedItem();
if(selectedMedicine != null) {
controller.setInfo(selectedMedicine);
Pharmachine.navigateTo(actionEvent, "detailspage.fxml");
}
}
}
this is the fxml for the next scene (just simple for now)
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.text.Text?>
<BorderPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
fx:id="pageRoot" fx:controller="app.DetailspageController"
stylesheets="#styles/main.css">
<center>
<GridPane fx:id="detailsArea"
alignment="CENTER" hgap="10" vgap="10">
<Label text="Details"/>
<Text text="Name: "
GridPane.rowIndex="1"/>
<Label fx:id="medNameLabel" text="~"
GridPane.rowIndex="1" GridPane.columnIndex="1"/>
<Text text="Price: "
GridPane.rowIndex="2"/>
<Label fx:id="medPriceLabel" text="~"
GridPane.rowIndex="2" GridPane.columnIndex="1"/>
</GridPane>
</center>
</BorderPane>
and this is the scene's controller
package app;
import app.data.Medicine;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import java.net.URL;
import java.util.ResourceBundle;
public class DetailspageController implements Initializable {
private Medicine selectedMedicine;
#FXML
private BorderPane pageRoot;
#FXML
private GridPane detailsArea;
#FXML
private Label medNameLabel;
#FXML
private Label medPriceLabel;
#Override
public void initialize(URL url, ResourceBundle rb){
}
public void setInfo(Medicine medicine){
selectedMedicine = medicine;
medNameLabel.setText(selectedMedicine.getName());
medPriceLabel.setText(String.format("RM%.2f", selectedMedicine.getPrice()));
System.out.println(medNameLabel.textProperty());
System.out.println(medNameLabel.getText());
medNameLabel.setStyle("-fx-background-color: red;");
}
}
The help would mean a lot. Thank you :]
In your displayDetailsPage method, you load a scene from detailspage.fxml, and you update its labels by calling the setInfo method of the controller.
Then, you call Pharmachine.navigateTo, which loads detailspage.xml again and replaces the scene root with the newly loaded root. You updated the text of the labels in the first details page, but the second details page is brand new, so it doesn’t have those changes.
The second argument in the navigateTo method should be of type Parent rather than a String. navigateTo should not attempt to load any .fxml file; let that be the caller’s responsibility.
Side note: When a list has multiple attributes for each data item, you should probably use a TableView rather than a ListView.
I'm working on a larger project (together with quite a number of fellow students), and I'm trying to display an arrayList (which type we're using over and over again in our project, so I wouldn't love to change all these to ObservableLists just for my rather small issue) in a TableView.
I've created a smaller project just in order to get to the heart of the problem and to show you:
In the class editMyGrades (together with an fxml file of the same name) I want to display my SUBJECTs (-> String subjectName), each one with its GRADE (-> int grade) in a TableView.
Initializing the parent root and the tableView the ArrayList subjects is transformed to an ObservableList, which is loaded into the TableView.
I really would like to understand theoretically, why changing GRADEs works with the tableView, but removing doesn't, and to know what to do about it practically ;-)
Thank you very much in advance!
Main class:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.util.ArrayList;
public class Main extends Application {
private static ArrayList<Subject> subjects = new ArrayList<>();
public static ArrayList<Subject> getSubjects () {return subjects;}
// private static ObservableList<Subject> subjects = FXCollections.observableArrayList();
// public static ObservableList<Subject> getSubjects () {return subjects;}
public static void main (String[] args) {
subjects.add(new Subject("computer science", 2));
subjects.add(new Subject("literature", 4));
launch (args);
}
public void start (Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/editMyGrades.fxml"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Class Subject:
public class Subject {
private String subjectName;
private int grade;
public Subject (String subjectName, int grade) {
this.subjectName = subjectName;
this.grade = grade;
}
public String getSubjectName () {return subjectName;}
public int getGrade () {return grade;}
public void setGrade (int grade) {this.grade = grade;}
}
controlling class editMyGrades:
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
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.input.MouseEvent;
import java.util.ArrayList;
public class editMyGrades {
Subject subjectToEdit;
#FXML
private TableView<Subject> tableView;
#FXML
private TableColumn<Subject, String> subjectCol;
#FXML
private TableColumn<Subject, Number> gradeCol;
#FXML
private TextField textField;
#FXML
private Button changeButton;
#FXML
private Button deleteButton;
#FXML
public void initialize() {
subjectCol.setCellValueFactory(new PropertyValueFactory<>("subjectName"));
gradeCol.setCellValueFactory(new PropertyValueFactory<>("grade"));
//tableView.setItems (FXCollections.observableArrayList(Main.getSubjects()));
ObservableList<Subject> subjects = FXCollections.observableArrayList(Main.getSubjects());
tableView.setItems(subjects);
}
#FXML
private void tableViewClicked(MouseEvent event) {
if (event.getClickCount() == 2) {
subjectToEdit = tableView.getSelectionModel().getSelectedItem();
textField.setText ("" + tableView.getSelectionModel().getSelectedItem().getGrade());
}
}
#FXML
private void change(ActionEvent event) {
subjectToEdit.setGrade(Integer.parseInt(textField.getText()));
textField.clear();
tableView.refresh();
}
#FXML
private void delete (ActionEvent event) {
Subject selectedSubject = tableView.getSelectionModel().getSelectedItem();
ArrayList<Subject> subjects = Main.getSubjects();
subjects.remove(selectedSubject);
tableView.refresh();
}
}
editingMyGrades.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="editMyGrades">
<children>
<Label text="change your grades!" />
<HBox>
<children>
<TableView fx:id="tableView" onMouseClicked="#tableViewClicked" prefHeight="200.0" prefWidth="518.0">
<columns>
<TableColumn fx:id="subjectCol" prefWidth="262.0" text="subject" />
<TableColumn fx:id="gradeCol" minWidth="0.0" prefWidth="146.0" text="grade" />
</columns>
</TableView>
<VBox alignment="CENTER">
<children>
<Button mnemonicParsing="false" onAction="#delete" text="deleteButton" />
</children>
</VBox>
</children>
</HBox>
<TextField fx:id="textField" maxWidth="100.0" />
<Button fx:id="changeButton" mnemonicParsing="false" onAction="#change" text="Change!" />
</children>
</VBox>
When working with JavaFX it's good practice to always use FXCollections.
When you make a modification to the ArrayList, the changes are not generating list change nofications. You must get the ObservableList from the tableview and perform the operations on it since it is the wrapper (the smart list) that is backed by your ArrayList.
Thank you indeed for your explanations! In fact in my real problem I'm using MVC, but the Controller object is located in the main - static -, and as it appears, it can't be done in other way. For the illustration of my problem I thought id wouldn't make a difference, if I had a static controller object with the list or just the list itself (perhaps I was wrong ...).
Now, having tried to apply everything you told me, it's just the other way around: the tableView displays removements, but no changes.
just the fxml control class EditMyGrades:
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
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.input.MouseEvent;
public class EditMyGrades {
private ObservableList<Subject> subjects = FXCollections.observableArrayList();
private Subject subjectToEdit;
#FXML
private TableView<Subject> tableView;
#FXML
private TableColumn<Subject, String> subjectCol;
#FXML
private TableColumn<Subject, Number> gradeCol;
#FXML
private TextField textField;
#FXML
private Button changeButton;
#FXML
private Button deleteButton;
#FXML
public void initialize() {
subjectCol.setCellValueFactory(new PropertyValueFactory<>("subjectName"));
gradeCol.setCellValueFactory(new PropertyValueFactory<>("grade"));
subjects.add(new Subject("computer science", 2));
subjects.add(new Subject("literature", 4));
tableView.setItems(subjects);
}
#FXML
private void tableViewClicked(MouseEvent event) {
if (event.getClickCount() == 2) {
subjectToEdit = tableView.getSelectionModel().getSelectedItem();
textField.setText ("" + tableView.getSelectionModel().getSelectedItem().getGrade());
}
}
#FXML
private void change(ActionEvent event) {
subjectToEdit.setGrade(Integer.parseInt(textField.getText()));
textField.clear();
}
#FXML
private void delete (ActionEvent event) {
ObservableList<Subject> subjects = tableView.getItems();
Subject selectedSubject = tableView.getSelectionModel().getSelectedItem();
subjects.remove(selectedSubject);
}
}
i created a simple GUI, using JavaFX with fxml. One part of the program is using fxml and other generate dynamic GUI. So, i created javafx.scene.controller.Accordion with fxml and add panes inside it with code:
#FXML
Accordion accordionPlant;
public void init(){
accordionPlant.getPanes().add(createTitledPanePlant(1));
accordionPlant.getPanes().add(createTitledPanePlant(2));
}
protected TitledPane createTitledPanePlant(int index){
TilePane tile = new TilePane(Orientation.HORIZONTAL, 5, 5);
Label typeLabel = new Label("Тип выпуска");
TextField typeText = new TextField();
VBox typeContainer = new VBox(typeLabel,typeText);
Label bankLabel = new Label("Берег");
Tooltip.install(bankLabel, new Tooltip("Берег, с которого производится выпуск"));
TextField bankText = new TextField();
VBox bankContainer = new VBox(bankLabel,bankText);
tile.getChildren().addAll(typeContainer, bankContainer);
TitledPane titledPane = new TitledPane("Параметры выпуска " + index, tile);
return titledPane;
}
After that user click on the button and the program should find all TextField and calculate their value. So how it must be done correctly or comfortable?
I tried to get all panes of accordion:
void test(){
for (TitledPane pane: accordionPlant.getPanes()) {
pane.getChildrenUnmodifiable().get(0); //blah, blah
}
}
but i think it isn't a good idea, especially using index. I need only textfields. Is there any way of implements of ?
If you have dynamic elements, why not just store them in a List when you create them? That way you don't have to try to get them from a container. You are probably only interested in the Controller, but I posted the whole app. If you look in createTitledPanePlant you can see where I add the TextFields to the List -> List<TextField> textFieldContainer = new ArrayList();
Main
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
*
* #author blj0011
*/
public class JavaFXApplication115 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();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
Controller
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Orientation;
import javafx.scene.control.Accordion;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TitledPane;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.TilePane;
import javafx.scene.layout.VBox;
/**
*
* #author blj0011
*/
public class FXMLDocumentController implements Initializable
{
#FXML
private Label label;
#FXML
private Accordion accordionMain;
List<TextField> textFieldContainer = new ArrayList();
#FXML
private void handleButtonAction(ActionEvent event)
{
//Sum TextField
double total = 0;
for (TextField node : textFieldContainer) {
double value;
try {
value = Double.parseDouble(node.getText());
}
catch (NumberFormatException e) {
value = 0;
}
total += value;
}
label.setText("Sum: " + total);
}
#Override
public void initialize(URL url, ResourceBundle rb)
{
// TODO
accordionMain.getPanes().add(createTitledPanePlant(1));
accordionMain.getPanes().add(createTitledPanePlant(2));
}
protected TitledPane createTitledPanePlant(int index)
{
TilePane tile = new TilePane(Orientation.HORIZONTAL, 5, 5);
Label typeLabel = new Label("Тип выпуска");
TextField typeText = new TextField();
textFieldContainer.add(typeText);//Add your textField to the container when you create it
VBox typeContainer = new VBox(typeLabel, typeText);
Label bankLabel = new Label("Берег");
Tooltip.install(bankLabel, new Tooltip("Берег, с которого производится выпуск"));
TextField bankText = new TextField();
textFieldContainer.add(bankText);//Add your textField to the container when you create it
VBox bankContainer = new VBox(bankLabel, bankText);
tile.getChildren().addAll(typeContainer, bankContainer);
TitledPane titledPane = new TitledPane("Параметры выпуска " + index, tile);
return titledPane;
}
}
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Accordion?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="AnchorPane" prefHeight="486.0" prefWidth="431.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.141" fx:controller="javafxapplication115.FXMLDocumentController">
<children>
<Button fx:id="button" layoutX="179.0" layoutY="437.0" onAction="#handleButtonAction" text="Sum nodes" />
<Label fx:id="label" layoutX="163.0" layoutY="37.0" minHeight="16" minWidth="69" prefHeight="17.0" prefWidth="106.0" />
<Accordion fx:id="accordionMain" layoutX="115.0" layoutY="72.0" prefHeight="310.0" prefWidth="202.0" />
</children>
</AnchorPane>
I am trying to populate a TableView from a method call on the click of a button from a Modal Window. I hope this is possible but I am not having any luck. The TableView is already created in an FXML file. Whenever I call upon the method, I receive a null pointer exception. Any ideas, or suggestions? Sorry if my question format is bad, I don't ask many questions.
MainController.java
#FXML TableView<Part> partsTableView;
#FXML ObservableList<Part> parts;
#FXML public TableColumn<Part, Integer> partIDColumn;
#FXML public TableColumn<Part, String> partNameColumn;
#FXML public TableColumn<Part, Integer> partILColumn;
#FXML public TableColumn<Part, Double> partPriceColumn;
#FXML
public void getPartData(){
partIDColumn.setCellValueFactory(new PropertyValueFactory<>("id")); (Line 112)
partNameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
partILColumn.setCellValueFactory(new PropertyValueFactory<>("instock"));
partPriceColumn.setCellValueFactory(new PropertyValueFactory<>("price"));
partsTableView.setItems(generateData());
}
private ObservableList<Part> generateData(){
parts = FXCollections.observableArrayList();
parts.add(new Part(0, "Part" , 1 , 25.00));
return parts;
}
main.fxml
<TableView fx:id="partsTableView" layoutX="13.0" layoutY="68.0" prefHeight="344.0" prefWidth="556.0">
<columns>
<TableColumn fx:id="partIDColumn" editable="false" prefWidth="138.0" resizable="false" text="Part ID" />
<TableColumn fx:id="partNameColumn" editable="false" prefWidth="139.0" resizable="false" text="Part Name" />
<TableColumn fx:id="partILColumn" editable="false" prefWidth="119.0" resizable="false" text="Inventory Level" />
<TableColumn fx:id="partPriceColumn" editable="false" prefWidth="159.0" resizable="false" text="Price / Cost Per Unit" />
</columns>
</TableView>
Error: Caused by: java.lang.NullPointerException
at ims.MainController.getPartData(MainController.java:112)
I am using IntelliJ and I build my stage / scene in Scene Builder. I apologize if this is really simple and I'm overlooking something, but, I am just having such a tough time figuring this out.
Thank you in advance for your time and assistance!
I have prepared a sample. You can follow its logic. Main point is to manage main dialog and modal dilog controllers. You can check DialogController dialogController = fxmlLoader.<DialogController>getController(); line in AppMainController.java. In this sample main controller (AppMainController) obtains dialog controller (DialogController) and sets its observablelist to dialog controller. Sample class and fxmls are below:
AppMain.java:
package populatetable;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class AppMain extends Application{
#Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("AppMain.fxml"));
Scene scene = new Scene(root, 500,500);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
AppMain.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane minHeight="500.0" minWidth="500.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.111" fx:controller="populatetable.AppMainController">
<children>
<TableView fx:id="tvData" layoutX="91.0" layoutY="159.0" prefHeight="300.0" prefWidth="300.0">
<columns>
<TableColumn fx:id="colId" prefWidth="75.0" text="ID" />
<TableColumn fx:id="colName" prefWidth="75.0" text="Name" />
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
<Button layoutX="211.0" layoutY="85.0" mnemonicParsing="false" onAction="#onOpenDialog" text="Open Dialog" />
</children>
</AnchorPane>
AppMainController.java
package populatetable;
import java.io.IOException;
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.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;
public class AppMainController implements Initializable {
#FXML
private TableView<Data> tvData;
#FXML
private TableColumn colId;
#FXML
private TableColumn colName;
private ObservableList<Data> tvObservableList = FXCollections.observableArrayList();
// Open dialog button click event
#FXML
void onOpenDialog(ActionEvent event) throws IOException {
System.out.println("onOpenDialog clicked");
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Dialog.fxml"));
Parent parent = fxmlLoader.load();
DialogController dialogController = fxmlLoader.<DialogController>getController();
dialogController.setAppMainObservableList(tvObservableList);
Scene scene = new Scene(parent, 300, 200);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
}
#Override
public void initialize(URL location, ResourceBundle resources) {
colId.setCellValueFactory(new PropertyValueFactory<>("id"));
colName.setCellValueFactory(new PropertyValueFactory<>("name"));
tvData.setItems(tvObservableList);
}
public ObservableList<Data> getTvObservableList() {
return tvObservableList;
}
}
DialogController.java
package populatetable;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
public class DialogController {
private ObservableList<Data> appMainObservableList;
//fill table button click event
#FXML
void fillTable(ActionEvent event) {
Data data = new Data(1, "Name1");
appMainObservableList.add(data);
}
public void setAppMainObservableList(ObservableList<Data> tvObservableList) {
this.appMainObservableList = tvObservableList;
}
}
Dialog.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane minHeight="200.0" minWidth="300.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.111" fx:controller="populatetable.DialogController">
<children>
<Button layoutX="115.0" layoutY="89.0" mnemonicParsing="false" onAction="#fillTable" text="Fill Table" />
</children>
</AnchorPane>
Data.java
package populatetable;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
public class Data {
private final SimpleIntegerProperty id;
private final SimpleStringProperty name;
public Data(int id, String name) {
this.id = new SimpleIntegerProperty(id);
this.name = new SimpleStringProperty(name);
}
public int getId() {
return id.get();
}
public void setId(int ID) {
id.set(ID);
}
public String getName() {
return name.get();
}
public void setName(String nme) {
name.set(nme);
}
#Override
public String toString() {
return "id: " + id.get() + " - " + "name: " + name.get();
}
}
Hope it is useful.
I am building a GUI for my project and I need to show information that is coming from various types of data structures: ArrayList,HashMap,TreeSet in a table.
Basically I need to show information from query methods I got in my model package in my view package.
My implementation right now includes a controller for each and every table I build with the object type specified every time I declare a TableView.
I wonder if there is a way to create a class that its constructor will build me the table I need(with as many columns this object require). some sort of a generic table builder that can read an object and find out how many columns it needs to be represented. for example: to present Reservation Object that has 4 fields/columns compare to Member object that has 6 fields/columns. according to the data-structure it will get. therefore I will be able to create an instance of this class and instantiate a table of my need and show it on the screen.
I also need a way to be able to control it from the scene builder as this is my main tool for building the GUI graphically.
I am adding one of my table codes here and I hope someone can help me with this tedious task :)
package view;
import java.util.ArrayList;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import model.Reservation;
public class QRYClientReservationsTableController {
private ObservableList<Reservation> ReservationsData = FXCollections.observableArrayList();
#FXML
private TableView<Reservation> ReservationforClient;
#FXML
private TableColumn<Reservation, String> ReservationIdColumn;
#FXML
private TableColumn<Reservation, String> LocationIdColumn;
#FXML
private TableColumn<Reservation, String> AddressColumn;
#FXML
private TableColumn<Reservation, String> DescriptionColumn;
#FXML
private TableColumn<Reservation, String> StartTimeAndDateColumn;
#FXML
private TableColumn<Reservation, String> EndTimeAndDateColumn;
#FXML
private TextField memId;
/**
* The constructor.
* The constructor is called before the initialize() method.
*/
public QRYClientReservationsTableController() {
}
/**
* Initializes the controller class. This method is automatically called
* after the fxml file has been loaded.
*/
#FXML
private void initialize() {
// Initialize the Location table with the tree columns.
ReservationIdColumn.setCellValueFactory(cellData -> cellData.getValue().ReservationIdProperty());
LocationIdColumn.setCellValueFactory(cellData -> cellData.getValue().LocationIdProperty());
AddressColumn.setCellValueFactory(cellData -> cellData.getValue().LocationAddressProperty());
DescriptionColumn.setCellValueFactory(cellData -> cellData.getValue().LocationDescriptionProperty());
StartTimeAndDateColumn.setCellValueFactory(cellData -> cellData.getValue().StartDateProperty());
EndTimeAndDateColumn.setCellValueFactory(cellData -> cellData.getValue().EndDateProperty());
}
#FXML
Button CloseBtn = new Button();//close button inside AddLocation window
#FXML
public void handleCloseButtonAction(ActionEvent event) {//for all close Button's this method is called
Stage stage = (Stage) CloseBtn.getScene().getWindow();
stage.close();
}
/**
* Is called by the main application to give a reference back to itself.
*
* #param Locaion
*/
public void setQRYClientReservationsTableController(String memId) {
this.memId.setEditable(true);
this.memId.setText(memId);
this.memId.setEditable(false);
ArrayList<Reservation> reservationsAdd = new ArrayList<Reservation>();
reservationsAdd.addAll(ViewLogic.controlLogic.Q_getAllReservationsForMember(memId));
ReservationsData.addAll(reservationsAdd);
// Add observable list data to the table
ReservationforClient.setItems(ReservationsData);
ReservationIdColumn.sortableProperty().setValue(false);
LocationIdColumn.sortableProperty().setValue(false);
AddressColumn.sortableProperty().setValue(false);
DescriptionColumn.sortableProperty().setValue(false);
StartTimeAndDateColumn.sortableProperty().setValue(false);
EndTimeAndDateColumn.sortableProperty().setValue(false);
}
}
The next code is in my ViewLogic Class inside my View package and this code summons the FXML file that loads up a small window to choose a member Id, by this Id I am getting the info to build the table on the code above.
the code for this method in ViewLogic is as follows:
#FXML
Button ShowReservationsForClientBtn= new Button();/*Code for pressing the get reservations for client button to open window after pressing the button in Queries*/
#FXML
public void pressShowReservationsForClientBtn(ActionEvent event) throws Exception {
try {
FXMLLoader fxmlLoader = new FXMLLoader(ChooseAClientForReservationsQueryWindowController.class.getResource("/view/ChooseAClientForReservationsQueryWindow.fxml"));
AnchorPane chooseMemberIdForQuery = (AnchorPane) fxmlLoader.load();
ChooseAClientForReservationsQueryWindowController controller = fxmlLoader.getController();
controller.setAddTripToReservationClass();
Stage stage = new Stage();
stage.setTitle("Please Choose a Member Id");
stage.setScene(new Scene(chooseMemberIdForQuery));
stage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
It then calls this class where you choose your member and then the table code above is initiated, the mini window code is:
package view;
import java.util.ArrayList;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class ChooseAClientForReservationsQueryWindowController {
private String memIdChosen;
#FXML
private ComboBox<String> MemberIds;
public void initialize() {
}
#FXML
Button CloseBtn = new Button();//close button inside AddLocation window
#FXML
public void handleCloseButtonAction(ActionEvent event) {//for all close Button's this method is called
Stage stage = (Stage) CloseBtn.getScene().getWindow();
stage.close();
}
#FXML
Button EraseBtn = new Button();//erase Button
#FXML
public void handleEraseButtonAction(ActionEvent event) {//erase Button code
MemberIds.setValue(null);
}
#FXML
Button GoBtn = new Button();//Go button
#FXML
public void handleGoBtn(ActionEvent event) throws Exception {//for all close Button's this method is called
try{
if(MemberIds.getValue()==null)
throw new Exception();
FXMLLoader fxmlLoader = new FXMLLoader(QRYClientReservationsTableController.class.getResource("/view/QRYClientReservationsTableController.fxml"));
AnchorPane qryShowAllReservationsForMember = (AnchorPane) fxmlLoader.load();
QRYClientReservationsTableController controller = fxmlLoader.getController();
controller.setQRYClientReservationsTableController(memIdChosen);
Stage stage = new Stage();
stage.setTitle("Query - Show all reservations for member Id: "+memIdChosen+".");
stage.setScene(new Scene(qryShowAllReservationsForMember));
stage.show();
handleCloseButtonAction(event);
}
catch (Exception e){
Alert alert = new Alert(AlertType.WARNING,"One of the fields is empty", ButtonType.OK);
alert.setTitle("One of the fields is empty");
alert.setHeaderText("One of the fields is empty");
alert.setContentText("Please fill the empty fields");
alert.showAndWait();
}
}
/**
* The constructor.
* The constructor is called before the initialize() method.
*/
public ChooseAClientForReservationsQueryWindowController() {
}
public void setAddTripToReservationClass(){
ArrayList<String> memIds = new ArrayList<String>();
memIds.addAll(ViewLogic.controlLogic.getMembers().keySet());
MemberIds.getItems().setAll(memIds);
MemberIds.valueProperty().addListener(new ChangeListener<String>(){
#Override
public void changed(ObservableValue<? extends String> arg0, String arg1, String arg2) {
memIdChosen=arg2;
}
});
}
}
That's the relevant FXML file for the table code above if it helps:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="1450.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="view.QRYClientReservationsTableController">
<children>
<ButtonBar layoutX="44.0" layoutY="541.0" prefHeight="40.0" prefWidth="700.0" AnchorPane.bottomAnchor="19.0">
<buttons>
<Button fx:id="CloseBtn" mnemonicParsing="false" onAction="#handleCloseButtonAction" prefHeight="31.0" prefWidth="212.0" text="Close" />
</buttons>
</ButtonBar>
<TableView fx:id="ReservationforClient" layoutX="5.0" layoutY="55.0" prefHeight="486.0" prefWidth="893.0" AnchorPane.bottomAnchor="59.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="2.0" AnchorPane.topAnchor="55.0">
<columns>
<TableColumn fx:id="ReservationIdColumn" prefWidth="121.0" text="Reservation Id" />
<TableColumn fx:id="LocationIdColumn" prefWidth="128.0" text="Location Id" />
<TableColumn fx:id="AddressColumn" prefWidth="276.0" text="Address" />
<TableColumn fx:id="DescriptionColumn" prefWidth="382.0" text="Description" />
<TableColumn fx:id="StartTimeAndDateColumn" prefWidth="282.0" text="Start Time and Date" />
<TableColumn fx:id="EndTimeAndDateColumn" prefWidth="252.0" text="EndTime and Date" />
</columns>
</TableView>
<Label layoutX="394.0" layoutY="8.0" prefHeight="40.0" prefWidth="350.0" text="Reservations for Client Id:" AnchorPane.leftAnchor="394.0" AnchorPane.rightAnchor="706.0" AnchorPane.topAnchor="8.0">
<font>
<Font name="System Bold" size="28.0" />
</font>
</Label>
<TextField fx:id="memId" editable="false" layoutX="738.0" layoutY="10.0" prefHeight="40.0" prefWidth="191.0" />
</children>
</AnchorPane>
Thank you
Tom
I stumbled across the similar problem and have created a simple library that solves it.
It uses your model class to build a TableView that represents the fields of the given class.
I've published it on GitHub with a sample and instruction.
If you have any suggestions, I would like to hear from you.
You can do it through reflection. This will make an editable String column for each SimpleStringProperty field. Doing it like this means you can't add them in FXML.
for (Field f : TableInfo.class.getDeclaredFields()) {
if (f.getType().equals(SimpleStringProperty.class)){
TableColumn<TableInfo, String> tc = new TableColumn<>(f.getName());
tv.getColumns().add(tc);
tc.setCellValueFactory(new PropertyValueFactory<>(f.getName()));
tc.setCellFactory(TextFieldTableCell.forTableColumn());
}//else if more field types...
}