At this time I learned how to make a program with javafx fxml application. I find out how to display listcell on listview. I use the code below. But from the code, it is not able to display listcell on the listview. When I run the program just show listview and listcell does not appear.
please help me.
Main.java
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("Main.fxml"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
Main.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
<children>
<ListView fx:id="listView" layoutX="67.0" layoutY="29.0" prefHeight="320.0" prefWidth="376.0" />
</children>
</AnchorPane>
Student.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package belajarlistview;
/**
*
* #author kuupie
*/
public class Student {
private static int studentIdAct = 0;
private int studentId;
private String name;
private GENDER gender;
enum GENDER {
MALE,
FEMALE
}
public Student(String name, GENDER gender) {
studentId = studentIdAct++;
this.name = name;
this.gender = gender;
}
public int getStudentId() {
return studentId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public GENDER getGender() {
return gender;
}
public void setGender(GENDER gender) {
this.gender = gender;
}
}
ListCell.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<GridPane fx:id="listCellDetail" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="39.0" prefWidth="421.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
<columnConstraints>
<ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Label fx:id="label1" alignment="CENTER" prefHeight="36.0" prefWidth="137.0" text="Label">
<GridPane.margin>
<Insets left="10.0" />
</GridPane.margin>
</Label>
<Label fx:id="label2" alignment="CENTER" prefHeight="36.0" prefWidth="137.0" text="Label" GridPane.columnIndex="2" />
<FontAwesomeIconView fx:id="fxIconGender" strokeLineCap="ROUND" strokeLineJoin="ROUND" GridPane.columnIndex="1" />
</children>
</GridPane>
StudentListViewCell.java
import java.io.IOException;
/**
* Created by Johannes on 23.05.16.
*
*/
public class StudentListViewCell extends ListCell<Student> {
#FXML
private Label label1;
#FXML
private Label label2;
#FXML
private FontAwesomeIconView fxIconGender;
#FXML
private GridPane gridPane;
private FXMLLoader mLLoader;
#Override
protected void updateItem(Student student, boolean empty) {
super.updateItem(student, empty);
mLLoader = new FXMLLoader(getClass().getResource("/fxml/ListCell.fxml"));
mLLoader.setController(this);
try {
mLLoader.load();
} catch (IOException e) {
e.printStackTrace();
}
label1.setText(String.valueOf(student.getStudentId()));
label2.setText(student.getName());
if(student.getGender().equals(Student.GENDER.MALE)) {
fxIconGender.setIcon(FontAwesomeIcon.MARS);
} else if(student.getGender().equals(Student.GENDER.FEMALE)) {
fxIconGender.setIcon(FontAwesomeIcon.VENUS);
} else {
fxIconGender.setIcon(FontAwesomeIcon.GENDERLESS);
}
setText(null);
setGraphic(gridPane);
}
}
Controller.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package belajarlistview;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.util.Callback;
import java.net.URL;
import java.util.ResourceBundle;
public class Controller implements Initializable {
#FXML
private ListView<Student> listView;
private final ObservableList<Student> studentObservableList;
public Controller() {
studentObservableList = FXCollections.observableArrayList();
//add some Students
studentObservableList.addAll(
new Student("John Doe", Student.GENDER.MALE),
new Student("Jane Doe", Student.GENDER.FEMALE),
new Student("Donte Dunigan", Student.GENDER.MALE),
new Student("Gavin Genna", Student.GENDER.MALE),
new Student("Darin Dear", Student.GENDER.MALE),
new Student("Pura Petty", Student.GENDER.FEMALE),
new Student("Herma Hines", Student.GENDER.FEMALE)
);
}
#Override
public void initialize(URL location, ResourceBundle resources) {
listView.setItems(studentObservableList);
listView.setCellFactory(studentListView -> new StudentListViewCell());
}
}
I tried alot of code to solve it, by adding
this code setGraphic(student == null ? null : gridPane); on StudentListViewCell.java and
adding
this code fx:controller="belajarlistview.Controller" on Main.fxml
But i have got some error like this :
error 1
error
please help me
Okay
I got the problem. The main problem on this code mLLoader = new FXMLLoader(getClass().getResource("/fxml/ListCell.fxml")); just change this one ("/fxml/ListCell.fxml") to ("ListCell.fxml") the program runs well. Thanks so much #fabian
Related
Overview
I am working on a simple JavaFX application with multiple scenes using the MVC Architectural pattern. I have the design in place but I am having trouble persisting the User model across the different scenes. At the moment I do not have any backend. I am looking to just instantiate the User model when the application starts and display that single instance across my application. Any changes to that user model will up updated across the scenes. I am not looking to persist after the application closes.
I believe the issue arises from a design choice I had early on in development. I use a Controller named SceneNavigatorControl that calls the class Navigator which handles the presentation of different scenes in the application. These scenes are .fxml files.
SceneNavigatorControl
public class SceneNavigatorControl implements Initializable {
#FXML
private BorderPane mainStage;
#FXML
private void displayHomeScene() {
Navigator object = new Navigator();
Pane view = object.getScene("Home");
handleButtonChange("Home");
mainStage.setCenter(view);
}
#FXML
private void displayProfileScene() {
Navigator object = new Navigator();
Pane view = object.getScene("Profile");
handleButtonChange("Profile");
mainStage.setCenter(view);
}
#Override
public void initialize(URL location, ResourceBundle resources) {
// Called when the application starts, should this be where I instantiate User?
//User user = new User();
//user.setName("Bob");
displayHomeScene();
}
}
The above code handles which scene to take the user to. Depending on which button is tapped. One of the above functions is called and the scene is displayed by using the Navigator Class. I would think this is where I instantiate User and pass it to all other controllers
Navigator Class
public class Navigator {
private Pane view;
public Pane getScene(String fileName) {
try {
URL fileUrl = ActivityTracker.class.getResource("/ActivityTracker/Views/" + fileName + ".fxml");
if (fileUrl == null) {
throw new java.io.FileNotFoundException("FXML File cannot be found");
}
view = new FXMLLoader().load(fileUrl);
} catch (Exception e) {
System.out.print("No page " + fileName + " please check FXMLLoader");
}
return view;
}
}
The navigator class simple checks if a .fxml file with a given name exists. If it does, it replaces the current scene on the stage with the one given.
HomeView
<AnchorPane fx:id="Home" prefHeight="500.0" prefWidth="500.0" xmlns="http://javafx.com/javafx/8.0.172-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ActivityTracker.Controllers.HomeController">
<children>
<Label fx:id="clockLabel" layoutX="90.0" layoutY="32.0" textFill="WHITE" AnchorPane.leftAnchor="90.0" AnchorPane.topAnchor="32.0">
<font>
<Font name="Lucida Grande" size="100.0" />
</font></Label>
<Pane layoutX="410.0" layoutY="-1.0" prefHeight="500.0" prefWidth="90.0" style="-fx-background-color: #1d1d1d;" />
</children>
This is our view which is created using an .fxml file. On the first line is where we declare what view is associated with what controller.
HomeController
public class HomeController implements Initializable {
#FXML
private Pane Home;
#Override
public void initialize(URL location, ResourceBundle resources) {
// Print users details
}
}
ActivityTracker (Main)
public class ActivityTracker extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
try {
Parent root = FXMLLoader.load(ActivityTracker.class.getResource("Views/Stage.fxml"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setResizable(false);
primaryStage.show();
} catch (Exception ex) {
Logger.getLogger(ActivityTracker.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
User Model
public class User {
// Define all user stat variables for the model
private String userName;
/**
* Method that returns the users name
* #return users name of type String
*/
public String getUsersName() {
return userName;
}
/**
* Method that takes in a string value and sets it as the current users name
* #param name the current users weight
*/
public void setUsersName(String name) {
this.userName = name;
}
}
The following is an mre of setting one model instance to two controllers.
Define a simple model to be used by the two controllers:
class User {
private final String fName, lName;
public User(String fName, String lName) {
this.fName = fName;
this.lName = lName;
}
public String getFirstName() {
return fName;
}
public String getLastName() {
return lName;
}
}
Define two very basic fxml views, each with a controller:
FirstName.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.text.Font?>
<HBox alignment="CENTER_LEFT" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
prefHeight="30.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="fx_tests.FNameController">
<children>
<Label text="First Name: " HBox.hgrow="NEVER">
<font>
<Font size="16.0" />
</font>
</Label>
<Label fx:id="fName" text=""-"">
<font>
<Font size="16.0" />
</font>
</Label>
</children>
</HBox>
LastName.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.text.Font?>
<HBox alignment="CENTER_LEFT" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
prefHeight="30.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="fx_tests.LNameController">
<children>
<Label text="Last Name: " HBox.hgrow="NEVER">
<font>
<Font size="16.0" />
</font>
</Label>
<Label fx:id="lName" text=""-"">
<font>
<Font size="16.0" />
</font>
</Label>
</children>
</HBox>
Define a simple interface:
interface Controller {
void setModel(User model);
}
And have the two controllers implement it:
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class FNameController implements Controller{
#FXML
private Label fName;
#Override
public void setModel(User model) {
fName.setText(model.getFirstName());
}
}
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class LNameController implements Controller{
#FXML
private Label lName;
#Override
public void setModel(User model) {
lName.setText(model.getLastName());
}
}
Define the main view that is the parent of the FirstName.fxml and LastName.fxml:
Main.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?>
<BorderPane xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="fx_tests.MainController">
<top>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="User Info" textAlignment="CENTER" wrappingWidth="250" BorderPane.alignment="CENTER">
<font>
<Font size="20.0" />
</font>
</Text>
</top>
<center>
<VBox fx:id="infoPane" alignment="CENTER" spacing="10.0" BorderPane.alignment="CENTER">
<BorderPane.margin>
<Insets left="20.0" />
</BorderPane.margin>
</VBox>
</center>
</BorderPane>
The controller of the main view sets the model to the two controllers:
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
public class MainController {
#FXML
private VBox infoPane;
#FXML
void initialize() throws IOException{
try {
User model = new User("Ann","Davis"); //initialize model
FXMLLoader loader = new FXMLLoader(getClass().getResource("FirstName.fxml"));
Pane fName = loader.load();
Controller controller = loader.getController();
controller.setModel(model); //set model to first name controller
loader = new FXMLLoader(getClass().getResource("LastName.fxml"));
Pane lName = loader.load();
controller = loader.getController();
controller.setModel(model); //set model to last name controller
infoPane.getChildren().addAll(fName, lName);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
Test it all using:
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class FxmlTest extends Application {
#Override
public void start(Stage primaryStage) throws IOException {
Pane root = FXMLLoader.load(getClass().getResource("Main.fxml"));
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(null);
}
}
I am trying to create a multiple choice movie game where a movie appears in the label and the actor matched with that movie and 3 randoms are set as the radio buttons. Every example or tutorials I have found online show me how to use JavaFx and Scene Builder without implementing any classes or objects already written. I have a class called MovieSet that takes in an Arraylist of movies, and I do the same with the actors. However all the tutorials online never say if its possible to set the labels and radio buttons to these arraylist.
Is it possible to do this? and if so how would this be done?
I have used a for loop to iterate over the movieList, but it is telling me it cannot find symbol for movieList
public static void displayMovies(ArrayList<Movie> movieList) {
for (int x = 0; x < movieList.size(); x++) {
Movie movie = movieList.get(x);
System.out.printf("%s", movie.toString());
}
}
This is my controller. I do apologize. This is my first time using JavaFX and
public class FXMLDocumentController implements Initializable {
#FXML
private Label movielabel;
#FXML
private void handleButtonAction(ActionEvent event) {
}
#Override
public void initialize(URL url, ResourceBundle rb) {
MovieSet movie = new MovieSet();
this.movielabel.getLabelFor(MovieSet.displayMovies(movieList));
}
}
This sample app is a general way of handling a multiple choice question and answer app. Comments in code.
Main Class
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
*
* #author blj0011
*/
public class MultipleChoiceGameExample 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.Collections;
import java.util.List;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
/**
*
* #author blj0011
*/
public class FXMLDocumentController implements Initializable
{
#FXML
private Label lblQuestionNumber;
#FXML
private TextArea taQuestionDisplay;
#FXML
private Button btnAnswer1, btnAnswer2, btnAnswer3, btnAnswer4, btnNextQuestion;
List<Question> questions;
int currentQuestion = 0;
#Override
public void initialize(URL url, ResourceBundle rb)
{
//Get Questions from Database or what ever source you use
FakeDatabaseHandler fakeDatabaseHandler = new FakeDatabaseHandler();
questions = fakeDatabaseHandler.getQuestions();
//Shuffle the questions
Collections.shuffle(questions);
//Load first question
lblQuestionNumber.setText("Question 1 of " + questions.size());
loadQuestion(questions.get(currentQuestion));
}
#FXML
public void handleBtnNextQuestion(ActionEvent actionEvent)
{
currentQuestion++;
if (currentQuestion < questions.size()) {
lblQuestionNumber.setText("Question " + (currentQuestion + 1) + " of " + questions.size());
loadQuestion(questions.get(currentQuestion));
}
else {
Alert alert = new Alert(Alert.AlertType.WARNING);
alert.setTitle("Game Over Alert");
alert.setContentText("There are no more questions!");
alert.showAndWait();
}
}
public void loadQuestion(Question question)
{
taQuestionDisplay.setText(question.getQuestion());//Set the question
List<String> choices = question.getIncorrectAnswers();//Get the incorrect answers
choices.add(question.getCorrectAnswer());////Get the correct answer and add it.
Collections.shuffle(choices);//Randomize the choices
//Add buttons to List to make creating event handlers easier
List<Button> choicesButtons = new ArrayList();
choicesButtons.add(btnAnswer1);
choicesButtons.add(btnAnswer2);
choicesButtons.add(btnAnswer3);
choicesButtons.add(btnAnswer4);
//Set the choices to a Button
for (int i = 0; i < choices.size(); i++) {
choicesButtons.get(i).setText(choices.get(i));
}
//Create Action handlers for each button
for (Button button : choicesButtons) {
button.setOnAction(actionEvent -> {
//Check if button's text equals the correct answer
if (button.getText().equals(question.getCorrectAnswer())) {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Answer Alert");
alert.setHeaderText("Correct!");
alert.setContentText("You got the answer right!");
alert.showAndWait();
}
else {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Answer Alert");
alert.setHeaderText("Incorrect!");
alert.setContentText("You got the answer wrong!\nThe correct answer is " + question.getCorrectAnswer());
alert.showAndWait();
}
});
}
}
}
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?>
<AnchorPane id="AnchorPane" prefHeight="476.0" prefWidth="668.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.141" fx:controller="multiplechoicegameexample.FXMLDocumentController">
<children>
<Label fx:id="label" layoutX="126" layoutY="120" minHeight="16" minWidth="69" />
<VBox alignment="TOP_CENTER" prefHeight="200.0" prefWidth="100.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<Label fx:id="lblQuestionNumber" alignment="CENTER" maxWidth="1.7976931348623157E308" text="Label" />
<TextArea fx:id="taQuestionDisplay" prefHeight="300.0" prefWidth="200.0">
<VBox.margin>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</VBox.margin>
</TextArea>
<GridPane hgap="5.0" maxWidth="400.0" vgap="5.0">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Button fx:id="btnAnswer1" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" mnemonicParsing="false" text="Button" />
<Button fx:id="btnAnswer2" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" mnemonicParsing="false" text="Button" GridPane.columnIndex="1" />
<Button fx:id="btnAnswer3" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" mnemonicParsing="false" text="Button" GridPane.rowIndex="1" />
<Button fx:id="btnAnswer4" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" mnemonicParsing="false" text="Button" GridPane.columnIndex="1" GridPane.rowIndex="1" />
</children>
</GridPane>
<Button fx:id="btnNextQuestion" onAction="#handleBtnNextQuestion" text="Next Question">
<VBox.margin>
<Insets top="20.0" />
</VBox.margin>
</Button>
</children>
</VBox>
</children>
</AnchorPane>
Question Class
import java.util.ArrayList;
import java.util.List;
/**
*
* #author blj0011
*/
public class Question
{
private String question;
private String correctAnswer;
private List<String> incorrectAnswers = new ArrayList();
public Question(String question, String correctAnswer, List<String> incorrectAnswers)
{
this.question = question;
this.correctAnswer = correctAnswer;
this.incorrectAnswers = incorrectAnswers;
}
public String getQuestion()
{
return question;
}
public void setQuestion(String question)
{
this.question = question;
}
public String getCorrectAnswer()
{
return correctAnswer;
}
public void setCorrectAnswer(String correctAnswer)
{
this.correctAnswer = correctAnswer;
}
public List<String> getIncorrectAnswers()
{
return incorrectAnswers;
}
public void setIncorrectAnswers(List<String> incorrectAnswers)
{
this.incorrectAnswers = incorrectAnswers;
}
}
Database Handler(You may get your data from a different source)
import java.util.ArrayList;
import java.util.List;
/**
*
* #author blj0011
*/
public class FakeDatabaseHandler
{
List<Question> questions = new ArrayList();
public FakeDatabaseHandler()
{
//connect to db!
//Simulate getting data from db!
List<String> incorrectAnswersQuestion1 = new ArrayList();
incorrectAnswersQuestion1.add("Pakistan");
incorrectAnswersQuestion1.add("Palau");
incorrectAnswersQuestion1.add("Panama");
questions.add(new Question("Which is the only American state to begin with the letter 'p'?", "Pennsylvania", incorrectAnswersQuestion1));
List<String> incorrectAnswersQuestion2 = new ArrayList();
incorrectAnswersQuestion2.add("Mississppi");
incorrectAnswersQuestion2.add("Nile");
incorrectAnswersQuestion2.add("Yangtze");
questions.add(new Question("What is the world's longest river?", "Amazon", incorrectAnswersQuestion2));
List<String> incorrectAnswersQuestion3 = new ArrayList();
incorrectAnswersQuestion3.add("6,000 miles");
incorrectAnswersQuestion3.add("10,000 miles");
incorrectAnswersQuestion3.add("12,000 miles");
questions.add(new Question("What is the diameter of Earth?", "8,000 miles", incorrectAnswersQuestion3));
}
public List<Question> getQuestions()
{
return questions;
}
}
I'm unable to bind TableView items with ObservableList in FXML.
Everything works fine when I set materialTable.setItems(materialDataObservableList); in button click event.
But I don't want button to know about TableView so I wanted to bind materialTable.items to materialDataObservableList property.
What am I doing wrong?
Or maybe I don't understand how binding works...
Thanks for help!
<GridPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="464.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.xxx.sm.frontend.fx.MainSceneController">
/*
*/
<children>
<VBox prefHeight="200.0" prefWidth="100.0">
<children>
<Button fx:id="getMaterialsButton" mnemonicParsing="false" onAction="#getMaterialsButton" text="Get materials" />
<TableView fx:id="materialTable" editable="true" prefHeight="413.0" prefWidth="600.0" items="${materialDataObservableList}" >
//Columns here
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
</children>
</VBox>
</children>
public class MainSceneController {
private ObservableList<MaterialData> materialDataObservableList;
public TableView materialTable;
public Button getMaterialsButton;
public void getMaterialsButton() {
getMaterialsFromRESTController();
}
private void getMaterialsFromRESTController() {
MaterialClient controller = new MaterialClient();
try {
materialDataObservableList = FXCollections.observableArrayList(controller.getMaterias());
} catch (IOException e) {
System.out.println("Failed to connect to RESTController");
}
//materialTable.setItems(materialDataObservableList);
}
public ObservableList<MaterialData> getMaterialDataObservableList() {
return materialDataObservableList;
}
}
If you are calling getMaterialsFromRESTController() from MainSceneController's constructor (so it is initialized as soon as the controller is available to the FXMLLoader), then
<TableView fx:id="materialTable" editable="true" prefHeight="413.0" prefWidth="600.0"
items="${controller.materialDataObservableList}" >
will work. (Note you access a property of the controller with ${controller.property}.)
If not, you can modify the controller as follows to make it work:
public class MainSceneController {
private final ObservableList<MaterialData> materialDataObservableList
= FXCollections.observableArrayList();
public TableView materialTable;
public Button getMaterialsButton;
public void getMaterialsButton() {
getMaterialsFromRESTController();
}
private void getMaterialsFromRESTController() {
MaterialClient controller = new MaterialClient();
try {
materialDataObservableList.setAll(controller.getMaterias());
} catch (IOException e) {
System.out.println("Failed to connect to RESTController");
}
}
public ObservableList<MaterialData> getMaterialDataObservableList() {
return materialDataObservableList;
}
}
and then the above FXML should work.
Here is a SSCCE:
BindTableItemsExample.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.cell.PropertyValueFactory?>
<?import javafx.scene.control.Button?>
<?import javafx.geometry.Insets?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="TableController">
<center>
<TableView items="${controller.items}">
<columns>
<TableColumn text="Item">
<cellValueFactory><PropertyValueFactory property="name" /></cellValueFactory>
</TableColumn>
</columns>
</TableView>
</center>
<bottom>
<Button text="Load" onAction="#loadItems" BorderPane.alignment="center" >
<BorderPane.margin>
<Insets top="5" left="5" right="5" bottom="5"/>
</BorderPane.margin>
</Button>
</bottom>
</BorderPane>
TableController.java:
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
public class TableController {
private final ObservableList<Item> items = FXCollections.observableArrayList();
#FXML
private void loadItems() {
items.setAll(createItems());
}
private List<Item> createItems() {
return IntStream.rangeClosed(1, 100)
.mapToObj(i -> "Item "+i)
.map(Item::new)
.collect(Collectors.toList());
}
public ObservableList<Item> getItems() {
return items ;
}
public static class Item {
private final StringProperty name = new SimpleStringProperty();
public Item(String name) {
setName(name);
}
public final StringProperty nameProperty() {
return this.name;
}
public final java.lang.String getName() {
return this.nameProperty().get();
}
public final void setName(final java.lang.String name) {
this.nameProperty().set(name);
}
}
}
BindTableItemsTest.java (application class):
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class BindTableItemsTest extends Application {
#Override
public void start(Stage primaryStage) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("BindTableItemsExample.fxml"));
primaryStage.setScene(new Scene(loader.load(), 600, 600));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I'm a novice and would very like a to know how to make a column cell editable when using FXML.
Specifically, I have been following the two working examples provided by Oracle. The first one (http://docs.oracle.com/javafx/2/ui_controls/table-view.htm) allows editing.
The second example ( http://docs.oracle.com/javafx/2/fxml_get_started/fxml_tutorial_intermediate.htm ) suggests at the bottom of the tutorial that it can be edited but doesn't show how this is achieved.
I have read the answers to the related questions but they are beyond my current level ofknowledge.
Can anyone show me how it can be done in as simpler way as possible please?
To save you looking up the tutorial these are the files which I've copied directly from Oracle:
Thanks in advance.
First is FXMLTableView.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package fxmltableview;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class FXMLTableView extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("FXML TableView Example");
Pane myPane = (Pane)FXMLLoader.load(getClass().getResource
("fxml_tableview.fxml"));
Scene myScene = new Scene(myPane);
primaryStage.setScene(myScene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The second one is the controller:
package fxmltableview;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
public class FXMLTableViewController {
#FXML private TableView<Person> tableView;
#FXML private TextField firstNameField;
#FXML private TextField lastNameField;
#FXML private TextField emailField;
#FXML
protected void addPerson(ActionEvent event) {
ObservableList<Person> data = tableView.getItems();
data.add(new Person(firstNameField.getText(),
lastNameField.getText(),
emailField.getText()
));
firstNameField.setText("");
lastNameField.setText("");
emailField.setText("");
}
}
The third file is FormattedTableCellFactory.java
package fxmltableview;
import java.text.Format;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.text.TextAlignment;
import javafx.util.Callback;
public class FormattedTableCellFactory<S, T>
implements Callback<TableColumn<S, T>, TableCell<S, T>> {
private TextAlignment alignment;
private Format format;
public TextAlignment getAlignment() {
return alignment;
}
public void setAlignment(TextAlignment alignment) {
this.alignment = alignment;
}
public Format getFormat() {
return format;
}
public void setFormat(Format format) {
this.format = format;
}
#Override
#SuppressWarnings("unchecked")
public TableCell<S, T> call(TableColumn<S, T> p) {
TableCell<S, T> cell = new TableCell<S, T>() {
#Override
public void updateItem(Object item, boolean empty) {
if (item == getItem()) {
return;
}
super.updateItem((T) item, empty);
if (item == null) {
super.setText(null);
super.setGraphic(null);
} else if (format != null) {
super.setText(format.format(item));
} else if (item instanceof Node) {
super.setText(null);
super.setGraphic((Node) item);
} else {
super.setText(item.toString());
super.setGraphic(null);
}
}
};
cell.setTextAlignment(alignment);
switch (alignment) {
case CENTER:
cell.setAlignment(Pos.CENTER);
break;
case RIGHT:
cell.setAlignment(Pos.CENTER_RIGHT);
break;
default:
cell.setAlignment(Pos.CENTER_LEFT);
break;
}
return cell;
}
}
The fourth file is the Person class:
package fxmltableview;
import javafx.beans.property.SimpleStringProperty;
public class Person {
private final SimpleStringProperty firstName = new SimpleStringProperty("");
private final SimpleStringProperty lastName = new SimpleStringProperty("");
private final SimpleStringProperty email = new SimpleStringProperty("");
public Person() {
this("", "", "");
}
public Person(String firstName, String lastName, String email) {
setFirstName(firstName);
setLastName(lastName);
setEmail(email);
}
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String fName) {
firstName.set(fName);
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String fName) {
lastName.set(fName);
}
public String getEmail() {
return email.get();
}
public void setEmail(String fName) {
email.set(fName);
}
}
and the last file is the .fxml file:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.cell.*?>
<?import javafx.collections.*?>
<?import fxmltableview.*?>
<GridPane alignment="CENTER" hgap="10.0" vgap="10.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="fxmltableview.FXMLTableViewController">
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
<children>
<Label style="-fx-font: NORMAL 20 Tahoma;" text="Address Book" GridPane.columnIndex="0" GridPane.rowIndex="0">
</Label>
<TableView fx:id="tableView" editable="true" GridPane.columnIndex="0" GridPane.rowIndex="1">
<columns>
<TableColumn fx:id="firstNameColumn" prefWidth="100" text="First Name">
<cellValueFactory>
<PropertyValueFactory property="firstName" />
</cellValueFactory>
<cellFactory>
<FormattedTableCellFactory alignment="left">
</FormattedTableCellFactory>
</cellFactory>
</TableColumn>
<TableColumn prefWidth="100" text="Last Name">
<cellValueFactory>
<PropertyValueFactory property="lastName" />
</cellValueFactory>
<cellFactory>
<FormattedTableCellFactory alignment="left">
</FormattedTableCellFactory>
</cellFactory>
</TableColumn>
<TableColumn prefWidth="200" text="Email Address">
<cellValueFactory>
<PropertyValueFactory property="email" />
</cellValueFactory>
<cellFactory>
<FormattedTableCellFactory alignment="left">
</FormattedTableCellFactory>
</cellFactory>
</TableColumn>
</columns>
<items>
<FXCollections fx:factory="observableArrayList">
<Person email="jacob.smith#example.com" firstName="Jacob" lastName="Smith" />
<Person email="isabella.johnson#example.com" firstName="Isabella" lastName="Johnson" />
<Person email="ethan.williams#example.com" firstName="Ethan" lastName="Williams" />
<Person email="emma.jones#example.com" firstName="Emma" lastName="Jones" />
<Person email="michael.brown#example.com" firstName="Michael" lastName="Brown" />
</FXCollections>
</items>
<sortOrder>
<fx:reference source="firstNameColumn" />
</sortOrder>
</TableView>
<HBox alignment="bottom_right" spacing="10" GridPane.columnIndex="0" GridPane.rowIndex="2">
<children>
<TextField fx:id="firstNameField" prefWidth="90" promptText="First Name" />
<TextField fx:id="lastNameField" prefWidth="90" promptText="Last Name" />
<TextField fx:id="emailField" prefWidth="150" promptText="email" />
<Button onAction="#addPerson" text="Add" />
</children>
</HBox>
</children>
<columnConstraints>
<ColumnConstraints />
</columnConstraints>
<rowConstraints>
<RowConstraints />
<RowConstraints />
<RowConstraints />
</rowConstraints>
</GridPane>
I am unable to populate my tableview. I believe the problem is in the controller, in the way my data is being sent to the FXML file, because a system out print (see below) shows exactly what I have in my database.
Please let me know where I did a mistake. I went over all tutorials that exist on that, but nothing fits my problem.
Thanks
Main App:
package tableview;
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) {
// TODO Auto-generated method stub
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("view/FXMLTable.fxml"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
}
Controller:
import tableview.model.Person;
public class FXMLTableController{
#FXML
public TableView<Person> tableview ;
#FXML
private TableColumn<Person, Number> clientIdColumn;
#FXML
private TableColumn<Person, String> firstNameColumn;
#FXML
private TableColumn<Person, String> lastNameColumn;
#FXML
private void initialize() {
assert tableview != null : "fx:id=\"tableview\" was not injected: check your FXML file 'UserMaster.fxml'.";
clientIdColumn.setCellValueFactory(cellData -> cellData.getValue().
clientIDProperty());
firstNameColumn.setCellValueFactory(cellData -> cellData.getValue()
.firstNameProperty());
lastNameColumn.setCellValueFactory(cellData -> cellData.getValue()
.lastNameProperty());
buildData();
}
private ObservableList<Person> data;
public void buildData(){
data = FXCollections.observableArrayList();
Connection con = null;
try {
Class.forName("org.sqlite.JDBC");
con = DriverManager.getConnection("jdbc:sqlite:tableviewdb.db");
String SQL = "Select * from INFO";
ResultSet rs = con.createStatement().executeQuery(SQL);
while(rs.next()){
Person per = new Person();
per.ClientID.set(rs.getInt("CLIENTID"));
per.FirstName.set(rs.getString("FIRSTNAME"));
per.LastName.set(rs.getString("LASTNAME"));
data.add(per);
}
tableview = new TableView<Person>();
tableview.setItems(data);
System.out.println(tableview.getItems().get(1).ClientID);
}
catch(Exception e){
e.printStackTrace();
System.out.println("Error on Building Data");
}
}
}
Model Class:
package tableview.model;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class Person {
public SimpleIntegerProperty ClientID = new SimpleIntegerProperty();
public SimpleStringProperty FirstName = new SimpleStringProperty();
public SimpleStringProperty LastName = new SimpleStringProperty();
public SimpleIntegerProperty getClientID() {
return ClientID;
}
public SimpleStringProperty getFirstname() {
return FirstName;
}
public SimpleStringProperty getLastName() {
return LastName;
}
public IntegerProperty clientIDProperty(){
return ClientID;
}
public StringProperty firstNameProperty(){
return FirstName;
}
public StringProperty lastNameProperty(){
return LastName;
}
}
FXML file:
(disregard the save button for now...)
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8">
<children>
<SplitPane prefHeight="400.0" prefWidth="600.0">
<items>
<SplitPane dividerPositions="0.5" orientation="VERTICAL" prefHeight="200.0" prefWidth="160.0">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
<children>
<TextField layoutX="93.0" layoutY="34.0" />
<TextField layoutX="93.0" layoutY="85.0" />
<Label layoutX="35.0" layoutY="39.0" text="name" />
<Label layoutX="35.0" layoutY="90.0" text="email" />
<Button layoutX="204.0" layoutY="140.0" mnemonicParsing="false" text="save" />
</children>
</AnchorPane>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0">
<children>
<TableView layoutY="-2.0" prefHeight="200.0" prefWidth="598.0">
<columns>
<TableColumn prefWidth="302.0" text="name" />
<TableColumn prefWidth="295.0" text="email" />
</columns>
</TableView>
</children>
</AnchorPane>
</items>
</SplitPane>
</items>
</SplitPane>
</children>
</AnchorPane>
You're creating a new TableView and setting its items, instead of setting the items on the table that the FXML file defined. Remove the
tableView = new TableView<Person>();
from the controller.
To get the #FXML-annotated fields in the controller to be populated with the appropriate elements from the FXML file, you need to add fx:id attributes to those elements:
<TableView fx:id="tableview" ... >
<columns>
<TableColumn fx:id="firstNameColumn" ... />
...