Resizing all elements in JavaFX GUI using Java Scene Builder - javafx

I'm building a project in javaFx and using scene builder to create and manage my GUI.
I want the stage to be resizable and when it the user resizes the window I want all elements to resize accordingly and automatically.
Another thing is I want the stage window to resize according to the scene size automatically.
I use Anchor Pane as my root node in all scenes.
What I get right now when resizing the window is the window frame going over the content of the window but doesn't affect it.
Here is the controller class for the specific scene:
![package screensframework;
import GameEngine.Player;
import GameEngine.PlayerActions;
import GameEngine.PlayerType;
import GameEngine.Table;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.layout.GridPane;
public class TableSceneController implements Initializable, ControlledScreen {
private ScreensController myController;
private GameEngine.Table myTable;
private int playerNumber;
private int betindex;
private SimpleBooleanProperty standPossible;
private SimpleBooleanProperty hitPossible;
private SimpleBooleanProperty doublePossible;
private SimpleBooleanProperty splitPossible;
#FXML
private Button standButton;
#FXML
private Button hitButton;
#FXML
private Button splitButton;
#FXML
private Button doubleButton;
#FXML
private Button placeBetButton;
#FXML
private Label messageLabel;
#FXML
private Slider betSlider;
#FXML
private GridPane betGridPane;
#FXML
private Label betLabel;
#FXML
private Label Player1Money;
#FXML
private Label Player2Money;
#FXML
private Label Player3Money;
#FXML
private Label Player4Money;
#FXML
private Label Player5Money;
#FXML
private Label Player6Money;
#Override
public void initialize(URL url, ResourceBundle rb) {
standPossible = new SimpleBooleanProperty();
hitPossible = new SimpleBooleanProperty();
doublePossible = new SimpleBooleanProperty();
splitPossible = new SimpleBooleanProperty();
splitPossible.set(false);
doublePossible.set(false);
hitPossible.set(false);
standPossible.set(false);
getDoublePossible().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> source, Boolean oldValue, Boolean newValue) {
setDoubleButton(newValue);
}
});
getHitPossible().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> source, Boolean oldValue, Boolean newValue) {
setHitButton(newValue);
}
});
getSplitPossible().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> source, Boolean oldValue, Boolean newValue) {
setSplitButton(newValue);
}
});
getStandPossible().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> source, Boolean oldValue, Boolean newValue) {
setStandButton(newValue);
}
});
betSlider.valueProperty().addListener(new ChangeListener() {
#Override
public void changed(ObservableValue ov, Object t, Object t1) {
betLabel.textProperty().setValue(String.valueOf((int) betSlider.getValue()));
}
});
playerNumber = 0;
betindex = 0;
}
#Override
public void setScreenParent(ScreensController screenParent) {
myController = screenParent;
}
#FXML
public void onHit(ActionEvent event) {
myTable.hit(playerNumber, betindex);
dealCardsAnimation();
checkStillInGame();
}
#FXML
public void onStand(ActionEvent event) {
checkStillInGame();
}
#FXML
public void onDouble(ActionEvent event) {
myTable.playDouble(playerNumber, betindex);
dealCardsAnimation();
checkStillInGame();
}
#FXML
public void onSplit(ActionEvent event) {
myTable.split(playerNumber, betindex);
dealCardsAnimation();
}
#FXML
public void onPlaceBet(ActionEvent event) {
myTable.getPlayersOnTable().get(playerNumber).AddBet((int) betSlider.getValue());
betSlider.setValue(1);
checkIfthereAreMorePlayersToBet();
}
public void checkIfthereAreMorePlayersToBet() {
if (playerNumber + 1 < myTable.getNumberOfPlayers()) {
playerNumber++;
getBets();
} else {
// messageLabel.textProperty().set("");
//betGridPane.disableProperty().set(true);
// myTable.dealCardsToPlyers();
//dealCardsAnimation();
startPlay();
}
}
private void setStandButton(boolean value) {
standButton.disableProperty().setValue(value);
}
private void setHitButton(boolean value) {
hitButton.disableProperty().setValue(value);
}
private void setSplitButton(boolean value) {
splitButton.disableProperty().setValue(value);
}
private void setDoubleButton(boolean value) {
doubleButton.disableProperty().setValue(value);
}
private void dealCardsAnimation() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
private void startPlay() {
for (Player player : myTable.getPlayersOnTable()) {
System.out.println(player.getBet(0).getSumOfBet());
}
// standPossible.set(myTable.isActionPossible(playerNumber, betindex, PlayerActions.STAND));
// hitPossible.set(myTable.isActionPossible(playerNumber, betindex, PlayerActions.HIT));
// doublePossible.set(myTable.isActionPossible(playerNumber, betindex, PlayerActions.DOUBLE));
// splitPossible.set(myTable.isActionPossible(playerNumber, betindex, PlayerActions.SPLIT));
}
private void endRound() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
public void checkStillInGame() {
if (!myTable.isHandInPlay(playerNumber, betindex)) {
if (myTable.getPlayerHand(playerNumber).size() - 1 > betindex) {
betindex++;
startPlay();
} else if (playerNumber + 1 == myTable.getNumberOfPlayers()) {
endRound();
} else {
playerNumber++;
betindex = 0;
startPlay();
}
}
}
public SimpleBooleanProperty getStandPossible() {
return standPossible;
}
public SimpleBooleanProperty getHitPossible() {
return hitPossible;
}
public SimpleBooleanProperty getDoublePossible() {
return doublePossible;
}
public SimpleBooleanProperty getSplitPossible() {
return splitPossible;
}
private void getBets() {
if (myTable.getPlayersOnTable().get(playerNumber).getType() == PlayerType.HUMAN) {
updateMessageLabel(" please enter your bet.");
placeBetButton.disableProperty().setValue(false);
betSlider.setMax(myTable.getPlayersOnTable().get(playerNumber).getSumOfMoney());
betSlider.setMin(1);
}else{
myTable.placeComputerBet(playerNumber);
checkIfthereAreMorePlayersToBet();
}
}
private void updateMessageLabel(String message) {
messageLabel.textProperty().set(myTable.getPlayersOnTable().get(playerNumber).getName() + message);
}
#Override
public void onShow() {
myTable.startNewRound();
getBets();
}
#Override
public void setMyTable(GameEngine.Table myTable) {
this.myTable = myTable;
}
}

Related

Can't Load all the data from bin file into text View. My program just show the latest value in tableView. Can't apply the loop properly

In this code, I'm trying to read data from the bin file and set it in the table view column. But I can't set the loop properly in loadTableFromFileButtonOnClick.It is only showing the latest value from the bin file. But I want to load all the binding data from the bin file Here is my controller class code.
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.URL;
import java.time.LocalDate;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.DatePicker;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.MouseEvent;
public class FXMLMainSceneController implements Initializable {
#FXML private TextField idTxt;
#FXML private TextField nameTxt;
#FXML private TextField deptTxt;
#FXML private TextField cgpaTxt;
#FXML private DatePicker birthdayDatePicker;
#FXML private TableView<Student> tableView;
#FXML private TableColumn<Student, String> idColumn;
#FXML private TableColumn<Student, String> nameColumn;
#FXML private TableColumn<Student, LocalDate> birthdayColumn;
#FXML private TableColumn<Student, String> deptColumn;
#FXML private TableColumn<Student, String> cgpaColumn;
#Override
public void initialize(URL url, ResourceBundle rb) {
idColumn.setCellValueFactory(new PropertyValueFactory<Student,String>("id"));
nameColumn.setCellValueFactory(new PropertyValueFactory<Student,String>("name"));
birthdayColumn.setCellValueFactory(new PropertyValueFactory<Student,LocalDate>("birthday"));
deptColumn.setCellValueFactory(new PropertyValueFactory<Student,String>("dept"));
cgpaColumn.setCellValueFactory(new PropertyValueFactory<Student,String>("cgpa"));
}
#FXML
private void saveToFileButtonOnClick(ActionEvent event) {
Student stud = new Student(
Integer.parseInt(idTxt.getText()),
nameTxt.getText(),
birthdayDatePicker.getValue(),
deptTxt.getText(),
Float.parseFloat(cgpaTxt.getText())
);
idTxt.setText(null); nameTxt.setText(null); cgpaTxt.setText(null); deptTxt.setText(null);
stud.display();
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Stud.bin"));
oos.writeObject(stud);
oos.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
#FXML
private void loadTableFromFileButtonOnClick(ActionEvent event) {
ObjectInputStream ois=null;
try {
Student s;
//There will be a loop for set up all the data ,i tried bt can't apply it properly
ois = new ObjectInputStream(new FileInputStream("Stud.bin"));
s = (Student) ois.readObject();
s.display();
tableView.getItems().add(s);
} catch (Exception ex) {
try {
if(ois!=null)
ois.close();
}
catch (IOException e) {
e.printStackTrace();
}
ex.printStackTrace();
}
}
#FXML
private void idTxtOnMouseClick(MouseEvent event) {
idTxt.setText(null);
}
#FXML
private void nameTxtOnMouseClick(MouseEvent event) {
nameTxt.setText(null);
}
#FXML
private void cgpaTxtOnMouseClick(MouseEvent event) {
cgpaTxt.setText(null);
}
#FXML
private void deptTxtOnMouseClick(MouseEvent event) {
deptTxt.setText(null);
}
}
Here is my subclass
import java.io.Serializable;
import java.time.LocalDate;
public class Student extends Person implements Serializable{
int id;
String dept;
float cgpa;
public Student(int id, String name, LocalDate birthday, String dept, float cgpa) {
super(name, birthday);
this.id = id;
this.dept = dept;
this.cgpa = cgpa;
}
public void setId(int id) {
this.id = id;
}
public void setDept(String dept) {
this.dept = dept;
}
public void setCgpa(float cgpa) {
this.cgpa = cgpa;
}
public int getId() {
return id;
}
public String getDept() {
return dept;
}
public float getCgpa() {
return cgpa;
}
#Override
public String toString(){
return "Id="+id+", Name="+name+", DoB="+birthday+", Dept="+dept+", Cgpa="+cgpa;
}
public void display(){
System.out.println("Id="+id+", Name="+name+", DoB="+birthday+", Dept="+dept+", Cgpa="+cgpa);
}
}
here is my superclass
import java.io.Serializable;
import java.time.LocalDate;
import javafx.beans.property.SimpleStringProperty;
public class Person implements Serializable{
protected String name;
protected LocalDate birthday;
public Person(String name, LocalDate birthday) {
this.name = name;
this.birthday = birthday;
}
public void setName(String name) {
this.name = name;
}
public void setBirthday(LocalDate birthday) {
this.birthday = birthday;
}
public String getName() {
//return firstName;
return name;
}
public LocalDate getBirthday() {
return birthday;
}
}

TableView row selection on Mouse Middle Button click

Currently in TableView, the row selection is not happening when we click mouse middle button. The row is selected if we do right or left click. I am trying to have the feature of selecting the row on middle button click.
I am already aware that including an event handler in table row factory can fix this. But I have a custom table view with lot of custom features for my application. And this custom TableView is widely used across my application. My main problem is, I cannot ask each and every table row factory to include this fix.
I am looking for a way to do this on higher level (may be on TableView level) so that the row factory does not need to care of that.
Any ideas/help is highly appreciated.
Below is a quick example of what I am trying to achieve.
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class TableRowSelectionOnMiddleButtonDemo extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
ObservableList<Person> persons = FXCollections.observableArrayList();
for (int i = 0; i < 4; i++) {
persons.add(new Person("First name" + i, "Last Name" + i));
}
CustomTableView<Person> tableView = new CustomTableView<>();
TableColumn<Person, String> fnCol = new TableColumn<>("First Name");
fnCol.setCellValueFactory(param -> param.getValue().firstNameProperty());
TableColumn<Person, String> lnCol = new TableColumn<>("Last Name");
lnCol.setCellValueFactory(param -> param.getValue().lastNameProperty());
tableView.getColumns().addAll(fnCol, lnCol);
tableView.getItems().addAll(persons);
tableView.setRowFactory(new Callback<TableView<Person>, TableRow<Person>>() {
#Override
public TableRow<Person> call(TableView<Person> param) {
return new TableRow<Person>(){
{
/* This will fix my issue, but I dont want each tableView rowfactory to set this behavior.*/
// addEventHandler(MouseEvent.MOUSE_PRESSED,e->{
// getTableView().getSelectionModel().select(getItem());
// });
}
#Override
protected void updateItem(Person item, boolean empty) {
super.updateItem(item, empty);
}
};
}
});
VBox sp = new VBox();
sp.setAlignment(Pos.TOP_LEFT);
sp.getChildren().addAll(tableView);
Scene sc = new Scene(sp);
primaryStage.setScene(sc);
primaryStage.show();
}
public static void main(String... a) {
Application.launch(a);
}
/**
* My custom tableView.
* #param <S>
*/
class CustomTableView<S> extends TableView<S> {
public CustomTableView() {
// A lot of custom behavior is included to this TableView.
}
}
class Person {
private StringProperty firstName = new SimpleStringProperty();
private StringProperty lastName = new SimpleStringProperty();
public Person(String fn, String ln) {
setFirstName(fn);
setLastName(ln);
}
public String getFirstName() {
return firstName.get();
}
public StringProperty firstNameProperty() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public String getLastName() {
return lastName.get();
}
public StringProperty lastNameProperty() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName.set(lastName);
}
}
}
The entry point for any global custom (per-control) functionality/behavior is the control's skin. More specifically: user interaction is controlled by the skin's behavior - which unfortunately didn't make it into public scope, such that its access/modification requires to go dirty (as in accessing internal classes/methods, partly via reflection).
Assuming that such access is allowed, the steps to tweak the mouse interaction into reacting the same way for the middle as for the primary button for a TableCell are
implement a custom TableCellSkin
reflectively access its behavior
find the mousePressed handler in the behavior's inputMap
replace the original handler with a custom handler that replaces the mouseEvent coming from the middle button by a mouseEvent coming from the primary button
make the custom TableCellSkin the default by css
Note: the TableRowSkin which is responsible for handling mouseEvents in the free space at the right of the table doesn't separate out the middle button, so currently nothing to do. If that changes in future, simple apply the same trick as for the table cells.
Example:
public class TableRowCustomMouse extends Application {
public static class CustomMouseTableCellSkin<T, S> extends TableCellSkin<T, S> {
EventHandler<MouseEvent> original;
public CustomMouseTableCellSkin(TableCell<T, S> control) {
super(control);
adjustMouseBehavior();
}
private void adjustMouseBehavior() {
// dirty: reflective access to behavior, use your custom reflective accessor
TableCellBehavior<T, S> behavior =
(TableCellBehavior<T, S>) FXUtils.invokeGetFieldValue(TableCellSkin.class, this, "behavior");
InputMap<TableCell<T, S>> inputMap = behavior.getInputMap();
ObservableList<Mapping<?>> mappings = inputMap.getMappings();
List<Mapping<?>> pressedMapping = mappings.stream()
.filter(mapping -> mapping.getEventType() == MouseEvent.MOUSE_PRESSED)
.collect(Collectors.toList());
if (pressedMapping.size() == 1) {
Mapping<?> originalMapping = pressedMapping.get(0);
original = (EventHandler<MouseEvent>) pressedMapping.get(0).getEventHandler();
if (original != null) {
EventHandler<MouseEvent> replaced = this::replaceMouseEvent;
mappings.remove(originalMapping);
mappings.add(new MouseMapping(MouseEvent.MOUSE_PRESSED, replaced));
}
}
}
private void replaceMouseEvent(MouseEvent e) {
MouseEvent replaced = e;
if (e.isMiddleButtonDown()) {
replaced = new MouseEvent(e.getSource(), e.getTarget(), e.getEventType(),
e.getX(), e.getY(),
e.getScreenX(), e.getScreenY(),
MouseButton.PRIMARY,
e.getClickCount(),
e.isShiftDown(), e.isControlDown(), e.isAltDown(), e.isMetaDown(),
true, false, false,
e.isSynthesized(), e.isPopupTrigger(), e.isStillSincePress(),
null
);
}
original.handle(replaced);
}
}
private Parent createContent() {
TableView<Person> table = new TableView<>(Person.persons());
TableColumn<Person, String> first = new TableColumn("First Name");
first.setCellValueFactory(cc -> cc.getValue().firstNameProperty());
TableColumn<Person, String> last = new TableColumn<>("Last Name");
last.setCellValueFactory(cc -> cc.getValue().lastNameProperty());
table.getColumns().addAll(first, last);
BorderPane content = new BorderPane(table);
return content;
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
// load the default css
stage.getScene().getStylesheets()
.add(getClass().getResource("customtablecellskin.css").toExternalForm());
stage.setTitle(FXUtils.version());
stage.show();
}
public static void main(String[] args) {
launch(args);
}
#SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(TableRowCustomMouse.class.getName());
}
The css with custom skin for TableCell:
.table-cell {
-fx-skin: "<yourpackage>.TableRowCustomMouse$CustomMouseTableCellSkin";
}
Accepted #kleopatra's approach as an answer. However the solution to my question is a bit different to #kleopatra's answer. But the core idea is still the same.
I used the approach to override the doSelect method of TableCellBehavior
#Override
protected void doSelect(double x, double y, MouseButton button, int clickCount, boolean shiftDown, boolean shortcutDown) {
MouseButton btn = button;
if (button == MouseButton.MIDDLE) {
btn = MouseButton.PRIMARY;
}
super.doSelect(x, y, btn, clickCount, shiftDown, shortcutDown);
}
Below is the working demo which solved my issue:
DemoClass:
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class TableRowSelectionOnMiddleButtonDemo extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
ObservableList<Person> persons = FXCollections.observableArrayList();
for (int i = 0; i < 4; i++) {
persons.add(new Person("First name" + i, "Last Name" + i));
}
CustomTableView<Person> tableView = new CustomTableView<>();
TableColumn<Person, String> fnCol = new TableColumn<>("First Name");
fnCol.setCellValueFactory(param -> param.getValue().firstNameProperty());
TableColumn<Person, String> lnCol = new TableColumn<>("Last Name");
lnCol.setCellValueFactory(param -> param.getValue().lastNameProperty());
tableView.getColumns().addAll(fnCol, lnCol);
tableView.getItems().addAll(persons);
tableView.setRowFactory(new Callback<TableView<Person>, TableRow<Person>>() {
#Override
public TableRow<Person> call(TableView<Person> param) {
return new TableRow<Person>() {
{
/* This will fix my issue, but I dont want each tableView rowfactory to set this behavior.*/
// addEventHandler(MouseEvent.MOUSE_PRESSED,e->{
// getTableView().getSelectionModel().select(getItem());
// });
}
#Override
protected void updateItem(Person item, boolean empty) {
super.updateItem(item, empty);
}
};
}
});
VBox sp = new VBox();
sp.setAlignment(Pos.TOP_LEFT);
sp.getChildren().addAll(tableView);
Scene sc = new Scene(sp);
sc.getStylesheets().add(this.getClass().getResource("tableRowSelectionOnMiddleButton.css").toExternalForm());
primaryStage.setScene(sc);
primaryStage.show();
}
public static void main(String... a) {
Application.launch(a);
}
/**
* My custom tableView.
*
* #param <S>
*/
class CustomTableView<S> extends TableView<S> {
public CustomTableView() {
// A lot of custom behavior is included to this TableView.
}
}
class Person {
private StringProperty firstName = new SimpleStringProperty();
private StringProperty lastName = new SimpleStringProperty();
public Person(String fn, String ln) {
setFirstName(fn);
setLastName(ln);
}
public String getFirstName() {
return firstName.get();
}
public StringProperty firstNameProperty() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public String getLastName() {
return lastName.get();
}
public StringProperty lastNameProperty() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName.set(lastName);
}
}
}
CustomTableCellSkin class:
import com.sun.javafx.scene.control.behavior.TableCellBehavior;
import com.sun.javafx.scene.control.skin.TableCellSkinBase;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.input.MouseButton;
public class CustomTableCellSkin<S, T> extends TableCellSkinBase<TableCell<S, T>, TableCellBehavior<S, T>> {
private final TableColumn<S, T> tableColumn;
public CustomTableCellSkin(TableCell<S, T> tableCell) {
super(tableCell, new CustomTableCellBehavior<S, T>(tableCell));
this.tableColumn = tableCell.getTableColumn();
super.init(tableCell);
}
#Override
protected BooleanProperty columnVisibleProperty() {
return tableColumn.visibleProperty();
}
#Override
protected ReadOnlyDoubleProperty columnWidthProperty() {
return tableColumn.widthProperty();
}
}
class CustomTableCellBehavior<S, T> extends TableCellBehavior<S, T> {
public CustomTableCellBehavior(TableCell<S, T> control) {
super(control);
}
#Override
protected void doSelect(double x, double y, MouseButton button, int clickCount, boolean shiftDown, boolean shortcutDown) {
MouseButton btn = button;
if (button == MouseButton.MIDDLE) {
btn = MouseButton.PRIMARY;
}
super.doSelect(x, y, btn, clickCount, shiftDown, shortcutDown);
}
}
tableRowSelectionOnMiddleButton.css
.table-cell {
-fx-skin: "<package>.CustomTableCellSkin";
}

Is it possible to add multiple table view using a single Controller class and multiple entity Class in Java FX?

My question is about JavaFX-9.
I am trying to handle two table view in one FXML Controller class. But it is giving me a Null pointer exception. How to solve this?
The first TableView (studentTable) is working and second table(rTable) is not working.
home.java:
package Home;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class home extends Application{
public void start(Stage stage)throws Exception{
Parent root=(Parent) FXMLLoader.load(getClass().getResource("Home.fxml"));
Scene scene=new Scene(root);
stage.setScene(scene);
stage.setTitle("Result Analysis System");
stage.show();
}
public static void main(String[] args){
launch(args);
}
}
homeController.java
package Home;
/*_____________________
Error in this file
_____________________*/
import dbUtils.dbConnection;
//import Home.rControl;
//import Home.resultData;
import javafx.collections.FXCollections;
import javafx.collections.ObservableArray;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
//import javafx.scene.control.DatePicker;
import javafx.scene.control.cell.PropertyValueFactory;
//import Home.resultData;
import java.net.URL;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.ResourceBundle;
public class homeController /*extends resultController*/ implements Initializable{
//Student tab
#FXML
private TextField usn;
#FXML
private TextField name;
#FXML
protected TableView<studentData> studentTable;
#FXML
protected TableColumn<studentData,String> USNcolumn;
#FXML
protected TableColumn<studentData,String> Namecolumn;
//Result tab
#FXML
private TextField rusn;
#FXML
private ComboBox<option> rSelectSem;
#FXML
private TextField rSub1;
#FXML
private TextField rSub2;
#FXML
private TextField rSub3;
#FXML
private TextField rSub4;
#FXML
private TextField rSub5;
#FXML
private TextField rSub6;
#FXML
private TextField rSub7;
#FXML
private TextField rSub8;
#FXML
private Button rAdd;
#FXML
private Button rLoad;
#FXML
private Button rClear;
#FXML
private ComboBox<option> rSelectSem1;
#FXML
private Button rLoad1;
#FXML
private TableView<resultData> rTable;
#FXML
private TableColumn<resultData,String> rColusn;
#FXML
private TableColumn<resultData,String> rColname;
#FXML
private TableColumn<resultData,Integer> rColsub1;
#FXML
private TableColumn<resultData,Integer> rColsub2;
#FXML
private TableColumn<resultData,Integer> rColsub3;
#FXML
private TableColumn<resultData,Integer> rColsub4;
#FXML
private TableColumn<resultData,Integer> rColsub5;
#FXML
private TableColumn<resultData,Integer> rColsub6;
#FXML
private TableColumn<resultData,Integer> rColsub7;
#FXML
private TableColumn<resultData,Integer> rColsub8;
#FXML
private TableColumn<resultData,Integer> rColtotal;
//Analyze tab
#FXML
private ComboBox<option> aSelectSem;
#FXML
private Button aHighmarks;
#FXML
private Button aPassedstudent;
#FXML
private Button aFailedstudent;
#FXML
private Button aListallstudent;
#FXML
private Button adistiction;
#FXML
private Button aFirstclass;
#FXML
private Button aSecondclass;
#FXML
private TableView<analysisData> aTable;
#FXML
private TableColumn<analysisData,String> aColusn;
#FXML
private TableColumn<analysisData,String> aColname;
#FXML
private TableColumn<analysisData,Integer> aColsub1;
#FXML
private TableColumn<analysisData,Integer> aColsub2;
#FXML
private TableColumn<analysisData,Integer> aColsub3;
#FXML
private TableColumn<analysisData,Integer> aColsub4;
#FXML
private TableColumn<analysisData,Integer> aColsub5;
#FXML
private TableColumn<analysisData,Integer> aColsub6;
#FXML
private TableColumn<analysisData,Integer> aColsub7;
#FXML
private TableColumn<analysisData,Integer> aColsub8;
#FXML
private TableColumn<analysisData,Integer> aColtotal;
protected dbConnection dc;
protected ObservableList<studentData> data;
//private ObservableList<resultData> list;
protected String sql = "SELECT * FROM studentDet";
#Override
public void initialize(URL url, ResourceBundle rb){
this.dc = new dbConnection();
this.rSelectSem.setItems(FXCollections.observableArrayList(option.values()));
this.rSelectSem1.setItems(FXCollections.observableArrayList(option.values()));
this.aSelectSem.setItems(FXCollections.observableArrayList(option.values()));
// rTable.setItems(list);
}
//Load student data in student tab
//This is working
#FXML
private void loadStudentData(ActionEvent event) throws SQLException{
try {
Connection conn = dbConnection.getConnection();
this.data = FXCollections.observableArrayList();
ResultSet rs = conn.createStatement().executeQuery(sql);
while (rs.next()){
this.data.add(new studentData(rs.getString(1),rs.getString(2)));
}
}
catch (SQLException e){
System.err.println("error" + e);
}
this.USNcolumn.setCellValueFactory(new PropertyValueFactory<studentData,String>("USN"));
this.Namecolumn.setCellValueFactory(new PropertyValueFactory<studentData,String>( "Name"));
this.studentTable.setItems(null);
this.studentTable.setItems(this.data);
}
//This is working
//Add student in student tab
#FXML
private void addStudent(ActionEvent actionEvent) {
String sqlInsert="INSERT INTO studentDet(USN,Name) VALUES(?,?)";
try {
//here the name is same but it doesn't matter because this is a local variable
Connection conn=dbConnection.getConnection();
PreparedStatement statement=conn.prepareStatement(sqlInsert);
statement.setString(1,this.usn.getText());
statement.setString(2,this.name.getText());
statement.execute();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//This is working
//Clear feilds in Student tab
#FXML
private void clearFields(ActionEvent actionEvent)
{
this.usn.setText("");
this.name.setText("");
}
#FXML
private void rclearFields(ActionEvent actionEvent)
{
this.rusn.setText("");
this.rSub1.setText("");
this.rSub2.setText("");
this.rSub3.setText("");
this.rSub4.setText("");
this.rSub5.setText("");
this.rSub6.setText("");
this.rSub7.setText("");
this.rSub8.setText("");
}
//This is working
//Add marks to sem in Result tab
#FXML
private void addSemMarks(ActionEvent actionEvent)
{
String sqlInsert;
try {
switch (((option) this.rSelectSem.getValue()).toString()) {
case "SEM1":
sqlInsert="INSERT INTO SEM1(USN,SUB1E,SUB2E,SUB3E,SUB4E,SUB5E,SUB6E,SUB7E,SUB8E) VALUES(?,?,?,?,?,?,?,?,?)";
semMarksAdd(sqlInsert);
break;
case "SEM2":
sqlInsert="INSERT INTO SEM2(USN,SUB1E,SUB2E,SUB3E,SUB4E,SUB5E,SUB6E,SUB7E,SUB8E) VALUES(?,?,?,?,?,?,?,?,?)";
semMarksAdd(sqlInsert);
break;
case "SEM3":
sqlInsert="INSERT INTO SEM3(USN,SUB1E,SUB2E,SUB3E,SUB4E,SUB5E,SUB6E,SUB7E,SUB8E) VALUES(?,?,?,?,?,?,?,?,?)";
semMarksAdd(sqlInsert);
break;
case "SEM4":
sqlInsert="INSERT INTO SEM4(USN,SUB1E,SUB2E,SUB3E,SUB4E,SUB5E,SUB6E,SUB7E,SUB8E) VALUES(?,?,?,?,?,?,?,?,?)";
semMarksAdd(sqlInsert);
break;
case "SEM5":
sqlInsert="INSERT INTO SEM5(USN,SUB1E,SUB2E,SUB3E,SUB4E,SUB5E,SUB6E,SUB7E,SUB8E) VALUES(?,?,?,?,?,?,?,?,?)";
semMarksAdd(sqlInsert);
break;
case "SEM6":
sqlInsert="INSERT INTO SEM6(USN,SUB1E,SUB2E,SUB3E,SUB4E,SUB5E,SUB6E,SUB7E,SUB8E) VALUES(?,?,?,?,?,?,?,?,?)";
semMarksAdd(sqlInsert);
break;
case "SEM7":
sqlInsert="INSERT INTO SEM7(USN,SUB1E,SUB2E,SUB3E,SUB4E,SUB5E,SUB6E,SUB7E,SUB8E) VALUES(?,?,?,?,?,?,?,?,?)";
semMarksAdd(sqlInsert);
break;
case "SEM8":
sqlInsert="INSERT INTO SEM8(USN,SUB1E,SUB2E,SUB3E,SUB4E,SUB5E,SUB6E,SUB7E,SUB8E) VALUES(?,?,?,?,?,?,?,?,?)";
semMarksAdd(sqlInsert);
break;
}
}
catch (Exception ex){
ex.printStackTrace();
}
}
private void semMarksAdd(String sqlinsert){
//String sqlInsert="INSERT INTO SEM1(USN,SUB1E,SUB2E,SUB3E,SUB4E,SUB5E,SUB6E,SUB7E,SUB8E) VALUES(?,?,?,?,?,?,?,?,?)";
try {
//here the name is same but it doesn't matter because this is a local variable
Connection conn=dbConnection.getConnection();
PreparedStatement statement=conn.prepareStatement(sqlinsert);
statement.setString(1,this.rusn.getText());
statement.setString(2,this.rSub1.getText());
statement.setString(3,this.rSub2.getText());
statement.setString(4,this.rSub3.getText());
statement.setString(5,this.rSub4.getText());
statement.setString(6,this.rSub5.getText());
statement.setString(7,this.rSub6.getText());
statement.setString(8,this.rSub7.getText());
statement.setString(9,this.rSub8.getText());
statement.execute();
conn.close();
}
catch (SQLException e) {
e.printStackTrace();
}
}
//#########################################################Stable upto here
//From here it is giving error
//TO LOAD STUDENT MARKS DATA IN RESULT TABLE
/*#FXML
private void loadResultData(ActionEvent actionEvent){
String sqlLoad;
try {
switch (((option) this.rSelectSem.getValue()).toString()) {
case "SEM1":
sqlLoad = "SELECT USN,SUB1E,SUB2E,SUB3E,SUB4E,SUB5E,SUB6E,SUB7E,SUB8E,TOTAL FROM SEM1";
loadRdata(sqlLoad);
break;
case "SEM2":
sqlLoad = "SELECT USN,SUB1E,SUB2E,SUB3E,SUB4E,SUB5E,SUB6E,SUB7E,SUB8E,TOTAL FROM SEM2";
loadRdata(sqlLoad);
break;
case "SEM3":
sqlLoad = "SELECT USN,SUB1E,SUB2E,SUB3E,SUB4E,SUB5E,SUB6E,SUB7E,SUB8E,TOTAL FROM SEM3";
loadRdata(sqlLoad);
break;
case "SEM4":
sqlLoad = "SELECT USN,SUB1E,SUB2E,SUB3E,SUB4E,SUB5E,SUB6E,SUB7E,SUB8E,TOTAL FROM SEM4";
loadRdata(sqlLoad);
break;
case "SEM5":
sqlLoad = "SELECT USN,SUB1E,SUB2E,SUB3E,SUB4E,SUB5E,SUB6E,SUB7E,SUB8E,TOTAL FROM SEM5";
loadRdata(sqlLoad);
break;
case "SEM6":
sqlLoad = "SELECT USN,SUB1E,SUB2E,SUB3E,SUB4E,SUB5E,SUB6E,SUB7E,SUB8E,TOTAL FROM SEM6";
loadRdata(sqlLoad);
break;
case "SEM7":
sqlLoad = "SELECT USN,SUB1E,SUB2E,SUB3E,SUB4E,SUB5E,SUB6E,SUB7E,SUB8E,TOTAL FROM SEM7";
loadRdata(sqlLoad);
break;
case "SEM8":
sqlLoad = "SELECT USN,SUB1E,SUB2E,SUB3E,SUB4E,SUB5E,SUB6E,SUB7E,SUB8E,TOTAL FROM SEM8";
loadRdata(sqlLoad);
break;
}
}
catch (Exception ex){
ex.printStackTrace();
}
}*/
#FXML
public void loadResultData(ActionEvent actionEvent) {
String sqlLoad = "SELECT USN,SUB1E,SUB2E,SUB3E,SUB4E,SUB5E,SUB6E,SUB7E,SUB8E,TOTAL FROM SEM1";
loadRdata(sqlLoad); //Error here
}
/*private void loadResultData(ActionEvent actionEvent) {
String sqlLoad = "SELECT USN,SUB1E,SUB2E,SUB3E,SUB4E,SUB5E,SUB6E,SUB7E,SUB8E,TOTAL FROM SEM1";
rc.loadRdata(sqlLoad);
}*/
private ObservableList<resultData> list = FXCollections.observableArrayList();
private void loadRdata(String sqlLoad) {
try {
Connection conn = dbConnection.getConnection();
this.list = FXCollections.observableArrayList();
//System.out.println("Hello"); //working
ResultSet rs = conn.createStatement().executeQuery(sqlLoad);
while (rs.next()) {
//System.out.println(rs.getString(1)); //Working
this.list.add(new resultData(rs.getString(1), rs.getInt(2), rs.getInt(3), rs.getInt(4), rs.getInt(5), rs.getInt(6), rs.getInt(7), rs.getInt(8), rs.getInt(9), rs.getInt(10)));
}
// this.rColusn.setCellValueFactory("Helloworld"); //working
this.rColusn.setCellValueFactory(new PropertyValueFactory<resultData, String>("rColUsn"));
this.rColsub1.setCellValueFactory(new PropertyValueFactory<resultData, Integer>("rColSub1"));
this.rColsub2.setCellValueFactory(new PropertyValueFactory<resultData, Integer>("rColSub2"));
this.rColsub3.setCellValueFactory(new PropertyValueFactory<resultData, Integer>("rColSub3"));
this.rColsub4.setCellValueFactory(new PropertyValueFactory<resultData, Integer>("rColSub4"));
this.rColsub5.setCellValueFactory(new PropertyValueFactory<resultData, Integer>("rColSub5"));
this.rColsub6.setCellValueFactory(new PropertyValueFactory<resultData, Integer>("rColSub6"));
this.rColsub7.setCellValueFactory(new PropertyValueFactory<resultData, Integer>("rColSub7"));
this.rColsub8.setCellValueFactory(new PropertyValueFactory<resultData, Integer>("rColSub8"));
this.rColtotal.setCellValueFactory(new PropertyValueFactory<resultData, Integer>("rColTotal"));
System.out.println("after block1");
list.clear();
System.out.println("after block2");
rTable.setItems(list);// CAUSING NULL POINTER EXCEPTION
System.out.println("after block3");//NOT PRINTING
}
catch (Exception e) {//System.out.println(" "+e);
System.err.println("error" + e);
}
}
}
resultData.java
package Home;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class resultData {
private final StringProperty rColUsn;
//private final StringProperty rColName;
private final IntegerProperty rColSub1;
private final IntegerProperty rColSub2;
private final IntegerProperty rColSub3;
private final IntegerProperty rColSub4;
private final IntegerProperty rColSub5;
private final IntegerProperty rColSub6;
private final IntegerProperty rColSub7;
private final IntegerProperty rColSub8;
private final IntegerProperty rColTotal;
public resultData(String usn,Integer sub1,Integer sub2,Integer sub3,Integer sub4,Integer sub5,Integer sub6,Integer sub7,Integer sub8,Integer total){
System.out.println("result data");// TWO TIMES IT WORKS,BUT THE THERE ARE 10 COLUMNS AND IT SHOULD PRINT 10 TIMES.
rColUsn = new SimpleStringProperty(usn);
//this.rColName = new SimpleStringProperty(name);
rColSub1 = new SimpleIntegerProperty(sub1);
rColSub2 = new SimpleIntegerProperty(sub2);
rColSub3 = new SimpleIntegerProperty(sub3);
rColSub4 = new SimpleIntegerProperty(sub4);
rColSub5 = new SimpleIntegerProperty(sub5);
rColSub6 = new SimpleIntegerProperty(sub6);
rColSub7 = new SimpleIntegerProperty(sub7);
rColSub8 = new SimpleIntegerProperty(sub8);
rColTotal = new SimpleIntegerProperty(total);
}
public String getrColUsn() {
return rColUsn.get();
}
public StringProperty rColUsnProperty() {
return rColUsn;
}
public void setrColUsn(String rColUsn) {
this.rColUsn.set(rColUsn);
}
public int getrColSub1() {
return rColSub1.get();
}
public IntegerProperty rColSub1Property() {
return rColSub1;
}
public void setrColSub1(int rColSub1) {
this.rColSub1.set(rColSub1);
}
public int getrColSub2() {
return rColSub2.get();
}
public IntegerProperty rColSub2Property() {
return rColSub2;
}
public void setrColSub2(int rColSub2) {
this.rColSub2.set(rColSub2);
}
public int getrColSub3() {
return rColSub3.get();
}
public IntegerProperty rColSub3Property() {
return rColSub3;
}
public void setrColSub3(int rColSub3) {
this.rColSub3.set(rColSub3);
}
public int getrColSub4() {
return rColSub4.get();
}
public IntegerProperty rColSub4Property() {
return rColSub4;
}
public void setrColSub4(int rColSub4) {
this.rColSub4.set(rColSub4);
}
public int getrColSub5() {
return rColSub5.get();
}
public IntegerProperty rColSub5Property() {
return rColSub5;
}
public void setrColSub5(int rColSub5) {
this.rColSub5.set(rColSub5);
}
public int getrColSub6() {
return rColSub6.get();
}
public IntegerProperty rColSub6Property() {
return rColSub6;
}
public void setrColSub6(int rColSub6) {
this.rColSub6.set(rColSub6);
}
public int getrColSub7() {
return rColSub7.get();
}
public IntegerProperty rColSub7Property() {
return rColSub7;
}
public void setrColSub7(int rColSub7) {
this.rColSub7.set(rColSub7);
}
public int getrColSub8() {
return rColSub8.get();
}
public IntegerProperty rColSub8Property() {
return rColSub8;
}
public void setrColSub8(int rColSub8) {
this.rColSub8.set(rColSub8);
}
public int getrColTotal() {
return rColTotal.get();
}
public IntegerProperty rColTotalProperty() {
return rColTotal;
}
public void setrColTotal(int rColTotal) {
this.rColTotal.set(rColTotal);
}
}
studentData.java
package Home;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
public class studentData{
private final StringProperty USN;
private final StringProperty Name;
public studentData(String usn,String name){
this.USN=new SimpleStringProperty(usn);
this.Name=new SimpleStringProperty(name);
}
public String getUSN() {
return USN.get();
}
public StringProperty USNProperty() {
return USN;
}
public void setUSN(String USN) {
this.USN.set(USN);
}
public String getName() {
return Name.get();
}
public StringProperty nameProperty() {
return Name;
}
public void setName(String name) {
this.Name.set(name);
}
}
Commnd Line Output:
"C:\Program Files\Java\jdk-9.0.1\bin\java" "-javaagent:C:\Program
Files\JetBrains\IntelliJ IDEA 2017.2.5\lib\idea_rt.jar=55700:C:\Program
Files\JetBrains\IntelliJ IDEA 2017.2.5\bin" -Dfile.encoding=UTF-8 -
/* WHEN CLICKING TABLE DISPLAY BUTTON FIRST TIME */
result data
result data
after block1
after block2
errorjava.lang.NullPointerException
/* WHEN CLICKING TABLE DISPLAY BUTTON SECOND TIME*/
errorjava.lang.NullPointerException
result data
result data
after block1
after block2
You need to set a value into the table.
This is not going to work (remove from your loadRdata method):
this.rTable.setItems(null); // giving error here:Null pointer Exception
this.rTable.setItems(this.list);
Initialize your ObservableList first:
private ObservableList<resultData> list = FXCollections.observableArrayList();
Then in your method loadRdata clear the values rather than initializing like you are now.
list.clear();
At the end of you initialize() method you can add the values
rTable.setItems(list);

JavaFX Increasing the number failed

In my Example I am trying to make a counter;
It starts with 0 and everytime i click the button it must be increase one more.
I add a PREFIX and than my increaseNumber() method have to function but it doesn't work.
Did I create my EventHandler false?
Here are my classes:
import java.util.Observable;
public class Number extends Observable {
int number = 0;
public int getZahl() {
return number;
}
public void setZahl(int number) {
this.number = number;
}
public void increaseTheNumber() {
int oldNumber = number;
number++;
setChanged();
notifyObservers(oldNumber);
}
public String toString() {
return number + " ";
}
}
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class NumberGUI extends Application {
private Button btn;
private Label lbl;
private Label lbl2;
private Number num;
private static String PREFIX = "The new number is: ";
public static void main(String[] args) {
launch(args);
}
#Override
public void init() throws Exception {
num = new Number();
initButton();
initLabels();
}
private void initButton() {
btn = new Button();
btn.setText("Click to Increase");
btn.setPrefHeight(50);
btn.setPrefWidth(200);
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
num.increaseTheNumber();
}
});
}
private void initLabels() {
lbl2=new Label(PREFIX+num.getZahl());
}
private Parent createSceneGraph() {
BorderPane root = new BorderPane();
root.setCenter(lbl);
root.setBottom(lbl2);
GridPane grid = new GridPane();
grid.addColumn(0,btn);
root.setCenter(grid);
return root;
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("Counter!");
primaryStage.setScene(new Scene(createSceneGraph(),300,250));
primaryStage.show();
}
}
public class Main {
public static void main(String[] args) {
Number num = new Number();
ObserveNumber on = new ObserveNumber();
num.addObserver(on);
num.increaseTheNumber();
}
}
If you print your stored number every time you invoke increaseTheNumber method you will see that the number is indeed being increased.
public void increaseTheNumber() {
int oldNumber = number;
number++;
setChanged();
notifyObservers(oldNumber);
System.out.println(number);
}
Your label doesn't change because you set its content only once, when the number is still zero.
lbl2=new Label(PREFIX+num.getZahl());
Since your Number is an observable, you can add an Observer to it that will update the label every time it's been notified.
num.addObserver((o, arg) -> {
lbl2.setText(PREFIX+num.getZahl());
});
This becomes much easier, if you use JavaFX properties, like IntegerProperty, since this allows you to simply bind the text property:
#Override
public void start(Stage primaryStage) {
Label label = new Label();
IntegerProperty property = new SimpleIntegerProperty(1);
Button btn = new Button("Increment");
btn.setOnAction((ActionEvent event) -> {
// increment the property
property.set(property.get()+1);
});
// format the property by prepending the prefix string
label.textProperty().bind(property.asString("The new number is: %d"));
Scene scene = new Scene(new VBox(label, btn));
primaryStage.setScene(scene);
primaryStage.show();
}

Custom ListCell implements InvalidationListener, repaint components

I have a custom ListCell implemented that contains a BorderPane layout with some components.
The cell registers itself to the item. So when the duration of the item changes the invalidated method is called.
In this method I set the text of the duration label. My problem is now the method is called but the label is not repainted.
I think if setText is called the cell should repaint. It is possible to manually repaint the cell or the Label.?
public static class ListItemCell extends ListCell<MusicListItem> implements InvalidationListener{
private AnchorPane listItem;
private Label artist;
private Label title;
private Label duration;
private BorderPane borderPane;
private FlowPane flowPane;
public ListItemCell() {
initCellLayout();
}
public ListItemCell(final LogicInterfaceFX logic) {
...
}
public void initCellLayout() {
try {
this.listItem = (AnchorPane) FXMLLoader.load(getClass().getResource("/de/roth/jsona/view/themes/" + Config.getInstance().THEME + "/" + "layout_list_item.fxml"));
} catch (Exception e) {
e.printStackTrace();
}
this.borderPane = (BorderPane) listItem.getChildren().get(0);
this.flowPane = (FlowPane) borderPane.getLeft();
this.artist = (Label) flowPane.getChildren().get(0);
this.artist.getStyleClass().add(defaultTextClass);
this.title = (Label) flowPane.getChildren().get(1);
this.title.getStyleClass().add(defaultTextClass);
this.duration = (Label) borderPane.getRight();
this.duration.getStyleClass().add(defaultTextClass);
this.setGraphic(listItem);
}
#Override
public void updateItem(MusicListItem item, boolean empty) {
super.updateItem(item, empty);
if (!empty && item != null) {
item.addListener(this);
item.durationProperty().addListener(this);
// Duration
this.duration.setText(item.getDuration());
// Artist / Title
if (item.getArtist() != null) {
this.artist.setText(item.getArtist());
this.title.setText(" - " + item.getTitle());
} else {
this.artist.setText("");
this.title.setText(item.getFile().getName());
}
} else {
this.artist.setText("");
this.title.setText("");
this.duration.setText("");
}
}
#Override
public void invalidated(Observable observable) {
System.out.println("INVALIDATE!!!" + getItem().getFile().getAbsolutePath());
this.duration.setText(getItem().getDuration());
}
}
You have a bug in there: you need to make sure you remove listeners from old items when the item is updated. Remember that ListCells are reused, so updateItem(...) is called multiple times during the lifespan of your ListView.
I don't know if that's what is causing it to fail to update. This works for me:
import java.util.Random;
import javafx.application.Application;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class ListViewUpdatableProperties extends Application {
#Override
public void start(Stage primaryStage) {
final ListView<Item> listView = new ListView<>();
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
final Random rng = new Random();
for (int i=1; i<=20; i++) {
listView.getItems().add(new Item("Item "+i, rng.nextInt(100)));
}
BorderPane root = new BorderPane();
root.setCenter(listView);
listView.setCellFactory(new Callback<ListView<Item>, ListCell<Item>>() {
#Override
public ListCell<Item> call(ListView<Item> param) {
return new ItemListCell();
}
});
HBox controls = new HBox();
controls.setPadding(new Insets(5));
Button incButton = new Button("Increment selected");
incButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
for (Item item : listView.getSelectionModel().getSelectedItems()) {
item.increment();
}
}
});
controls.getChildren().add(incButton);
root.setBottom(controls);
Scene scene = new Scene(root, 250, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static class ItemListCell extends ListCell<Item> implements InvalidationListener {
private final HBox hbox ;
private final Label nameLabel ;
private final Label valueLabel ;
public ItemListCell() {
hbox = new HBox(5);
nameLabel = new Label();
valueLabel = new Label();
hbox.getChildren().addAll(nameLabel, valueLabel);
setGraphic(hbox);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
#Override
public void updateItem(Item item, boolean empty) {
Item oldItem = getItem();
if (oldItem != null) {
oldItem.valueProperty().removeListener(this);
}
super.updateItem(item, empty);
if (item != null) {
nameLabel.setText(item.getName());
valueLabel.setText(String.valueOf(item.getValue()));
item.valueProperty().addListener(this);
} else {
nameLabel.setText("");
valueLabel.setText("");
}
}
#Override
public void invalidated(Observable observable) {
final int value = getItem().getValue();
System.out.println("Invalidated: item is "+getItem().getName() + " with value "+value);
valueLabel.setText(String.valueOf(value));
}
}
public static class Item {
public Item(String name, int value) {
setName(name);
setValue(value);
}
private final StringProperty name = new SimpleStringProperty(this, "name");
public StringProperty nameProperty() {
return name ;
}
public String getName() {
return name.get();
}
public void setName(String name) {
this.name.set(name);
}
private final IntegerProperty value = new SimpleIntegerProperty(this, "value");
public IntegerProperty valueProperty() {
return value ;
}
public int getValue() {
return value.get();
}
public void setValue(int value) {
this.value.set(value);
}
public void increment() {
value.set(value.get()+1);
}
}
public static void main(String[] args) {
launch(args);
}
}
As stated in the other answer, there is no repaint() method in JavaFX. If you wire things up correctly, when the properties are invalidated, it will know to repaint.
JavaFX uses a "retained mode" rendering model whereas Swing uses an "immediate mode".
See: Retained Mode Versus Immediate Mode (Windows)
So, you don't have a direct repaint() method.

Resources