I am super new to JavaFX and for my checkers game I want to display who's turn it is above my board. I have read through multiple very similar questions, but it doesn't seem to get me where I want to go. I hope you can help me!
I have a FXML file with the following code:
<HBox fx:id="topBox" alignment="TOP_CENTER" prefHeight="45.0" prefWidth="200.0" spacing="10.0" BorderPane.alignment="CENTER" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.SampleController">
<children>
<Label fx:id="labelTurn" text="test" />
<Button fx:id="buttonResign" alignment="CENTER" mnemonicParsing="false" onAction="#doResign" prefHeight="25.0" prefWidth="80.0" text="Resign" />
</children>
</HBox>
In my main:
public class Main extends Application {
Checkers content = new Checkers();
BorderPane border = new BorderPane();
#Override
public void start(Stage primaryStage) throws Exception {
try {
border.setTop(FXMLLoader.load(getClass().getResource("Sample.fxml")));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
border.setCenter(createContent());
Scene scene = new Scene(border);
primaryStage.setTitle("Checkers from main");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
My controller:
public class SampleController {
#FXML
private Label labelTurn;
#FXML
private HBox topBox;
#FXML
private Button buttonResign;
private Checkers content = new Checkers();
#FXML
void doResign(ActionEvent event) {
content.resign();
// System.exit(0); //TODO eine tatsächliche Methode eingeben!!
}
public void setLabelTurn(Label labelTurn) {
this.labelTurn = labelTurn;
String setLabelText() {
String s = " ";
if (content.getWhiteTurnPropValue()) {
labelTurn.setText("White, it is your turn.");
s = "White, it is your turn.";
return s;
} else {
labelTurn.setText("Black, it is your turn.");
s = "Black, it is your turn.";
return s;
}
}
#FXML
void initialize() {
assert buttonResign != null : "fx:id=\"buttonResign\" was not injected: check your FXML file 'Sample.fxml'.";
assert topBox != null : "fx:id=\"topBox\" was not injected: check your FXML file 'Sample.fxml'.";
assert labelTurn != null : "fx:id=\"labelTurn\" was not injected: check your FXML file 'Sample.fxml'.";
labelTurn.textProperty().setValue(setLabelText());
}
}
My game data has a BooleanProperty to show who's turn it is. It gets changed after every successful move.
BooleanProperty whiteTurnProp = new SimpleBooleanProperty(true);
public BooleanProperty getWhiteTurnProp() {
return whiteTurnProp;
}
public void setWhiteTurnPropValue(Boolean whiteTurn) {
this.whiteTurnProp.setValue(whiteTurn);
}
public boolean getWhiteTurnPropValue() {
return this.whiteTurnProp.getValue();
}
As far as I have understood, I need to bind the Label to the value, which I have done in the initialize method. But when I play and the value in my game class changed, the label stays the same. How do I update the label? I think I need to add a change listener to something, but I dont know how to do that and more specifically WHERE.
Thank you so much for your help! I shortened my code a bit and hope it is still not too long.
Best,
Lisa
In initialize: labelTurn.textProperty().setValue(setLabelText()); is not a binding, it is a simple set on the property. A really strange one as setLabelText() sets the text of the Label and also return the string which was set, which value is used again to the text of the Label.
You could create a real binding instead:
labelTurn.textProperty().bind(Bindings.createStringBinding(() -> {
String s = " ";
if (content.getWhiteTurnProp().get())
s = "White, it is your turn.";
else
s = "Black, it is your turn.";
return s;
}, content.getWhiteTurnProp()));
Related
Somehow, I cannot add items to my ComboBox variable from another class as it always says either the value of variable is null or the return value of its getter is null.
I use Scene Builder to build Sample.fxml, with controller as Controller class, luanch the whole thing form UserInterface class
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Controller">
<center>
<ComboBox fx:id="myBox" prefWidth="150.0" BorderPane.alignment="CENTER" />
</center>
</BorderPane>
Controller
public class Controller {
#FXML
private ComboBox<String> myBox;
public ComboBox<String> getMyBox() {
return myBox;
}
public void initialize() {
ObservableList<String> defaultTicker = FXCollections.observableArrayList("Something");
myBox.getItems().addAll(defaultTicker);
}
}
UserInterface
public class UserInterface extends Application {
#Override
public void start(Stage primaryStage) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Sample.fxml"));
Parent root = loader.load();
Controller controller = new Controller();
primaryStage.setTitle("Title");
primaryStage.setScene(new Scene(root));
primaryStage.show();
List<String> addStuff = new ArrayList<String>();
addStuff.add("a");
addStuff.add("b");
addStuff.add("c");
controller.getMyBox().getItems().addAll(addStuff);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
After running, the interface appears, with only the option "Something" in it. So I guess it initialize fine?
The interface after launch
What did I do wrong? I couldn't seem to find any solutions so far.
Thanks in advance.
I am working on a project involves RFID technology, what i am trying to do at the moment, once the RFIDTagListener method runs, I basically pass a tag to a reader which its serial number will be sent to a server to get some relevant data back and pop them on a GUI screen. what I have done so far is getting the data upon sending reader's data manually without passing a tag becasue I don't know how to do it otherwise and here is the problem. where I am working on a javafx project and when I tried to put the RFIDTagListener method within the MainController class and on compiling, the taglistner method wouldn't be triggered and just ignored, it's only the GUI screen will be opened.However, I also tried to have RFIDTagListener within the main class but on compiling, the taglistner method would be run first and when it's finished in 5 seconds, my GUI window will be opened next. SO I Don't know where this method should be located exactly. Essentially What I want is to have them both running at the same time, the taglistener running in the background with the GUI window opened simulitniously.
Any recommendation guys would be much appricated.
MainController class :
public class MainController {
RFID rfid = new RFID();
String ReaderNo = null;
String walletJson = new String();
Gson gson = new Gson();
public static String sensorServerURL = "http://localhost:8080/PhidgetServer2019/SensorServerRFIDdata";
walletDAO dao = new walletDAO();
ArrayList<wallet> allwallets = new ArrayList<wallet>();
#FXML VBox ConsultHR;
#FXML private Label message;
#FXML private Label WalletName;
#FXML private ListView<ArrayList<wallet>> list;
#FXML private ListView<ArrayList<wallet>> RoomAList;
#FXML private TableView<wallet> tableViewData;
#FXML private TableColumn<wallet, String> NameColumn;
#FXML private TableColumn<wallet, String> LocationColumn;
#FXML private TableColumn<wallet, String> TagColumn;
public void getTags(ActionEvent event) throws SQLException {
allwallets = dao.getWalletTag();
try {
allwallets = dao.getWalletTag();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(allwallets);
list.getItems().add(allwallets);
}
public MainController() throws PhidgetException {
// Make the RFID Phidget able to detect loss or gain of an rfid card
rfid.addTagListener(new RFIDTagListener() {
// What to do when a tag is found
public void onTag(RFIDTagEvent e) {
try {
ReaderNo = String.valueOf(rfid.getDeviceSerialNumber());
} catch (PhidgetException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
System.out.println("Reader serial number is " + ' '+ReaderNo);
wallet walletData = new wallet("385055");
walletJson = gson.toJson(walletData);
String resultfromserver = sendToServer(walletJson);
System.out.println("DEBUG: data in json : " +resultfromserver);
wallet walletObject = gson.fromJson(resultfromserver, wallet.class);
System.out.println("DEBUG: The wallet's Data: "+' '+ walletObject);
WalletName.setText(walletObject.getWalletName());
}
});
rfid.addTagLostListener(new RFIDTagLostListener() {
// What to do when a tag is lost
public void onTagLost(RFIDTagLostEvent e) {
// optional print, used as debug here
System.out.println("DEBUG: Tag lost: " + e.getTag());
}
});
}
}
Main class :
public class Main extends Application {
//RFID rfid = new RFID();
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Wallet locator !");
try {
Parent root = FXMLLoader.load(getClass().getResource("/application/Main.fxml"));
Scene scene = new Scene(root);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
public static void main(String[] args) throws PhidgetException {
new MainController();
launch(args);
}
}
Main.FXML :
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="269.0" prefWidth="403.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController">
<Button layoutX="149.0" layoutY="251.0" mnemonicParsing="false" onAction="#getTags" prefHeight="46.0" prefWidth="82.0" text="tags" />
<Label fx:id="message" layoutX="139.0" layoutY="209.0" prefHeight="35.0" prefWidth="101.0" />
<ListView id="studentObservableList" fx:id="list" layoutY="209.0" prefHeight="131.0" prefWidth="139.0" />
<TableView fx:id="tableViewData" prefHeight="200.0" prefWidth="231.0" style="-fx-border-color: red;">
<columns>
<TableColumn fx:id="NameColumn" prefWidth="75.0" text="Name"/>
<TableColumn fx:id="LocationColumn" prefWidth="75.0" text="Location" />
<TableColumn fx:id="TagColumn" prefWidth="75.0" text="Tag" />
</columns>
</TableView>
<Button layoutX="251.0" layoutY="14.0" mnemonicParsing="false" onAction="#getWallets" prefHeight="56.0" prefWidth="82.0" text="wallets" />
</AnchorPane>
1st point
The main method is inside the MainController class and it's instantiating itself, which is possible but not conventional.
2nd point
Never put your main method into a controller! Put it into your Main class instead.
3rd point
You must define the application launch inside your main method, by calling Application.launch(). This method will call your overriden start method (among other things) and display the GUI:
public static void main(String[] args) throws PhidgetException {
launch(args);
new MainController();
}
4th point
You don't need to instantiate directly a controller in JavaFX. With your current code you're creating a MainController instance which is unlinked to your GUI; that's why you can see your GUI without interacting with it. You need to remove the new MainController(); line into your main method, then check if the fx:controller attribute is defined in the root of your FXML file.
You can call this controller instance using root.getController() in your start method.
I've been building a cinema booking application and am trying to create a scene that displays movies and showing times. It works when I have used an anchor pane and vbox to display all the information but when I try to insert an additional scroll pane (in scenebuilder) the FXML loader returns a null pointer exception and I cant work out why...
Here is my FXML code
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<AnchorPane prefHeight="598.0" prefWidth="798.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="MovieShowingsController">
<children>
<MenuBar>
<menus>
<Menu mnemonicParsing="false" text="myBookings">
<items>
<MenuItem mnemonicParsing="false" text="Close" />
</items>
</Menu>
</menus>
</MenuBar>
<ScrollPane fx:id="scrollpane" hbarPolicy="NEVER" layoutY="22.0" prefHeight="576.0" prefWidth="798.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="22.0">
<content>
<VBox fx:id="vbox" prefHeight="555.0" prefWidth="740.0" />
</content>
</ScrollPane>
</children>
</AnchorPane>
Here is the controller class
public class MovieShowingsController {
#FXML
private VBox vbox;
private ArrayList<FilmInfo> filmList;
private ArrayList<Screening> screeningList;
private MovieShowings showings;
//FXML loader instance variable to be accessed by dynamic scene controller
private VBox holder;
#FXML
private void initialize() {
}
public MovieShowingsController() {
}
public MovieShowingsController(MovieShowings showings) {
String date = "2019-04-15";
Date sqlDate = Date.valueOf(date);
System.out.println("\n");
System.out.println("***Screenings for " + date + "***");
filmList = new ArrayList();
screeningList = DatabaseConnection.getInstance().retrieveScreeningsForDay(sqlDate);
for (Screening screeningInstance : screeningList) {
if (!filmList.contains(screeningInstance.getFilmInfo())) {
filmList.add(screeningInstance.getFilmInfo());
}
System.out.println(screeningInstance.toString());
}
Collections.sort(screeningList);
this.showings = showings;
//populating FXML instance variable from loader
this.holder = (VBox) showings.getRoot().lookup("#vbox");
buildMovieShowings(holder);
}
private void buildMovieShowings(VBox holder) {
holder.setSpacing(50);
for (int i = 0; i < filmList.size(); i++) {
VBox infoHolder = new VBox();
infoHolder.setSpacing(10);
Label title = new Label(String.format("%s%8s", filmList.get(i).getTitle(),
"(" + filmList.get(i).getRating() + ")"));
title.setStyle("-fx-font: 24 arial;");
Label duration = new Label(String.format("%s%s%s", "Film Length: ",
filmList.get(i).getDuration(), " mins"));
duration.setStyle("-fx-font: 24 arial;");
Label director = new Label(String.format("%s%s", "Directed By: ",
filmList.get(i).getDirector()));
director.setStyle("-fx-font: 24 arial;");
infoHolder.getChildren().addAll(title, duration, director);
HBox timesHolder = new HBox();
timesHolder.setSpacing(10);
for (int j = 0; j < screeningList.size(); j++) {
if (screeningList.get(j).getFilmInfo().equals(filmList.get(i))){
Label time = new Label();
Background black = new Background(new BackgroundFill(Color.BLACK, CornerRadii.EMPTY, Insets.EMPTY));
Background red = new Background(new BackgroundFill(Color.RED, CornerRadii.EMPTY, Insets.EMPTY));
time.setBackground(black);
Screen screen = screeningList.get(j).getScreen();
Screening screening = screeningList.get(j);
time.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e) {
try {
System.out.println(screening.getFilmInfo().getTitle() + screening.getShowTime());
time.setBackground(red);
SeatMap seatMap = new SeatMap();
SeatMapController seatMapController = new SeatMapController(seatMap,
screen);
Scene seatMapScene = seatMap.getScene();
Stage window = (Stage) ((Node) e.getSource()).getScene().getWindow();
window.setResizable(false);
window.setWidth(800);
window.setHeight(600);
window.setScene(seatMapScene);
window.show();
}
catch (IOException ex) {
ex.printStackTrace();
}
}
});
time.setPrefSize(100, 100);
time.setAlignment(Pos.CENTER);
time.setStyle("-fx-border-color: black");
time.setStyle("-fx-font: 22 arial;");
time.setStyle("-fx-text-fill: white");
time.setText(screeningList.get(j).getShowTime());
timesHolder.getChildren().add(time);
}
}
infoHolder.getChildren().add(timesHolder);
holder.getChildren().add(infoHolder);
}
}
}
The main class
public class MovieShowings{
private AnchorPane root;
public MovieShowings() {
try {
root = FXMLLoader.load(getClass().getResource("movieshowings.fxml"));
}
catch(IOException e){
e.printStackTrace();
}
}
public Scene getScene() {
Scene scene = new Scene(root,800,600);
return scene;
}
public AnchorPane getRoot() {
return root;
}
}
and the code that calls it after the user has logged in
if(DatabaseConnection.getInstance().login(Username.getText(), Password.getText())) {
MovieShowings films = new MovieShowings();
MovieShowingsController filmsController = new MovieShowingsController(films);
Scene movieShowings = films.getScene();
Stage window = (Stage) ((Node) e.getSource()).getScene().getWindow();
window.setScene(movieShowings);
window.show();
Any ideas as how to fix this?
Edit: The fx:id 'vbox' is not being accessed from the getRoot() method even though is has been injected into the FXML loader
The reason for this is that ScrollPane adds content, ScrollBars, ect. to the scene during the first layout pass when it's skin is created. This layout pass happens after the JavaFX application thread "regains control" (i.e. you're done with the event handler, Application.start method or similar way of having JavaFX execute your code).
Note that you're using your controller class in a pretty weird way. I recommend using one of the approaches described in the answers to this question to communicate with the controller: Passing Parameters JavaFX FXML
For example:
public class MovieShowings{
private AnchorPane root;
public MovieShowings() {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("movieshowings.fxml"));
root = loader.load();
MovieShowingsController controller = loader.getController();
controller.initMovieShowings(this);
}
catch(IOException e){
e.printStackTrace();
}
}
...
}
public class MovieShowingsController {
...
public void initMovieShowings(MovieShowings showings) {
String date = "2019-04-15";
Date sqlDate = Date.valueOf(date);
System.out.println("\n");
System.out.println("***Screenings for " + date + "***");
filmList = new ArrayList();
screeningList = DatabaseConnection.getInstance().retrieveScreeningsForDay(sqlDate);
for (Screening screeningInstance : screeningList) {
if (!filmList.contains(screeningInstance.getFilmInfo())) {
filmList.add(screeningInstance.getFilmInfo());
}
System.out.println(screeningInstance.toString());
}
Collections.sort(screeningList);
this.showings = showings;
//populating FXML instance variable from loader
// use the injected field here
buildMovieShowings(vbox);
}
...
}
Since you don't actually use the MovieShowings object in your controller, the code could be simplified a bit by doing teh initialisation from a
#FXML
private void initialize()
method in the controller and remove every MovieShowings-related part from the controller code. This way you'd get rid of the necessity to pass it to the controller.
Using a ListView using custom cells could also be an option to display the movies...
I have a root screen which generates a popup and in popup I have a listview with button on it and I want to update the texfield in root screen and close the popupwindow when a button is clicked in popup. Code for popup and its controller.
POPUP
public void display() throws IOException {
Stage window =new Stage();
FXMLLoader loader=new FXMLLoader();
Parent root = loader.load(getClass().getResource("/ProfilePopup.fxml"));
window.setTitle("Your profile");
window.setScene(new Scene(root, 400, 500));
window.show();
}
PopUPController
public void initialize() {
listView.setEditable(true);
listView.setVisible(true);
listView.setItems(walletModel.myWallets);
listView.setCellFactory(param -> {
try {
return new EditableCell();
} catch (IOException e) {
e.printStackTrace();
}
return null;
});
listView.layout();
addWalletButton.setOnMouseClicked(event -> {
walletModel.createWallet();
listView.getFixedCellSize();
size.setText("Total Wallets: " + walletModel.walletSize());
});
if (walletModel.myWallets.size() == 0) {
walletModel.initializeWalletData();
walletModel.myWallets.add(walletModel.initializeWalletData());
}
size.setText("Wallet Size " + walletModel.walletSize());
}
static class EditableCell extends ListCell<WalletModel.WalletData> {
private final WalletCellController controller;
EditableCell() throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/selectButton.fxml"));
Node graphic = loader.load();
controller = loader.getController();
setGraphic(graphic);
}
#Override
protected void updateItem(WalletModel.WalletData item, boolean empty) {
if (empty) {
controller.rootView.setVisible(false);
} else {
controller.textField.setText(item.getName());
controller.rootView.setVisible(true);
}
}
}
}
I want the button on listview to update the root screen when its clicked and plus close the popup as well. Each listview is getting graphics from walletcellcontroller code below.
Here is how I am calling from the root screen.
Creating instance in root screen and then calling
(Popup popup=new Popup();)
public void popupOpen() throws IOException {
popup.display();
}
here is the code for listview item
public class WalletCellController implements OnClick {
public Button select;
public TextField textField;
public AnchorPane rootView;
public void initialize(){
onMouseClicked();
}
public void onMouseClicked() {
select.setOnAction(closeEvent -> {
Node source = (Node) closeEvent.getSource();
Stage stage = (Stage) source.getScene().getWindow();
stage.close();
});
}}
Can you tell me how to use the callbacks for actionevents here. I think I need call back from POPUP Controller to POPup and then from POPup to root screen.
I am new in java so I not sure about the implementation of it.
interface Callable {
public void callBackMethod();
}
class Worker {
// Worker gets a handle to the boss object via the Callable interface.
// There's no way this worker class can call any other method other than
// the one in Callable.
public void doSomeWork(Callable myBoss) {
myBoss.callBackMethod();
// ERROR!
//myBoss.directMethod();
}
}
class Boss implements Callable {
public Boss() {
// Boss creates a worker object, and tells it to do some work.
Worker w1 = new Worker();
// Notice, we're passing a reference of the boss to the worker.
w1.doSomeWork(this);
}
//developer that develop library just call controll the place of calling
public void callBackMethod() {
System.out.println("What do you want?");
}
public void directMethod() {
System.out.println("I'm out for coffee.");
}
}
public class Main {
public static void main(String[] args) {
Boss b = new Boss();
b.directMethod();
// write your code here
}
}
this is sample code of call back method
In cases like these I recommend using Dialog, since it allows you to query & wait for user input.
Example
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Dialog?>
<?import javafx.scene.control.DialogPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.VBox?>
<Dialog xmlns:fx="http://javafx.com/fxml/1" fx:id="dialog" fx:controller="fxml.DialogController">
<dialogPane>
<DialogPane headerText="Choose item!">
<content>
<VBox prefWidth="100" spacing="5">
<children>
<Button text="a" onAction="#choice" maxWidth="Infinity" />
<Button text="b" onAction="#choice" maxWidth="Infinity" />
<Button text="c" onAction="#choice" maxWidth="Infinity" />
<Button text="Cancel" onAction="#cancel" maxWidth="Infinity" />
</children>
</VBox>
</content>
</DialogPane>
</dialogPane>
</Dialog>
public class DialogController {
#FXML
private Dialog<String> dialog;
#FXML
private void choice(ActionEvent event) {
Button source = (Button) event.getSource();
dialog.setResult(source.getText());
dialog.close();
}
#FXML
private void cancel() {
dialog.setResult("");
dialog.close();
}
}
#Override
public void start(Stage primaryStage) {
TextField textField = new TextField();
Button btn = new Button("Choose");
btn.setOnAction((ActionEvent event) -> {
Dialog<String> dialog;
try {
dialog = FXMLLoader.load(getClass().getResource("/fxml/Dialog.fxml"));
} catch (IOException ex) {
throw new IllegalStateException(ex);
}
Optional<String> result = dialog.showAndWait();
if (!result.orElse("").isEmpty()) {
textField.setText(s);
}
});
VBox root = new VBox(textField, btn);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
The core of the problem is that I cannot refresh or change the content of a node of a scene (here TablesMain) from another class (here NamePriceCell).
I am building and application with a main StackPane (TableMainController extends StackPane) that contains other nodes, some of which are ListViews. In a specific ListView (say 'readitemslistview') I have created a custom ListCell (public class NamePriceCell extends ListCell) and this NamePriceCell listcell contains a button (plusbtn). When a user clicks on the plusbtn, an arraylist (say 'chosenitemslist') gets filled up with the item displayed in the particular cell and at the same time another listview in the stackpane (say 'ordereditemslist') should be triggered to display the content of the 'chosenitemslist' arraylist.
I haven't found a way to reference the plusbtn of the NamePriceCell from the main controller. Moreover this post
Get ListCell via ListView
Unfortunately right now there is no API to get List Cell by index or
to get All children's(listcells) for ListView.
has turned me away from such an attempt (I haven't been able to understand if the solution provided there is suitable for my needs, as I don't want a refresh of the same listview but from another in the same scene).
So the only way I have found to get a mouse click action from the plusbtn is with an EventHandler in the same class (NamePriceCell). And even though I can get an arraylist filled according to the clicks on the NamePriceCell buttons (the NamePriceCell calls a static method in another class with a static arraylist in it) and I can get the result in my orderedlistview by letting the user click on another button node of the TableMain StackPane, I cannot find a way to do that when the user clicks on the NamePriceCell button.
Among the things I tried is setting a static method in the TablesMainController that I can access it from the NamePriceCell EventHandler (but then I have to make the holder node of the ordereditemslist static, and even so, it hasn't worked),
writing a separate fxml for the VBox containing the ordereditemslist with a corresponding separate controller so that I can have a method there to create the ordereditemslist and set the cellFactory (but even though the fxml file displays correctly and calling the method from the EventHandler of NamePriceCell works - a System.out gets displayed - it won't affect anything on the screen not the ordereditemslist, not even a label I tested.
I am relatively a java newbie and even so more when it comes to javafx. Your help is greatly needed and appreciated.
The code of my last approach is below:
VboxList.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.VBox?>
<VBox>
<Label text="Label" id="label1"/>
<Label text="Label" id="label2"/>
<Label text="sdfsdf" id="label3"/>
<ListView nodeOrientation="LEFT_TO_RIGHT" id="orderedlist" prefHeight="200.0" prefWidth="200.0" />
</VBox>
VboxListController.java
public class VboxListController extends VBox{
FXMLLoader fxmlLoader;
#FXML
private Label label1;
#FXML
private Label label2;
#FXML
private ListView<OrderItem> orderedlist;
public VboxListController() {
fxmlLoader = new FXMLLoader(getClass().getResource("fxml/VboxList.fxml"));
//fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
public void getorderedlist(ArrayList<OrderItem> chosenitems){
ObservableList<OrderItem> myObservableList = FXCollections.observableList(chosenitems);
this.getChildren().remove(orderedlist);
orderedlist = new ListView<>();
orderedlist.setItems(myObservableList);
orderedlist.setCellFactory(new Callback<ListView<OrderItem>, ListCell<OrderItem>>() {
#Override
public ListCell<OrderItem> call(ListView<OrderItem> p) {
ListCell<OrderItem> cell = new ListCell<OrderItem>() {
#Override
protected void updateItem(OrderItem dataobj, boolean bln) {
super.updateItem(dataobj, bln);
if (dataobj != null) {
setText(dataobj.read_item_name);
}
}
};
return cell;
}
});
this.getChildren().add(orderedlist);
orderedlist.setItems(null);
orderedlist.setItems(myObservableList);
label1 = null;
this.getChildren().remove(label1);
label1 = new Label();
label1.setText("oooops!");
System.out.println("sdf");
this.getChildren().add(label1);
}
}
NamePriceCell.java
public class NamePriceCell extends ListCell<ReadItemChoiceObj> {
int count=0;
#FXML
Label namelbl;
#FXML
Label pricelbl;
#FXML
Button plusbtn;
#FXML
Region spacer;
#FXML
HBox cellHbox;
FXMLLoader mLLoader;
ReadItemChoiceObj readitem;
ArrayList<ReadItemChoiceObj> chosenreaditems;
#Override
protected void updateItem(ReadItemChoiceObj readitem, boolean empty) {
super.updateItem(readitem, empty);
chosenreaditems = new ArrayList<>();
if(empty || readitem == null) {
setText(null);
setGraphic(null);
} else {
this.readitem = readitem;
if (mLLoader == null) {
mLLoader = new FXMLLoader(getClass().getResource("fxml/NamePriceCell.fxml"));
mLLoader.setController(this);
try {
mLLoader.load();
} catch (IOException e) {
e.printStackTrace();
}
}
namelbl.setText(readitem.getname());
namelbl.setMaxWidth(500);
pricelbl.setText(String.format("%.2f", readitem.getprice()));
pricelbl.setStyle("-fx-font: 8pt \"Arial\";");
pricelbl.setMaxWidth(40);
spacer.setMaxWidth(10);
spacer.setMinWidth(10);
plusbtn.setMaxWidth(20);
cellHbox.setHgrow(namelbl, Priority.ALWAYS);
cellHbox.setAlignment(Pos.BASELINE_LEFT);
setText(null);
setGraphic(cellHbox);
plusbtn.setOnMouseClicked(whattodohandler);
}
}
EventHandler<MouseEvent> whattodohandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
ChosenItemsStat.getplusbtnclicked(readitem);
ChosenItemsStat chos = new ChosenItemsStat();
count+=1;
plusbtn.setText(String.valueOf(count));
new VboxListController().getorderedlist(chos.sendchosenlist());
}
};
}
and a part of TablesMain.java
#FXML
Label label1;
#FXML
BorderPane borderpane;
#FXML
Pane tablepane;
#FXML
ListView<DataObj> tabcatlist;
#FXML
VBox VboxList;
#FXML
VboxListController vboxListController;
/* #FXML
ListView<OrderItem> orderedlist;*/
#FXML
VBox leftVbox;
public TablesMain(Order order) {
this.order = order;
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("fxml/tablesmain.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
ObservableList<DataObj> myObservableList = FXCollections.observableList(gettable_categories());
tabcatlist.setItems(myObservableList);
tabcatlist.setCellFactory(new Callback<ListView<DataObj>, ListCell<DataObj>>() {
#Override
public ListCell<DataObj> call(ListView<DataObj> p) {
ListCell<DataObj> cell = new ListCell<DataObj>() {
#Override
protected void updateItem(DataObj dataobj, boolean bln) {
super.updateItem(dataobj, bln);
if (dataobj != null) {
setText(dataobj.getname());
}
}
};
return cell;
}
});
if (table_categoryID == 0) table_categoryID = tablecategories.get(0).getid();
gettables(table_categoryID);
}
private void make_readitemlist(int itemcategoryID) {
readitems = new ArrayList<>();
.... the readitems arraylist gets filled up...
ObservableList<ReadItemChoiceObj> myObservableList = FXCollections.observableList(readitems);
readitemlist.setItems(myObservableList);
readitemlist.setCellFactory(new Callback<ListView<ReadItemChoiceObj>,
ListCell<ReadItemChoiceObj>>() {
#Override
public ListCell<ReadItemChoiceObj> call(ListView<ReadItemChoiceObj> list) {
readitemlistcell = new NamePriceCell();
return readitemlistcell;
}
}
);
readitemlist.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
ReadItemChoiceObj dataobj = (ReadItemChoiceObj) readitemlist.getSelectionModel().getSelectedItem();
System.out.println(String.valueOf(dataobj.getid()));
// showorderedlist(new ChosenItemsStat().sendchosenlist());
}
});
System.out.println(readitemlist.cellFactoryProperty().getClass().toString());
}
...
}
Since it's been almost 5 months without another contribution, i would like to post my solution as an answer. It's not mine but inspired (or copied) by the post mentioned in my question (or another one, now I don't remember, please excuse my possible misplacement of credits).
More precisely I created a static StringProperty in TableMain file (the main Controller class). Every time the plusbtn (NamePriceCell class that extends ListCell) is clicked a random number turned to string is forced in the static StringProperty of the TableMain controller class. Inside this class the StringProperty has a ChangeListener added which in its turn triggers (from inside of the main controller now - that was the clue) the refreshing of the orderedlist (the listview that had to be refreshed with the added items).
NamePriceCell.java
public class NamePriceCell extends ListCell<ReadItemChoiceObj> {
#FXML
Label namelbl;
#FXML
Label pricelbl;
#FXML
Button plusbtn;
#FXML
Region spacer;
#FXML
HBox cellHbox;
FXMLLoader mLLoader;
ReadItemChoiceObj readitem;
#Override
protected void updateItem(ReadItemChoiceObj readitem, boolean empty) {
super.updateItem(readitem, empty);
if(empty || readitem == null) {
setText(null);
setGraphic(null);
} else {
this.readitem = readitem;
if (mLLoader == null) {
mLLoader = new FXMLLoader(getClass().getResource("fxml/NamePriceCell.fxml"));
mLLoader.setController(this);
try {
mLLoader.load();
} catch (IOException e) {
e.printStackTrace();
}
}
namelbl.setText(readitem.getname());
namelbl.setMaxWidth(500);
pricelbl.setText(String.format("%.2f", readitem.getprice()));
pricelbl.setStyle("-fx-font: 8pt \"Arial\";");
pricelbl.setMaxWidth(40);
spacer.setMaxWidth(10);
spacer.setMinWidth(10);
plusbtn.setMaxWidth(20);
cellHbox.setHgrow(namelbl, Priority.ALWAYS);
cellHbox.setAlignment(Pos.BASELINE_LEFT);
setText(null);
setGraphic(cellHbox);
plusbtn.setOnMouseClicked(whattodohandler);
}
}
EventHandler<MouseEvent> whattodohandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
OrderItem orderitem = new OrderItem();
orderitem.read_item_name = readitem.getname();
orderitem.read_item_price=readitem.getprice();
orderitem.read_itemID=readitem.getid();
orderitem.choice_name_list = new ArrayList<String>();
orderitem.choice_price_list = new ArrayList<Float>();
orderitem.choice_id_list = new ArrayList<Integer>();
ChosenItemsStat.getplusbtnclicked(orderitem);
Random rand = new Random();
TablesMain.stringProperty.setValue(String.valueOf(rand));
}
};
}
ChosenItemsStat.java (a static class receiving the additions to the list)
public class ChosenItemsStat {
static ArrayList<OrderItem> chosenorderitemsstat = new ArrayList<>();
static void getplusbtnclicked(OrderItem orderitem){
chosenorderitemsstat.add(orderitem);
}
}
I didn't use the VboxList.fxml or the VboxListController.java as I included this in the TablesMain.java (see below)
TablesMain.java (as in the question but with this addition)
stringProperty = new SimpleStringProperty();
stringProperty.setValue("");
tablelbl.setText(stringProperty.getValue());
stringProperty.addListener(new ChangeListener(){
#Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
showselectedlist();
}
});
while the showselectedlist() that is being called by the change of the StringProperty value is the one below (in this method the cell is being contructed as in the removed class (VboxListController)
private void showselectedlist(){
orderitems.addAll(ChosenItemsStat.chosenorderitemsstat);
ChosenItemsStat.chosenorderitemsstat.clear();
ListView<OrderItem> selectedlist = new ListView<>();
ObservableList<OrderItem> myObservableList = FXCollections.observableList(orderitems);
selectedlist.setItems(myObservableList);
selectedlist.setMaxWidth(220);
selectedlist.setCellFactory(new Callback<ListView<OrderItem>, ListCell<OrderItem>>() {
#Override
public ListCell<OrderItem> call(ListView<OrderItem> p) {
ListCell<OrderItem> cell = new ListCell<OrderItem>(){
OrderedCell ordcell= new OrderedCell();
#Override
protected void updateItem(OrderItem orderitem, boolean bln) {
super.updateItem(orderitem, bln);
if (orderitem != null) {
Float price = ordcell.getitemTempPrice(orderitem,orderitem.read_item_price);
HBox namepriceHbox = new HBox();
Label lblprice= new Label(String.format("%.2f",price));
Region betweenregion = new Region();
Label lblname = new Label();
lblname.setText(orderitem.read_item_name );
lblname.setStyle("-fx-font: 10pt \"Arial\";-fx-font-weight:bold");
namepriceHbox.setHgrow(betweenregion, Priority.ALWAYS);
namepriceHbox.getChildren().addAll(lblname,betweenregion,lblprice);
VBox allVbox = new VBox();
Text lblchoices = new Text();
String choices = ordcell.choicestostring(orderitem);
lblchoices.setText(choices);
lblchoices.setWrappingWidth(listpane.getLayoutBounds().getWidth());
if (choices.equals("")) allVbox.getChildren().addAll(namepriceHbox);
else allVbox.getChildren().addAll(namepriceHbox, lblchoices);
double namepricewidth = listpane.getLayoutBounds().getWidth();
System.out.println("namepricewidth is "+String.valueOf(namepricewidth));
//allVbox.setPadding(new Insets(10,0,10,0));
setGraphic(allVbox);
}
}
};
return cell;
}
});
listpane.getChildren().add(selectedlist);
}