This question already has answers here:
Javafx PropertyValueFactory not populating Tableview
(2 answers)
Closed 8 years ago.
I have following basic code in JavaFx. If I want to change the name of PropertyValueFactory("id") to let's say "rid" it does not fill my table anymore. Even if I change the SimpleLongProperty object to rid too. Do you know where the "id" is referencing / pointing to?
public class Main_Controller implements Initializable{
public class Item {
public SimpleLongProperty id = new SimpleLongProperty();
public Long getId() {
return id.get();
}
}
// The table and columns
#FXML TableView<Item> itemTbl;
#FXML TableColumn itemIdCol;
#FXML Button add_Button;
// The table's data
ObservableList<Item> data;
#Override
public void initialize(URL url, ResourceBundle rb) {
// Set up the table data
itemIdCol.setCellValueFactory(
new PropertyValueFactory<Item,Long>("id")
);
data = FXCollections.observableArrayList();
itemTbl.setItems(data);
}
static long nextId = 1;
#FXML
private void handleButtonAction(ActionEvent event) {
Item item = new Item();
item.id.setValue(nextId++);
data.add(item);
}
}
Code With changed PropertyValueFactory:
public class Main_Controller implements Initializable{
public class Item {
public SimpleLongProperty rid = new SimpleLongProperty();
public Long getId() {
return rid.get();
}
}
// The table and columns
#FXML TableView<Item> itemTbl;
#FXML TableColumn itemIdCol;
#FXML Button add_Button;
// The table's data
ObservableList<Item> data;
#Override
public void initialize(URL url, ResourceBundle rb) {
// Set up the table data
itemIdCol.setCellValueFactory(
new PropertyValueFactory<Item,Long>("rid")
);
data = FXCollections.observableArrayList();
itemTbl.setItems(data);
}
static long nextId = 1;
#FXML
private void handleButtonAction(ActionEvent event) {
Item item = new Item();
item.rid.setValue(nextId++);
data.add(item);
}
}
You need a method in your Item class called
public Long getRid(){return rid.get();}
or
public SimpleLongProperty ridProperty(){return rid;}
Note, it's not the name of the variable, which would normally be private, but the name of the getter that matters.
Javadoc link
Related
Basically I have tableview and a FORM. I enter value in the form and they are displayed inside the table view via a database. It looks like this: FORM and TableView
When I click on the row to select it , I want to retrieve the DatePicker value from the column Begin Datum back to the Datepicker field.
I have a onMouseClicked method like this to retrieve the text/vlaues from the selected row back to the FORM
#FXML
void getSelected(MouseEvent event) {
index = tableViewBooking.getSelectionModel().getSelectedIndex();
if (index <= -1) {
return;
}
beginTime.setText(tableColSTime.getCellData(index).toString());
endTime.setText(tableColETime.getCellData(index).toString());
beginDate.setValue(tableColDate.getCellData(index).toString()); // problem has somewith with `toString()?`
reminderDesc.setText(tableColName.getCellData(index).toString());
}
Relevant primaryController code:
public class PrimaryController {
ObservableList<String> intervalList =
FXCollections.observableArrayList("Täglich","Wochelich","Monatlich");
ObservableList<String> projectTypeList = FXCollections.observableArrayList("ProjeKt Iot","Projekt Data & Analytics","Projekt SAP Intelligent Enterprise",
"Projekt Prozess & Betrieb"," Projekt Moderne Software Architekturen ");
#FXML
private DatePicker beginDate;
#FXML
private TextField beginTime;
#FXML
private Button clearButton;
#FXML
private DatePicker endDate;
#FXML
private TextField endTime;
#FXML
private TextField reminderDesc;
#FXML
private Button saveButton;
#FXML
private ComboBox cycleComboBox;
#FXML
private ComboBox projectComboBox;
#FXML
private JFXListView<String> listofdata;
#FXML
private Button modifyButton;
#FXML
private ResourceBundle resources;
#FXML
private URL url;
// Table View
#FXML
public TableView<Booking> tableViewBooking;
#FXML
public TableColumn<Booking, String> tableColName;
#FXML
public TableColumn<Booking, Double> tableColHours;
#FXML
public TableColumn<Booking, String> tableColType;
// #FXML
// public TableColumn<Booking, String> tableColProj;
#FXML
public TableColumn<Booking, String> tableColDate;
#FXML
public TableColumn<Booking, String> tableColSTime;
#FXML
public TableColumn<Booking, String> tableColETime;
int index = -1 ;
#FXML
public void initialize() throws IOException, ParseException {
cycleComboBox.setValue("Täglich");
cycleComboBox.setItems(intervalList);
projectComboBox.setValue("Projekt Moderne Software Architekturen ");
projectComboBox.setItems(projectTypeList);
System.out.println("Inside initialize");
tableColName.setCellValueFactory(new PropertyValueFactory<>("Name"));
tableColDate.setCellValueFactory(new PropertyValueFactory<>("Date"));
tableColSTime.setCellValueFactory(new PropertyValueFactory<>("startTime"));
tableColETime.setCellValueFactory(new PropertyValueFactory<>("endTime"));
tableColHours.setCellValueFactory(new PropertyValueFactory<>("Hours"));
tableColType.setCellValueFactory(new PropertyValueFactory<>("Type"));
tableViewBooking.setItems(getTableBookings());
}
the problem here is that String is undefined type for Datepicker, it is LocalDate . So what to use here instead of .toString()?
beginDate.setValue(tableColDate.getCellData(index).toString());
You should either make the tableColDate be TableColumn<Booking, LocalDate>. Then you can remove the toString() call and everything works.
The reason is that setValue() expects a LocalDate, not a String.
Or you parse the date when you are setting the DatePicker: beginDate.setValue(LocalDate.parse(tableColDate.getCellData(index))).
In my JavaFx application I want to update my BarChart whenever calculate button is clicked. The problem is that I am getting:
java.lang.NullPointerException at
application.StatController.setActivityData(StatController.java:47)
It is always pointing to:`xAxis.setCategories(optionsNames);
But it has elements on the list (See printscreen:https://image.ibb.co/b9YO8Q/Capture.png
In my StatController class I have setActivityData which is called from FormController class.
StatController class:
public class StatController {
#FXML
private BarChart<String, Double> barChart;
#FXML
private CategoryAxis xAxis;
Activities activities = new Activities();
private Map<String, List<Double>> uniqueActivityOptions = new HashMap<>();
private ObservableList<String> optionsNames = FXCollections.observableArrayList();
public StatController(){}
#FXML
private void initialize() {
}
public void setActivityData(Activities activitiesList) {
for(Activity activity : activities.getActivityList()) {
String optionName = activity.getOption();
if(uniqueActivityOptions.containsKey(optionName)) {
uniqueActivityOptions.get(optionName).add((double) activity.getNumber());
} else {
List<Double> activityOptionList = new ArrayList<>();
activityOptionList.add((double) activity.getNumber());
uniqueActivityOptions.put(optionName, activityOptionList);
}
}
for (Map.Entry<String, List<Double>> entry : uniqueActivityOptions.entrySet()) {
optionsNames.add(entry.getKey());
}
xAxis.setCategories(optionsNames);
XYChart.Series<String, Double> series = new XYChart.Series<>();
for (Map.Entry<String, List<Double>> entry : uniqueActivityOptions.entrySet()) {
Double average = calculateAverage(entry.getValue());
series.getData().add(new XYChart.Data<>(entry.getKey().toString(), average));
}
barChart.getData().add(series);
}
private double calculateAverage(List<Double> values) {
double result = 0;
for (Double value : values) {
result += value;
}
return result / values.size();
}
}
FormController class:
public class FormController {
Activities act = new Activities();
List<Activity> activities = act.getActivityList();
private ObservableList<String> opt = FXCollections.observableArrayList(
"Option 1",
"Option 2",
"Option 3"
);
#FXML
private Button calculateButton;
#FXML
private TextField numberField;
#FXML
private ComboBox<String> options;
private String selectedOption;
#FXML
private void initialize() {
options.getItems().addAll(opt);
options.getSelectionModel().selectedItemProperty()
.addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
selectedOption = newValue;
}
});
}
#FXML
public void calculateButtonClicked(){
activities.add(new Activity(selectedOption, Integer.parseInt(numberField.getText())));
StatController sc = new StatController();
sc.setActivityData(act);
}
}
I tested setActivityData method in StatsController and it is working correctly when I am passing Activities.
Please advise what to change in the code to pass and update BarChart.
I know that this is something trivial but I really don't know how to do it.
`
Thank you very much for the help!
Your problem is in FormController#calculateButtonClicked(). You create a new instance of StatController manually and then call the setActivityData() method. This is not how JavaFX works and will result in the xAxis member being null, hence your NullPointerException.
Controller members annotated with the #FXML annotation will be injected by the FXMLLoader class when you call the FXMLLoader#load() method. You will need to use the FXMLLoader class to load the .fxml file that corresponds to the StatController class, which will then create an instance of your StatController object for you and will also inject your xAxis and barChart instances.
Here's a quick (not production ready) example that you will have to adapt for your specific scenario:
FXMLLoader loader = new FXMLLoader();
loader.setLocation(filePath); // your path to the .fxml file
loader.load();
StatController controller = (StatController) loader.getController();
controller.setActivityData(activities); // your activities data
How and when you do that depends on how your scene graph is setup. Looking at your source code, I would suggest the following changes:
MainController.java
public class MainController
implements Initializable {
#FXML
private TabPane tabPane;
#FXML
private Tab formTabPage;
#FXML
private FormController formTabController; // change name to this
#FXML
private Tab statsTabPage;
#FXML
private StatController statsTabController; // change name to this
private MainApp mainApp;
public void setMainApp(MainApp mainApp) {
this.mainApp = mainApp;
}
#Override // rename your init() method to this
public void initialize(URL location, ResourceBundle resources) {
// add this line
formTabController.setStatController(statTabController);
}
}
FormController.java
public class FormController {
Activities act = new Activities();
List<Activity> activities = act.getActivityList();
private ObservableList<String> opt = FXCollections.observableArrayList(
"Option 1",
"Option 2",
"Option 3"
);
#FXML
private Button calculateButton;
#FXML
private TextField numberField;
#FXML
private ComboBox<String> options;
private String selectedOption;
private StatController statController; // add this member
#FXML
private void initialize() {
options.getItems().addAll(opt);
options.getSelectionModel().selectedItemProperty()
.addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
selectedOption = newValue;
}
});
}
#FXML
public void calculateButtonClicked() {
// change this method here
statController.setActivityData(act);
}
// add this method here
public void setStatController(StatController statController) {
this.statController = statController;
}
}
There are other problems that occur, but this fixes your null pointer exception. The reason this works and your previous code didn't, is because you were manually creating a new instance of StatController and not using the instance that the FXMLLoader had already loaded and mapped to the user interface node for you. All I have done above, is capture a reference to the controller you needed, and provided it to the other through a setter method.
I want to make a custom cell in the ListView. Excuse my bad English!
I want to display the picture, name and status in the ListView.
For this I use a different Fxml that contains Hbox .
public class Controller {
CollectionContactForListCollection contactForList = new CollectionContactForListCollection();
#FXML
private ListView<Contact> listContact ;
#FXML
HBox hbox;
#FXML
ImageView avatar;
#FXML
Label labelName;
#FXML
Label lblStatus;
#FXML
Label lblSense;
#FXML
private void initialize(){
contactForList.fieldData();
// listContact.setItems((ObservableList) contactForList.getContactList());
listContact.setCellFactory(new Callback<ListView<Contact>, ListCell<Contact>>() {
#Override
public ListCell<Contact> call(ListView<Contact> param) {
ListCell<Contact> listCell = new ListCell<Contact>() {
#Override
protected void updateItem(Contact item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
//This method does not work download
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/view/boxInContact.fxml"));
fxmlLoader.setController(this);
labelName.setText(item.getName());
lblSense.setText(item.getSense());
lblStatus.setText(item.getStatus());
avatar.setImage(item.getImage());
}
}
};
return listCell;
}
});
listContact.setItems((ObservableList) contactForList.getContactList());
}
As a general, rule, you should use a different controller class for each FXML file. With the code you have, all cells are using the same controller instance, so there is only one reference to each control, even though there are many labelNames, etc (one for each cell).
So define a controller for the FXML defined for the list cell, and define the methods you need to update the controls:
public class ContactCellController {
#FXML
private Label labelName ;
#FXML
private Label labelStatus ;
#FXML
private Label labelSense ;
#FXML
private ImageView avatar ;
public void setName(String name) {
labelName.setText(name);
}
public void setStatus(String status) {
labelStatus.setText(status);
}
public void setSense(String sense) {
labelSense.setText(sense);
}
public void setAvatarImage(Image image) {
avatar.setImage(image);
}
}
Update the FXML file to use a controller attribute with fx:controller="my.package.ContactCellController", and then your cell implementation can look like
listContact.setCellFactory(lv -> new ListCell<Contact>() {
private Node graphic ;
private ContactCellController controller ;
{
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/boxInContact.fxml"));
graphic = loader.load();
controller = loader.getController();
} catch (IOException exc) {
throw new RuntimeException(exc);
}
}
#Override
protected void updateItem(Contact contact, boolean empty) {
super.updateItem(contact, empty);
if (empty) {
setGraphic(null);
} else {
controller.setName(contact.getName());
controller.setStatus(contact.getStatus());
controller.setSense(contact.getSense());
controller.setAvatarImage(contact.getImage());
setGraphic(graphic);
}
}
});
The code below runs as its name says on Java 8 update 5 but not on later ones:
public class TableViewShowingOnlyAnAppendContextMenuItemIfRowIsEmptyElseDeleteIsIncluded extends Application {
private final TableView<Name> table = new TableView<>();
private final ObservableList<Name> data = FXCollections.observableArrayList(new Name("Jacob"),
new Name("Isabella"), new Name("Ethan"), new Name("Emma"), new Name("Michael"));
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
TableColumn<Name, String> column = new TableColumn<>("Name");
column.setCellValueFactory(new PropertyValueFactory<>("name"));
table.getColumns().add(column);
table.setItems(data);
ContextMenu contextMenu = new ContextMenu();
contextMenu.getItems().add(new MenuItem("append"));
table.setContextMenu(contextMenu);
table.setRowFactory(tableView -> {
TableRow<Name> row = new TableRow<>();
row.contextMenuProperty().bind(
Bindings.when(Bindings.isNotNull(row.itemProperty()))
.then(showOnlyAppendContextMenuItemIfRowIsEmptyElseIncludeDelete())
.otherwise((ContextMenu) null));
return row;
});
Scene scene = new Scene(table);
stage.setScene(scene);
stage.show();
}
private ContextMenu showOnlyAppendContextMenuItemIfRowIsEmptyElseIncludeDelete() {
ContextMenu rowMenu = new ContextMenu();
ContextMenu tableMenu = table.getContextMenu();
if (tableMenu != null)
rowMenu.getItems().addAll(tableMenu.getItems());
rowMenu.getItems().add(new MenuItem("delete"));
return rowMenu;
}
public static class Name {
private final SimpleStringProperty name;
private Name(String name) {
this.name = new SimpleStringProperty(name);
}
public String getName() {
return name.get();
}
public void setName(String name) {
this.name.set(name);
}
} }
Can help me find the error in the code? Or if there is none, is this a regression that should be submitted? As of now, all the PCs in use have 8u5.
Thanks in advance.
This code looks like something I may have posted a while back...
The issue is that using a MenuItem in multiple menus is not really supported. While this isn't explicitly stated in the Javadocs, the fact that MenuItem has a getMenu() method does imply this. The fact that it worked prior to 8u5 is really just luck...
The fix is to create new menu items that are copies of the ones in the table's context menu:
private ContextMenu showOnlyAppendContextMenuItemIfRowIsEmptyElseIncludeDelete() {
ContextMenu rowMenu = new ContextMenu();
ContextMenu tableMenu = table.getContextMenu();
if (tableMenu != null) {
for (MenuItem item : tableMenu.getItems()) {
MenuItem rowItem = new MenuItem(item.getText());
rowItem.setGraphic(item.getGraphic());
rowItem.setOnAction(item.getOnAction());
rowMenu.getItems().add(rowItem);
}
}
rowMenu.getItems().add(new MenuItem("delete"));
return rowMenu;
}
Another approach is to use ControlsFX Actions, so you can maintain a (single) list of actions for the whole table and generate menu items from them for both the table and the rows.
I have a BorderPane, onto which I placed a MenuBar. At the center of the BorderPane I display differnt AnchorPanes depending on the MenuItem selected. So far so good.
Now, how do I make sure that the Menus change behavior in response to the item selected in the child AnchorPane? So for example if the user selects "Edit", there will be a different action depending on whether the item currently higlighted is a user account, a file etc.
So far I made something along these lines:
The BorderPane controller:
public class MenuTest implements Initializable{
#FXML
private BorderPane borderPaneMain;
#FXML
private AnchorPane anchorPaneMain;
#FXML
private Menu menuEdit;
#FXML
private MenuItem itemEdit;
static String menuMode;
static String entityName;
public MenuTest(){
menuMode ="";
entityName = "";
}
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
AnchorPane anchor;
try {
anchor = (AnchorPane) new FXMLLoader().load(getClass().getResource("views/MainView.fxml"));
borderPaneMain.setCenter(anchor);
} catch (IOException e) {
e.printStackTrace();
}
}
protected static void setMenuMode(String menuMd, String entityNm){
entityName = entityNm;
menuMode = menuMd;
}
#FXML
private void onEditClick(){
if(entityName.equals(AnchorTest.FILE)){
//Launches correct edit view
new FXMLLoader().load(getClass().getResource("views/EditFile.fxml"));
//Passes the name of the entity so that the controller can retrieve its data
FileEditController.setFile(entityName);
}else if(entityName.equals(AnchorTest.PERSON)){
new FXMLLoader().load(getClass().getResource("views/EditPerson.fxml"));
PersonEditController.setFile(entityName);
}
}
}
The child AnchorPane controller:
public class AnchorTest implements Initializable{
public static final String PERSON = "PERSON";
public static final String FILE = "FILE";
ObservableList<String> peopleList;
ObservableList<String> fileList;
#FXML
private ListView<String> listPeople;
#FXML
private ListView<String> listFiles;
#Override
public void initialize(URL location, ResourceBundle resources) {
peopleList = FXCollections.observableArrayList("Frank","Matin","Anne");
fileList = FXCollections.observableArrayList("hello.txt","holiday.jpg","cv.doc");
listPeople.setItems(peopleList);
listFiles.setItems(fileList);
}
#FXML
private void personSelected(){
MenuTest.setMenuMode(this.PERSON, listPeople.getSelectionModel().getSelectedItem());
}
#FXML
private void fileSelected(){
MenuTest.setMenuMode(this.FILE, listFiles.getSelectionModel().getSelectedItem());
}
}
However I'm not sure that it's the best solution, especially considering the if/elseif statement will need to be altered whenever I add a new element type and its corresponding edit options. So is there any way that I can do this better?
I think if your application has only a few (2-4) different types of "things" that are represented by a AnchorPane, then your approach is totally fine. An alternative to your approach is the state pattern. In that case, your currently selected "item type" would be your state.