After updating to Java 8, i got problems displaying content of list view
public class TestController extends AnchorPane implements Initializable {
#FXML
private ListView<String> listView;
#Override
public void initialize(URL url, ResourceBundle rb) {
}
public void setApp() {
setContent();
}
/**
* Set content in list
*/
private void setContent() {
ObservableList<String> items = FXCollections.observableArrayList();
items.add("1");
items.add("2");
listView.setItems(items);
}
}
With Java 7 this gives me a list with values. After update to java 8, it gives me a empty frame.
Thanks James_D, here is the code you asked for
public void goToTest() {
try {
TestController test = (TestController) replaceSceneContent("/fxml/test.fxml");
test.setApp();
} catch (Exception ex) {
System.out.println("Error loading: " + ex + " - " + ex.getMessage());
}
}
private Initializable replaceSceneContent(String fxml) throws Exception {
FXMLLoader loader = new FXMLLoader();
InputStream in = MainApp.class.getResourceAsStream(fxml);
loader.setBuilderFactory(new JavaFXBuilderFactory());
loader.setLocation(MainApp.class.getResource(fxml));
AnchorPane page;
try {
page = (AnchorPane) loader.load(in);
} finally {
in.close();
}
Scene scene = new Scene(page);
stage.setScene(scene);
stage.sizeToScene();
stage.setResizable(resizable);
return (Initializable) loader.getController();
}
listView = new ListView<>(items);
will not work; it will create a new ListView and populate it, whereas what you want to do is populate the ListView you created in your FXML.
listView.setItems(items);
is the correct code.
(I assume you really have public class TestController implements Initializable, not public class test implements Initializable.)
So the following should work:
private void setContent() {
ObservableList<String> items = FXCollections.observableArrayList();
items.add("1");
items.add("2");
listView.setItems(items);
}
Your FXMLLoader code is a little unusual in that you set a location (URL) but then use the load(InputStream) method to load. I'd be surprised if this fixed it, but the usual way would be:
private Initializable replaceSceneContent(String fxml) throws Exception {
FXMLLoader loader = new FXMLLoader();
// not needed:
// InputStream in = MainApp.class.getResourceAsStream(fxml);
// Not really needed, this is the default:
loader.setBuilderFactory(new JavaFXBuilderFactory());
loader.setLocation(MainApp.class.getResource(fxml));
AnchorPane page;
// instead of this:
// try {
// page = (AnchorPane) loader.load(in);
// } finally {
// in.close();
// }
// just this (since you have set the location):
page = loader.load();
Scene scene = new Scene(page);
stage.setScene(scene);
stage.sizeToScene();
stage.setResizable(resizable);
return (Initializable) loader.getController();
}
(Random guess: did you accidentally make listView static in the Controller?)
Related
all the questions I found around here answered how to send data from one controller to another. However, I don't know how to return another value from the second controller to the first one. Basically, send data back and forth while the first controller is still opened.
Main class:
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("Screen1.fxml"));
Scene scene = new Scene(loader.load());
primaryStage.setScene(scene);
primaryStage.setTitle("Screen 1");
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Screen 1 controller:
public class Screen1Controller {
#FXML
TextField textFieldScreen1;
#FXML
Button buttonScreen1;
#FXML
Label labelScreen1;
#FXML
private void initialize() {
buttonScreen1.setOnAction((event) -> {
try{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("Screen2.fxml"));
Scene scene = new Scene(loader.load());
Stage stage = new Stage();
//Send data to Screen 2
Screen2Controller controller2 = loader.getController();
controller2.receiveTextFromScreen1(textFieldScreen1.getText());
stage.setTitle("Screen 2");
stage.setScene(scene);
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
Screen 2 controller:
public class Screen2Controller {
#FXML
TextField textFieldScreen2;
#FXML
Button buttonScreen2;
#FXML
Label labelScreen2;
#FXML
private void initialize() {
buttonScreen2.setOnAction((event) -> {
Stage currentStage = (Stage) buttonScreen2.getScene().getWindow();
currentStage.close();
});
}
public void receiveTextFromScreen1(String stringScreen1){
labelScreen2.setText(stringScreen1);
}
}
I have tried to use the same strategy (create a method in Screen1Controller and instantiate Screen1Controller in the second controller) with the following code but it didn't work.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("Screen2.fxml"));
//Send data to Screen 1
Screen1Controller controller1 = loader.getController();
controller1.receiveTextFromScreen2(textFieldScreen2.getText());
Also, related to this matter (I'm assuming) a second question: how would I update the second view while I change the TextField in controller 1 without creating a new window everytime I click the button (i.e using the same second view instance)?
Any help will be greatly appreciated!
Create a constructor
public Screen1Controller {
Arrow the parameters
}
and when you call the controller, you parse the parameter:
void on2players(ActionEvent event) throws IOException {
client.out.println("ACTION NEWGAME 2 4");
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/boardscreen.fxml"));
loader.setController(new Screen1Controller(parameter);
Parent root = loader.load();
Scene scene= new Scene(root);
Stage window = (Stage) ((Node) event.getSource()).getScene().getWindow();
window.setScene(scene);
window.show();
}
public class Screen1Controller implements Initializable {
public Screen1Controller() {
//parameters
}
#Override
public void initialize(URL location, ResourceBundle resources) {
switch(numberOfPlayers){
case 2:
//TODO...creating board for 2 players
case 3:
//TODO creatinf board for 3 players
}
}
If you do this, you force the fxml loader to call the controller. NOTE: You should get the controller inside .FXML
Departments controllers are loaded from SceneWithButtonsController. StageAddController is loaded from LayoutWithHomeAndAddController. I want to execute this method from StageAddController which executes corresponding method from departments controller :
//Show Stage to add a worker
public class LayoutWithHomeAndAddController {
#FXML
private void addButtonClicked() throws IOException{
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("/views/StageAdd.fxml"));
BorderPane addWorkerPane = loader.load();
StageAddController stageAddController = loader.getController();
Stage stageAdd = new Stage();
stageAdd.setTitle("Add New Worker");
stageAdd.resizableProperty().setValue(Boolean.FALSE);
Scene scene = new Scene(addWorkerPane);
scene.getStylesheets().add("/css/redBorder.css");
stageAdd.setScene(scene);
stageAdd.initModality(Modality.WINDOW_MODAL);
Stage mainStage = (Stage) root.getScene().getWindow();
stageAdd.initOwner(mainStage);
stageAdd.show();}
}
Controller class where departments are loaded:
public class SceneWithButtonsController {
//Show workers from Mechanical Department
#FXML
private void mechanicalDepartmentButtonClicked() throws IOException {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("/views/MechanicalDepartment.fxml"));
BorderPane mehOdjel = loader.load();
MechanicalDepartmentController mechanicalDepartmentController = loader.getController();
mechanicalDepartmentController.populateTable();
BorderPane borderPane = (BorderPane) pane.getParent();
borderPane.setCenter(mehOdjel);
}
//Show workers from Electrical Department
#FXML
private void electricalDepartmentButtonClicked() throws IOException {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("/views/ElectricalDepartment.fxml"));
BorderPane elOdjel = loader.load();
ElectricalDepartmentController electricalDepartmentController = loader.getController();
electricalDepartmentController.populateTable();
BorderPane borderPane = (BorderPane) pane.getParent();
borderPane.setCenter(elOdjel);
}
}
StageAddController:
public class StageAddController {
private ElectricalDepartmentController electricalDepartmentController;
private MechanicalDepartmentController mechanicalDepartmentController;
public void setElectricalDepartmentController(ElectricalDepartmentController electricalDepartmentController) {
this.electricalDepartmentController = electricalDepartmentController;
}
public void setMechanicalDepartmentController(MechanicalDepartmentController mechanicalDepartmentController) {
this.mechanicalDepartmentController = mechanicalDepartmentController;
}
public void refreshTableOnSaveButtonClicked() {
if (departmentBox.getValue().equals("Electrical")) {
electricalDepartmentController.refreshButtonClicked();
}
else if(departmentBox.getValue().equals("Mechanical")) {
mechanicalDepartmentController.refreshButtonClicked();
}
}
}
This means that refreshButtonClicked() method from ElectricalDepartmentController or MechanicalDepartmentController will be executed.
ElectricalDepartmentController (same is with MechanicalDepartmentController):
public class ElectricalDepartmentController {
public void initialize() throws IOException {
StageAddController stageAddController = new StageAddController();
stageAddController.setElectricalDepartmentController(this);
workersTableColumn.setCellValueFactory(new PropertyValueFactory<>("nameSurname"));
rowSelected();
}
#FXML
public void populateTable() {
for(Worker worker : workerDao.getWorkersNameSurname("Electrical")) workersList.addAll(worker);
workersTable.setItems(workersList);
}
#FXML
public void refreshButtonClicked() {
workersList.removeAll(workersList);
populateTable();
}
}
How to achieve this.
You need to link controllers. In order to do that, when loading the respective FXML for the subordinate form, you have to set the parent controller for it manually so that you can have access to it.
Unfortunately, FXML does not provide a native way to link controllers. What you want to do is you want to use the full FXMLLoader functionality (instead of using FXMLLoader.load()), since the FXMLLoader also has a getController() method. It's probably a wise idea for your controllers to implement something like this:
public interface HierarchicalController {
HierarchicalController getParentController();
void setParentController(HierarchicalController controller);
}
EDIT: Component preload code:
public class SceneWithButtonsController {
protected FXMLLoader<?> mechanicalLoader;
protected FXMLLoader<?> electricalLoader;
protected BorderPane mechanicalPane;
protected BorderPane electricalPane;
public void initialize() {
mechanicalLoader = new FXMLLoader(Main.class.getResource("/views/MechanicalDepartment.fxml"));
mechanicalPane = loader.load();
MechanicalDepartmentController mechanicalDepartmentController = loader.getController();
mechanicalDepartmentController.populateTable();
electricalLoader = new FXMLLoader(Main.class.getResource("/views/MechanicalDepartment.fxml"));
electricalPane = loader.load();
ElectricalDepartmentController electricalDepartmentController = loader.getController();
electricalDepartmentController.populateTable();
// now what is the relation between StageAddController and SceneWithButtonsController ?
}
//Show workers from Mechanical Department
#FXML
private void mechanicalDepartmentButtonClicked() throws IOException {
BorderPane borderPane = (BorderPane) pane.getParent();
borderPane.setCenter(mechanicalPane);
}
//Show workers from Electrical Department
#FXML
private void electricalDepartmentButtonClicked() throws IOException {
BorderPane borderPane = (BorderPane) pane.getParent();
borderPane.setCenter(electricalPane);
}
}
I try to implement an TreeView in my JavaFX App. But unfortenatly no items are showed, but i cannot find an issue. I search for some example and did it like them.
I put an TreeView Control to my FXML File in SceneBuilder and selected the ControllerClass which was generated and slected the Treeview field from this class as an id for the TreeView Control in SceneBuilder.
That's my Controller code:
public class MainSceneController implements Initializable {
#FXML
TreeView<String> treeview;
#FXML
Button btn;
#Override
public void initialize(URL url, ResourceBundle rb) {
TreeItem<String> root = new TreeItem<>("root");
for(int i = 0; i < 10; i++) {
TreeItem<String> child = new TreeItem<>("Children " + i);
root.getChildren().add(child);
}
root.setExpanded(true);
this.treeview = new TreeView<>(root);
treeview.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
}
#FXML
public void addTreeViewItem() {
}
#FXML
private void showAddStreamDialog() {
try {
Parent p;
p = FXMLLoader.load(getClass().getResource("AddStream.fxml"));
Scene s = new Scene(p);
Stage stage = new Stage();
stage.initModality(Modality.APPLICATION_MODAL);
stage.setScene(s);
stage.show();
} catch (IOException ex) {
Logger.getLogger(MainSceneController.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
Any idea's whats wrong?
You shouldn't assign new instance to the this.treeview because this field was already initialized by the FXLoader.
So instead of this.treeview = new TreeView<>(root); you need simply to set the root item this.treeview.setRoot(root);
How can i add an item to an already existing ListView from a different Stage (Window).
Basically i just want to add Text to the ListView from Window 2 to the ListView in Window 1.
Thanks in advance.
Sorry i forgot this is what i've got so far in my controller class: (im a beginner in javafx ..)
(below i try to add a String to the ListView but this doesnt work...and i dont know why )
public class ClientGUIController implements Initializable {
#FXML private Text usernameText;
#FXML private Button cancelButtonNewDate;
#FXML private TextField newDateTitel,newDateJahr;
#FXML private TextArea newDateNotiz;
#FXML private ComboBox<String> newDateTag,newDateMonat,newDateStunde,newDateMinute;
#FXML private ListView<String> terminListView;
private ObservableList<String> termine =
FXCollections.observableArrayList();
private ObservableList<String> listItems = FXCollections.observableArrayList("Add Items here");
#Override
public void initialize(URL location, ResourceBundle resources) {
}
public Text getUsernameText() {
return usernameText;
}
public void setUsernameText(String username ) {
this.usernameText.setText(username);
terminListView.setItems(listItems);
listItems.add("test");
}
public void newDate() {
Stage newDate = new Stage();
Parent root;
try {
root = FXMLLoader.load(getClass().getResource("newDate.fxml"));
// FXMLLoader loader = new FXMLLoader();
// root = (Parent) loader.load(getClass().getResource("NewDate.fxml").openStream());
} catch (IOException e) {
e.printStackTrace();
return;
}
Scene sceneNewDate = new Scene(root);
// sceneNewDate.getStylesheets().add(getClass().getResource("Style.css").toExternalForm());
newDate.setTitle("Neuer Termin");
newDate.setScene(sceneNewDate);
newDate.show();
}
public void createNewDate() throws IOException {
// Termine meinTermin = new Termine(Integer.parseInt(newDateTag.getValue()), Integer.parseInt(newDateMonat.getValue()), Integer.parseInt(newDateJahr.getText()), newDateTitel.getText(), newDateNotiz.getText(),
// Integer.parseInt(newDateStunde.getValue()), Integer.parseInt(newDateMinute.getValue()));
//Add item to ListView
listItems.add("test"); <- this doesnt work
}
public void closeDialogue(){
Stage stage = (Stage) cancelButtonNewDate.getScene().getWindow();
stage.close();
}
}
One way to do this is to pass listItems to the controller for newDate.fxml, so it can just add to that list. So, assuming the controller class for newDate.fxml is NewDateController, you would do something like:
public class NewDateController {
private ObservableList<String> data ;
public void setData(ObservableList<String> data) {
this.data = data ;
}
// other code as before...
// button handler:
#FXML
private void handleButtonPress() {
data.addItem("test");
}
}
Then in your ClientGUIController, load the fxml like this:
public void newDate() {
Stage newDate = new Stage();
Parent root;
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("newDate.fxml"));
root = loader.load();
NewDateController controller = loader.getController();
controller.setData(listItems);
} catch (IOException e) {
e.printStackTrace();
return;
}
Scene sceneNewDate = new Scene(root);
newDate.setTitle("Neuer Termin");
newDate.setScene(sceneNewDate);
newDate.show();
}
Working with SceneBuilder. I have 2 stages, each one with a controller: stage1Controller,stage2Controller.
Stage1Controller :
public class Stage1Controller {
#FXML
private MenuItem translate;
#FXML
private Menu file;
#FXML
private Menu edit;
#FXML
private Menu help;
#FXML
private void handleTranslate (ActionEvent event){
translateFirstStage();
//HOW TO ACCESS THE stage2Controller setLabel()??
}
private void translateFirstStage(){
file.setText("Fichier");
edit.setText("Modifier");
help.setText("Aide");
}
}
Stage2Controller:
public class Stage2Controller {
#FXML
private Label lb;
private void setLabel(String string){
lb.setText("string");
}
}
Here is how both fxml files are loaded in Main.java class using 2 methods (called in Start(Stage primaryStage) method):
public void firstStage() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/stage1.fxml"));
rootLayout = (BorderPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
public void secondStage() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/stage2.fxml"));
XD = (AnchorPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(XD);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
The handleTranslate(ActionEvent event) method is used as an OnAction method for the MenuItem translate in the first Stage, it translates the view in both stages.
How Can i put setLabel in handleTranslate Method ? Thanks
The "quick and dirty" way is to give the Stage1Controller a reference to the Stage2Controller:
public class Stage1Controller {
private final Stage2Controller stage2Controller ;
public void setStage2Controller(Stage2Controller stage2Controller) {
this.stage2Controller = stage2Controller ;
}
// ...
#FXML
private void handleTranslate (ActionEvent event){
translateFirstStage();
stage2Controller.setLabel(...);
}
// other code as before ...
}
Now in your main app:
public class MainApp extends Application {
private Stage1Controller stage1Controller ;
private Stage2Controller stage2Controller ;
#Override
public void start(Stage primaryStage) {
firstStage();
secondStage();
stage1Controller.setStage2Controller(stage2Controller);
// ...
}
public void firstStage() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/stage1.fxml"));
rootLayout = (BorderPane) loader.load();
stage1Controller = loader.getController();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
public void secondStage() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/stage2.fxml"));
XD = (AnchorPane) loader.load();
stage2Controller = loader.getController();
// Show the scene containing the root layout.
Scene scene = new Scene(XD);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
// ...
}
A more robust approach to this, though, is to let both controllers access a shared model class, storing the data. If you represent the data using JavaFX observable properties, the controllers can listen for changes on the properties they care about. For example:
public class Model {
private final StringProperty text = new SimpleStringProperty("Initial text...");
public StringProperty textProperty() {
return text ;
}
public final void setText(String text) {
textProperty().set(text);
}
public final String getText() {
return textProperty().get();
}
// other properties as needed...
}
Now your controllers will look like this:
public class Stage1Controller {
private Model model ;
public void setModel(Model model) {
this.model = model ;
}
#FXML
private void handleTranslate (ActionEvent event){
translateFirstStage();
model.setText(...); // value will appear in stage2 controller's label!
}
// ...
}
and
public class Stage2Controller {
#FXML
private Label lb ;
private Model model ;
public void setModel(Model model) {
lb.textProperty().unbind();
this.model = model ;
lb.textProperty().bind(model.textProperty());
}
// ...
}
And in this case your main app looks like:
public class MainApp extends Application {
private final Model = new Model();
#Override
public void start(Stage primaryStage) {
// ...
}
public void firstStage() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/stage1.fxml"));
rootLayout = (BorderPane) loader.load();
Stage1Controller controller = loader.getController();
controller.setModel(model);
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
public void secondStage() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/stage2.fxml"));
XD = (AnchorPane) loader.load();
Stage2Controller controller = loader.getController();
controller.setModel(model);
// Show the scene containing the root layout.
Scene scene = new Scene(XD);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
}