Situation: I'm writing a simple note-taking program from scratch for a school project using java and JavaFX. I'm using tags to group notes. I can add tags to a note and if it's a new tag that is created it is also added to my tag-cloud.
Each tag has a button marked with an X. I now need to make the button work but depending on the placement of the tag I need it to do one of two things:
1) if the user wishes to remove a tag from a note I need to remove the tag from the tagbar (which is a TilePane) where the tags of that specific note are shown and remove it from the note.
2) if the user wishes to delete the tag altogether the user clicks X of the tag in the tag-cloud (which is a FlowPane) and the tag is then removed from the tag-cloud and from all notes.
Problem: As far as I understand it I need to make two different actions for the same button and I have no idea how to make that work.
Ideas: I have thought of making two different kinds of tags each with its own FXML-file but I'm not sure.
Question: How do I make two different actions for the same button and how do I make it so that the right action is called?
Here's a link to what the program looks like so far:
Here is an app you can play with. I went here for the start. This app creates two sets of tags and deletes all tags with the same id if one is deleted.
Main:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
*
* #author blj0011
*/
public class JavaFXApplication102 extends Application
{
#Override
public void start(Stage stage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
Controller:
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.ResourceBundle;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
/**
*
* #author blj0011
*/
public class FXMLDocumentController implements Initializable
{
#FXML
private VBox vbMain;
#FXML
private FlowPane fpMain;
String[] tagType = {"Chicken", "Soup", "Fall", "Winter", "Happy"};
Random random = new Random();
#FXML
private void handleButtonAction(ActionEvent event)
{
int control = random.nextInt(5);
HBox tag1 = new HBox();
tag1.setId(tagType[control]);
tag1.setStyle("-fx-padding:4;" +
" -fx-border-width: 2;" +
" -fx-border-color: black;" +
" -fx-border-radius: 4;" +
" -fx-background-color: f1f1f1;" +
" -fx-border-insets: 5;");
tag1.setSpacing(5);
tag1.setPrefHeight(20);
tag1.setMaxWidth(75);
tag1.getChildren().add(new Label(tagType[control]));
Label closingX1 = new Label("x");
tag1.getChildren().add(closingX1);
closingX1.setOnMouseClicked((MouseEvent event1) -> {
System.out.println("Parent: " + ((Label) event1.getSource()).getParent().getParent());
for(Node child : fpMain.getChildren())
{
if(child.getId().equals(tag1.getId()))
{
Platform.runLater(()->fpMain.getChildren().remove(child));
}
}
for(Node child : vbMain.getChildren())
{
if(child.getId().equals(tag1.getId()))
{
Platform.runLater(()->vbMain.getChildren().remove(child));
}
}
});
vbMain.getChildren().add(tag1);
HBox tag2 = new HBox();
tag2.setId(tagType[control]);
tag2.setStyle("-fx-padding:4;" +
" -fx-border-width: 2;" +
" -fx-border-color: black;" +
" -fx-border-radius: 4;" +
" -fx-background-color: f1f1f1;" +
" -fx-border-insets: 5;");
tag2.setSpacing(5);
tag2.setPrefHeight(20);
tag2.setMaxWidth(75);
tag2.getChildren().add(new Label(tagType[control]));
Label closingX2 = new Label("x");
tag2.getChildren().add(closingX2);
closingX2.setOnMouseClicked((MouseEvent event1) -> {
System.out.println("Parent: " + ((Label) event1.getSource()).getParent().getParent());
for(Node child : vbMain.getChildren())
{
if(child.getId().equals(tag2.getId()))
{
Platform.runLater(()->vbMain.getChildren().remove(child));
}
}
for(Node child : fpMain.getChildren())
{
if(child.getId().equals(tag2.getId()))
{
Platform.runLater(()->fpMain.getChildren().remove(child));
}
}
});
fpMain.getChildren().add(tag2);
}
#Override
public void initialize(URL url, ResourceBundle rb)
{
}
}
FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.FlowPane?>
<?import javafx.scene.layout.VBox?>
<AnchorPane id="AnchorPane" prefHeight="623.0" prefWidth="820.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication102.FXMLDocumentController">
<children>
<Button fx:id="button" layoutX="372.0" layoutY="569.0" onAction="#handleButtonAction" text="add button" />
<Label fx:id="label" layoutX="126" layoutY="120" minHeight="16" minWidth="69" />
<VBox fx:id="vbMain" prefHeight="549.0" prefWidth="387.0" />
<FlowPane fx:id="fpMain" layoutX="371.0" layoutY="18.0" prefHeight="505.0" prefWidth="434.0" />
</children>
</AnchorPane>
In the pictures below, I added three tags randomly and then deleted the chicken tag.
Related
I'm working on a project and I need to display the information of a Medicine object from a ListView to another Scene.
The user would select a Medicine from the ListView and press the Button to see it's details in the next Scene that would be displayed by Labels. The problem now is, I have transferred the info, and the text property of the Label has changed (observed through println and debugging), but the Label just won't display the changed text.
this is the main
package app;
import app.data.MedicineData;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;
import org.w3c.dom.events.Event;
import java.io.IOException;
public class Pharmachine extends Application {
public static final double WIDTH = 480;
public static final double HEIGHT = 720;
#Override
public void start(Stage stage) throws Exception{
Parent homePage = FXMLLoader.load(getClass().getResource("homepage.fxml"));
Scene homePageScene = newDefaultScene(homePage);
stage.setResizable(false);
stage.setScene(homePageScene);
stage.setTitle("Pharmachine");
stage.show();
}
#Override
public void init(){
MedicineData.getInstance().loadData();
}
public static Scene newDefaultScene(Parent parent){
Scene scene = new Scene(parent, WIDTH, HEIGHT);
scene.addEventHandler(MouseEvent.MOUSE_CLICKED,
(mouseEvent) -> {
Node targetNode = scene.getFocusOwner();
if(targetNode != null && targetNode.isFocused()) {
if(targetNode.getParent() != null)
targetNode.getParent().requestFocus();
mouseEvent.consume();
}
}
);
return scene;
}
public static void navigateTo(ActionEvent actionEvent, String filename){
Stage mainWindow = (Stage) ((Node) actionEvent.getSource()).getScene().getWindow();
Scene currentScene = ((Node) actionEvent.getSource()).getScene();
try {
currentScene.setRoot(FXMLLoader.load(Pharmachine.class.getResource(filename)));
mainWindow.setScene(currentScene);
} catch(IOException e){
e.printStackTrace();
}
}
public static void main(String[] args){ launch(args); }
}
this is the fxml for the list's page
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ListView?>
<BorderPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
fx:controller="app.ListpageController" fx:id="pageRoot"
stylesheets="#styles/main.css">
<left>
<GridPane fx:id="listArea" alignment="CENTER">
<ListView fx:id="medicineList"
GridPane.rowIndex="1"/>
</GridPane>
</left>
<center>
<GridPane fx:id="buttonsArea" alignment="CENTER"
hgap="10" vgap="10">
<Button text="Back"
onAction="#displayHomePage"
GridPane.halignment="CENTER"/>
<Button text="Details"
onAction="#displayDetailsPage"
GridPane.rowIndex="1"
GridPane.halignment="CENTER"/>
</GridPane>
</center>
</BorderPane>
here is the controller for the list's page
package app;
import app.data.Medicine;
import app.data.MedicineData;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.util.Callback;
import java.io.IOException;
public class ListpageController {
#FXML
private BorderPane pageRoot;
#FXML
private GridPane listArea;
#FXML
private GridPane buttonsArea;
#FXML
private ListView<Medicine> medicineList;
#FXML public void initialize(){
listArea.setMaxWidth(Pharmachine.WIDTH * 3/5);
medicineList.setMinWidth(listArea.getMaxWidth());
medicineList.setMinHeight(Pharmachine.HEIGHT);
medicineList.setItems(MedicineData.getInstance().getMedicines());
medicineList.setCellFactory(new Callback<>() {
#Override
public ListCell<Medicine> call(ListView<Medicine> medicineListView) {
ListCell<Medicine> listCell = new ListCell<>(){
#Override
public void updateItem(Medicine medicine, boolean empty){
super.updateItem(medicine, empty);
if(!empty){
HBox medName = new HBox(new Label(medicine.getName()));
HBox.setHgrow(medName, Priority.ALWAYS);
HBox medPrice = new HBox(new Label(String.format("RM%.2f", medicine.getPrice())));
HBox medLabel = new HBox(medName, medPrice);
setGraphic(medLabel);
}
}
};
listCell.setOnMouseClicked(
(mouseEvent) -> {
if(mouseEvent.getClickCount() == 2){
medicineList.getSelectionModel().clearSelection();
}
}
);
return listCell;
}
});
}
#FXML private void displayHomePage(ActionEvent actionEvent){
Pharmachine.navigateTo(actionEvent, "homepage.fxml");
}
#FXML private void displayDetailsPage(ActionEvent actionEvent){
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("detailspage.fxml"));
try {
loader.load();
} catch(IOException e){
e.printStackTrace();
}
DetailspageController controller = loader.getController();
Medicine selectedMedicine = medicineList.getSelectionModel().getSelectedItem();
if(selectedMedicine != null) {
controller.setInfo(selectedMedicine);
Pharmachine.navigateTo(actionEvent, "detailspage.fxml");
}
}
}
this is the fxml for the next scene (just simple for now)
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.text.Text?>
<BorderPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
fx:id="pageRoot" fx:controller="app.DetailspageController"
stylesheets="#styles/main.css">
<center>
<GridPane fx:id="detailsArea"
alignment="CENTER" hgap="10" vgap="10">
<Label text="Details"/>
<Text text="Name: "
GridPane.rowIndex="1"/>
<Label fx:id="medNameLabel" text="~"
GridPane.rowIndex="1" GridPane.columnIndex="1"/>
<Text text="Price: "
GridPane.rowIndex="2"/>
<Label fx:id="medPriceLabel" text="~"
GridPane.rowIndex="2" GridPane.columnIndex="1"/>
</GridPane>
</center>
</BorderPane>
and this is the scene's controller
package app;
import app.data.Medicine;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import java.net.URL;
import java.util.ResourceBundle;
public class DetailspageController implements Initializable {
private Medicine selectedMedicine;
#FXML
private BorderPane pageRoot;
#FXML
private GridPane detailsArea;
#FXML
private Label medNameLabel;
#FXML
private Label medPriceLabel;
#Override
public void initialize(URL url, ResourceBundle rb){
}
public void setInfo(Medicine medicine){
selectedMedicine = medicine;
medNameLabel.setText(selectedMedicine.getName());
medPriceLabel.setText(String.format("RM%.2f", selectedMedicine.getPrice()));
System.out.println(medNameLabel.textProperty());
System.out.println(medNameLabel.getText());
medNameLabel.setStyle("-fx-background-color: red;");
}
}
The help would mean a lot. Thank you :]
In your displayDetailsPage method, you load a scene from detailspage.fxml, and you update its labels by calling the setInfo method of the controller.
Then, you call Pharmachine.navigateTo, which loads detailspage.xml again and replaces the scene root with the newly loaded root. You updated the text of the labels in the first details page, but the second details page is brand new, so it doesn’t have those changes.
The second argument in the navigateTo method should be of type Parent rather than a String. navigateTo should not attempt to load any .fxml file; let that be the caller’s responsibility.
Side note: When a list has multiple attributes for each data item, you should probably use a TableView rather than a ListView.
I am writing an in-app web browser for a JavaFX application which as all browsers, has tabs. The structure of the whole app is based on a TabPane in which all of the functionality of the app is shown. When the user selects to use the browser, a new Tab (let's call it browserMainTab) is created, and it contains another TabPane for the browser tabs. I would like to show a confirmation dialog when the user selects to close the browserMainTab but the inner TabPane still has opened tabs. So far, evertyhing is ok, but when I consume the onCloseRequest event of browserMainTab the tab stays still active with all its' children but it can't be clicked, dragged, or closed.
private void mainBrowserTabCloseConfirmationEvent(Tab tab){
tab.setOnCloseRequest(event -> {
AnchorPane pane = (AnchorPane) tab.getContent();
TabPane browserSubTabPane = (TabPane) pane.getChildren().get(0);
if(browserSubTabPane.getTabs().size() >= 1){
StillOpenBrowserTabsAlert alert = new StillOpenBrowserTabsAlert();
alert.setNumOfOpenTabs(browserSubTabPane.getTabs().size());
Alert alert1 = alert.alertWithReturnType();
if(alert1.getResult() == ButtonType.OK){
getMainTabPane().getTabs().remove(getMainTabPane()
.getSelectionModel()
.getSelectedItem());
}else{
event.consume();
}
}
});
}
The above snippet throws no exceptions, and the first part works fine. The issue comes up after I consume() the event, when the tab becomes unclickable. Can somebody help please?
**EDIT**
Reproducible example as requested
ExampleMain.java:
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class ExampleMain extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("mainTabPane.fxml"));
Parent root = loader.load();
primaryStage.setScene(new Scene(root, 600, 400));
primaryStage.show();
}
}
MainTabPane.java
package sample;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
public class MainTabPane implements Initializable {
#FXML private TabPane mainTabPane;
private SubTabPane subTabPaneController;
private SubTabPane getSubTabPaneController() {
return subTabPaneController;
}
private void setSubTabPaneController(SubTabPane subTabPaneController) {
this.subTabPaneController = subTabPaneController;
}
private TabPane getMainTabPane() { return mainTabPane; }
private void loadSubTabPaneAsTabContent(){
FXMLLoader loader = new FXMLLoader(getClass().getResource("subTabPane.fxml"));
Parent root = null;
try{
root = loader.load();
}catch (IOException exception){
exception.printStackTrace();
}
assert root != null;
setSubTabPaneController(loader.getController());
Tab tab = new Tab("Tab of main TabPane");
tab.setContent(root);
getMainTabPane().getTabs().add(tab);
closeRequestOfMainTabPane(tab);
}
private void closeRequestOfMainTabPane(Tab tab){
tab.setOnCloseRequest(e -> {
int numOfOpenTabsInSubTabPane = getSubTabPaneController().getSubTabPane().getTabs().size();
if(numOfOpenTabsInSubTabPane >= 1){
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.showAndWait();
if(alert.getResult() == ButtonType.OK){
getMainTabPane().getTabs().remove(getMainTabPane()
.getSelectionModel()
.getSelectedItem());
}else{
e.consume();
}
}
});
}
#Override
public void initialize(URL location, ResourceBundle resources) {
Platform.runLater(this::loadSubTabPaneAsTabContent);
getMainTabPane().setTabDragPolicy(TabPane.TabDragPolicy.REORDER);
}
}
SubTabPane.java
package sample;
import javafx.fxml.FXML;
import javafx.scene.control.TabPane;
public class SubTabPane {
#FXML private TabPane subTabPane;
TabPane getSubTabPane() { return subTabPane; }
}
mainTabPane.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.MainTabPane">
<children>
<TabPane fx:id="mainTabPane" prefHeight="400.0" prefWidth="600.0" tabClosingPolicy="UNAVAILABLE" />
</children>
</AnchorPane>
subTabPane.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.SubTabPane">
<children>
<TabPane fx:id="subTabPane" layoutX="7.0" prefHeight="400.0" prefWidth="593.0">
<tabs>
<Tab text="Untitled Tab 1">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" />
</content>
</Tab>
</tabs>
</TabPane>
</children>
</AnchorPane>
I tried something similar in an application with tabs coresponding to opened projects. The problem apears when the TabDragPolicy is set to REORDER, I smash a tab closing button, a confirmirmation dialog apears asking me if I'd like to save the project, and the event gets consumed if I cancel tab closing. If you remove any of these three (the drag policy, the dialog or event consuming), the tab does not freeze. Removing tab and adding it back to the same position helps, but looks ugly.
The following approach works perfectly for some reason:
tab.setOnCloseRequest(e -> {
tab.getTabPane().setTabDragPolicy(TabPane.TabDragPolicy.FIXED);
// your confirmation code
Platform.runLater(() -> tab.getTabPane().setTabDragPolicy(TabPane.TabDragPolicy.REORDER));
});
Why do you have code to manually remove the tab?
This works for me:
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.Event;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonType;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
CheckBox askFirst = new CheckBox("confirm before allowing close");
Tab tab = new Tab("Close Me", askFirst);
tab.setOnCloseRequest((Event t) -> {
if (askFirst.isSelected()) {
Alert areYouSureAlert = new Alert(Alert.AlertType.CONFIRMATION, "Are you sure?", ButtonType.YES, ButtonType.NO);
Optional<ButtonType> result = areYouSureAlert.showAndWait();
if (result.isEmpty() || result.get() != ButtonType.YES) {
t.consume();
}
}
});
TabPane tp = new TabPane(tab);
stage.setScene(new Scene(tp));
stage.show();
Platform.setImplicitExit(true);
}
}
Your problem is in this code:
if(alert.getResult() == ButtonType.OK){
getMainTabPane().getTabs().remove(getMainTabPane()
.getSelectionModel()
.getSelectedItem());
}else{
e.consume();
}
all you really need is:
if (alert.getResult() != ButtonType.OK) {
e.consume();
}
To remove the tab, let the event be pressed, to not close the tab, consume the event. You are likely confusing the TabPane which thinks it is supposed to be removing a tab that you have already tried to remove.
Well, I found the bug in the end. This was happening because I had a TabDragPolicy enabled during initialization. I never thought that this could cause such problem so this is why I didn't mention it. It seems that when I consume() the event, the TabDragPolicy is not applied anymore and this makes the Tab to completely freeze. Once I removed the TabDragPolicy.REORDER from initialize the issue stopped.
Listview cells loading blank instead of custom cell
I've tried rebuilding fxml file, using debug tools, none of these have helped.I've looked extensively on the net but can only find guides for older versions of javafx listview.
Before some tells me that the updateItem code is wrong, please just help me get it working first, then at least it'll be somewhere to start from for optimizing it
package Test;
import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.layout.Pane;
import org.controlsfx.glyphfont.Glyph;
/**
* FXML Controller class
*
* #author james
*/
public class ListCellController2 extends ListCell<Student>
{
FXMLLoader loader;
#FXML
private Glyph attendenceSymbolGlyph;
#FXML
private Label studentDetailsLabel;
#FXML
private Pane entryPane;
#Override
protected void updateItem(Student student, boolean empty)
{
super.updateItem(student, empty);
if (empty || student == null)
{
setText(null);
setGraphic(null);
}
else
{
loader = new FXMLLoader(getClass().getResource("ListCell2.fxml"));
try
{
loader.load();
studentDetailsLabel.setText(student.toString());
setGraphic(entryPane);
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.Pane?>
<?import org.controlsfx.glyphfont.Glyph?>
<Pane fx:id="entryPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="95.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Test.ListCellController2">
<children>
<Glyph id="attendenceSymbolGlyph" fx:id="attendenceSymbolGlyph" layoutX="5.0" layoutY="8.0" prefHeight="80.0" prefWidth="87.0" />
<Label id="studentDetailsLabel" fx:id="studentDetailsLabel" layoutX="113.0" layoutY="5.0" prefHeight="88.0" prefWidth="482.0" text="Label" />
</children>
</Pane>
package Test;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.util.Callback;
/**
* FXML Controller class
*
* #author james
*/
public class MainController implements Initializable {
#FXML
public ListView<Student> myListView;
public ObservableList<Student> studentList;
public MainController()
{
studentList = FXCollections.observableArrayList();
studentList.add(new Student("Jimmy", "u0764987", "ef937b3"));
studentList.add(new Student("John", "u0762809", "543jh32"));
}
public void setupListView()
{
try
{
myListView.setItems((ObservableList)studentList);
myListView.setCellFactory(new Callback<ListView<Student>, ListCell<Student>>()
{
#Override
public ListCell<Student> call(ListView<Student> p)
{
return new ListCellController();
}
});
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL location, ResourceBundle resources)
{
if (!studentList.isEmpty())
{
setupListView();
}
else
{
System.out.println("student list is empty");
}
}
}
My expected result would be that it loads my data into the custom cell then displays it, what it actually does is is give me a blank cell
It seems you created two instances of your ListCellController2 class.
you create the first instance in your list view cell factory
the second instance is created when you load the FXML as the controller class is given inside the FXML
I also suggest you only load the list cell component once when your cell is created, instead of every time a new value is shown.
The controller is created by the cell factory and the instance is used by the FXML-loader. This way, the fields annotated with #FXML get initialized correctly.
Here is my code:
MainController.java
import java.net.URL;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ListView;
public class MainController implements Initializable {
#FXML
public ListView<Student> myListView;
public ObservableList<Student> studentList;
public MainController() {
studentList = FXCollections.observableArrayList();
studentList.add(new Student("Jimmy", "u0764987", "ef937b3"));
studentList.add(new Student("John", "u0762809", "543jh32"));
}
#Override
public void initialize(URL location, ResourceBundle resources) {
if (!studentList.isEmpty()) {
setupListView();
} else {
System.out.println("student list is empty");
}
}
private void setupListView() {
myListView.setItems(studentList);
myListView.setCellFactory((listView) -> new ListCellController2());
}
}
ListCellController2
import java.io.IOException;
import org.controlsfx.glyphfont.Glyph;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.layout.Pane;
public class ListCellController2 extends ListCell<Student> {
#FXML
private Glyph attendenceSymbolGlyph;
#FXML
private Label studentDetailsLabel;
#FXML
private Pane entryPane;
public ListCellController2() {
try {
FXMLLoader loader = new FXMLLoader();
loader.setController(this);
loader.setLocation(getClass().getResource("ListCell2.fxml"));
loader.load();
} catch (IOException e) {
throw new RuntimeException("Creating UI component failed", e);
}
}
#Override
protected void updateItem(Student student, boolean empty) {
super.updateItem(student, empty);
if (empty || student == null) {
setText(null);
setGraphic(null);
} else {
studentDetailsLabel.setText(student.toString());
setGraphic(entryPane);
}
}
}
ListCell2.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.Pane?>
<?import org.controlsfx.glyphfont.Glyph?>
<Pane fx:id="entryPane" prefHeight="95.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Glyph id="attendenceSymbolGlyph" fx:id="attendenceSymbolGlyph" layoutX="5.0" layoutY="8.0" prefHeight="80.0" prefWidth="87.0" />
<Label id="studentDetailsLabel" fx:id="studentDetailsLabel" layoutX="113.0" layoutY="5.0" prefHeight="88.0" prefWidth="482.0" text="Label" />
</children>
</Pane>
i created a simple GUI, using JavaFX with fxml. One part of the program is using fxml and other generate dynamic GUI. So, i created javafx.scene.controller.Accordion with fxml and add panes inside it with code:
#FXML
Accordion accordionPlant;
public void init(){
accordionPlant.getPanes().add(createTitledPanePlant(1));
accordionPlant.getPanes().add(createTitledPanePlant(2));
}
protected TitledPane createTitledPanePlant(int index){
TilePane tile = new TilePane(Orientation.HORIZONTAL, 5, 5);
Label typeLabel = new Label("Тип выпуска");
TextField typeText = new TextField();
VBox typeContainer = new VBox(typeLabel,typeText);
Label bankLabel = new Label("Берег");
Tooltip.install(bankLabel, new Tooltip("Берег, с которого производится выпуск"));
TextField bankText = new TextField();
VBox bankContainer = new VBox(bankLabel,bankText);
tile.getChildren().addAll(typeContainer, bankContainer);
TitledPane titledPane = new TitledPane("Параметры выпуска " + index, tile);
return titledPane;
}
After that user click on the button and the program should find all TextField and calculate their value. So how it must be done correctly or comfortable?
I tried to get all panes of accordion:
void test(){
for (TitledPane pane: accordionPlant.getPanes()) {
pane.getChildrenUnmodifiable().get(0); //blah, blah
}
}
but i think it isn't a good idea, especially using index. I need only textfields. Is there any way of implements of ?
If you have dynamic elements, why not just store them in a List when you create them? That way you don't have to try to get them from a container. You are probably only interested in the Controller, but I posted the whole app. If you look in createTitledPanePlant you can see where I add the TextFields to the List -> List<TextField> textFieldContainer = new ArrayList();
Main
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
*
* #author blj0011
*/
public class JavaFXApplication115 extends Application
{
#Override
public void start(Stage stage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
Controller
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Orientation;
import javafx.scene.control.Accordion;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TitledPane;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.TilePane;
import javafx.scene.layout.VBox;
/**
*
* #author blj0011
*/
public class FXMLDocumentController implements Initializable
{
#FXML
private Label label;
#FXML
private Accordion accordionMain;
List<TextField> textFieldContainer = new ArrayList();
#FXML
private void handleButtonAction(ActionEvent event)
{
//Sum TextField
double total = 0;
for (TextField node : textFieldContainer) {
double value;
try {
value = Double.parseDouble(node.getText());
}
catch (NumberFormatException e) {
value = 0;
}
total += value;
}
label.setText("Sum: " + total);
}
#Override
public void initialize(URL url, ResourceBundle rb)
{
// TODO
accordionMain.getPanes().add(createTitledPanePlant(1));
accordionMain.getPanes().add(createTitledPanePlant(2));
}
protected TitledPane createTitledPanePlant(int index)
{
TilePane tile = new TilePane(Orientation.HORIZONTAL, 5, 5);
Label typeLabel = new Label("Тип выпуска");
TextField typeText = new TextField();
textFieldContainer.add(typeText);//Add your textField to the container when you create it
VBox typeContainer = new VBox(typeLabel, typeText);
Label bankLabel = new Label("Берег");
Tooltip.install(bankLabel, new Tooltip("Берег, с которого производится выпуск"));
TextField bankText = new TextField();
textFieldContainer.add(bankText);//Add your textField to the container when you create it
VBox bankContainer = new VBox(bankLabel, bankText);
tile.getChildren().addAll(typeContainer, bankContainer);
TitledPane titledPane = new TitledPane("Параметры выпуска " + index, tile);
return titledPane;
}
}
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Accordion?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="AnchorPane" prefHeight="486.0" prefWidth="431.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.141" fx:controller="javafxapplication115.FXMLDocumentController">
<children>
<Button fx:id="button" layoutX="179.0" layoutY="437.0" onAction="#handleButtonAction" text="Sum nodes" />
<Label fx:id="label" layoutX="163.0" layoutY="37.0" minHeight="16" minWidth="69" prefHeight="17.0" prefWidth="106.0" />
<Accordion fx:id="accordionMain" layoutX="115.0" layoutY="72.0" prefHeight="310.0" prefWidth="202.0" />
</children>
</AnchorPane>
I have one main screen with a label firstLabel. There is a button openSecondWindow.
Pressing that button opens up a new window, while leaving the first one on the screen.
How would I lock the first screen so that the user cannot interact with it until the second window is closed?
The second window will have a TextField and Button setFirstLabel. When the user enters text in the text field and hits the button, the text from the first window is changed.
How am I supposed to access the controller class for the first window from the second window's controller?
How do I ensure that only one instance of each window be opened at a time? (No duplicates)
i made a small example for you.
If you click on Stage 1 on the Button to open the second Stage, the Button will be disabled, so you cant open one more Stage.
If you type some text into the TextField on Stage2 and click on the Update Button there, the TextField on Stage 1 will be updated and the second Stage will be closed.
Here is the code:
firstWindow.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="de.professional_webworkx.jfx.twowindows.controller.FirstController">
<children>
<TextField fx:id="textField" layoutX="50.0" layoutY="189.0" prefWidth="500.0" promptText="Waiting for input from second Stage..." />
<Button fx:id="openBtn" layoutX="441.0" layoutY="304.0" mnemonicParsing="false" text="open 2 Stage" />
</children>
</AnchorPane>
Second fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<AnchorPane id="AnchorPane" fx:id="mainPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="de.professional_webworkx.jfx.twowindows.controller.SecondController">
<children>
<TextField fx:id="textField" layoutX="50.0" layoutY="66.0" prefWidth="243.0" promptText="Type some text" />
<Button fx:id="updateBtn" layoutX="50.0" layoutY="118.0" mnemonicParsing="false" text="update Text on Stage 1" />
</children>
</AnchorPane>
FirstController
package de.professional_webworkx.jfx.twowindows.controller;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
/**
*
* #author Patrick Ott <Patrick.Ott#professional-webworkx.de>
* #version 1.0
*/
public class FirstController implements Initializable {
#FXML
Button openBtn;
#FXML
TextField textField;
private boolean secondOpen;
private Stage secondStage;
FirstController firstController;
public FirstController() {
}
#Override
public void initialize(URL url, ResourceBundle rb) {
this.firstController = this;
System.out.println(firstController);
openBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
if(!secondOpen) {
openBtn.setDisable(true);
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("second.fxml"));
Parent load = loader.load();
SecondController controller = (SecondController) loader.getController();
controller.setFirstController(firstController);
Scene scene = new Scene(load);
secondStage = new Stage();
secondStage.setScene(scene);
secondStage.show();
secondOpen = true;
} catch (IOException ex) {
Logger.getLogger(FirstController.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
});
}
public void updateText(final String text) {
this.textField.setText(text);
}
public void secondClosed() {
this.secondOpen = false;
this.openBtn.setDisable(false);
secondStage.close();
}
}
SecondController
package de.professional_webworkx.jfx.twowindows.controller;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
/**
*
* #author Patrick Ott <Patrick.Ott#professional-webworkx.de>
* #version 1.0
*/
public class SecondController implements Initializable {
#FXML
AnchorPane mainPane;
#FXML
TextField textField;
#FXML
Button updateBtn;
private FirstController firstController;
#Override
public void initialize(URL url, ResourceBundle rb) {
updateBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent t) {
String text = textField.getText();
firstController.updateText(text);
firstController.secondClosed();
}
});
}
public void setFirstController(final FirstController firstController) {
this.firstController = firstController;
}
}
And start it
package de.professional_webworkx.jfx.twowindows;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
*
* #author Patrick Ott <Patrick.Ott#professional-webworkx.de>
*/
public class TwoStages extends Application {
#Override
public void start(Stage primaryStage) {
try {
Parent load = FXMLLoader.load(getClass().getResource("firstWindow.fxml"));
Scene scene = new Scene(load);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException ex) {
Logger.getLogger(TwoStages.class.getName()).log(Level.SEVERE, null, ex);
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
The Magic is, to hand over a FirstController object to the SecondController, that you are able to update the TextField on the first Stage.
Hope this will help.
Patrick