How to add node to another node's child in FXML? - javafx

Example of what I'd like to get, written in Java code:
public class Main extends Application {
private static Scene scene;
public static void main(String[] args) {
launch(args);
}
#Override
public void init() throws IOException {
// Load root pane from FXML file.
URL url = getClass().getResource("sample.fxml");
StackPane root = FXMLLoader.load(url);
// Create scene for a root node on JavaFX thread.
Platform.runLater(() -> scene = new Scene(root, 600, 400));
}
#Override
public void start(Stage stage) {
stage.setScene(scene);
stage.show();
}
}
Custom node:
public class CustomGroup extends Group {
private VBox contentPane = new VBox();
public CustomGroup() {
getChildren().add(contentPane);
contentPane.getChildren().add(new Label("First Label"));
contentPane.getChildren().add(new Label("Second Label"));
}
}
FXML:
<StackPane>
<CustomGroup/>
</StackPane>
The code above is example of what I'd like to get, but instead of adding labels in Java code, I want to add them in FXML. Something like that:
<StackPane>
<CustomGroup>
<Label text="First Label"/>
<Label text="Second Label"/>
</CustomGroup>
</StackPane>
but this adds labels to the custom group. I want to add them to the content pane (VBox) of the custom group.

Though, I am not sure why you are adding the Labels to your VBox inside the constructor of CustomGroup, I will ignore it and answer your question.
You can add a separate method to add the items to your VBox. Let us consider the methods:
setItems() which accepts Nodes adds them to the VBox
getItems() which returns the ObservableList<Node> from the VBox
CustomGroup
public class CustomGroup extends Group {
private VBox contentPane = new VBox();
public CustomGroup() {
getChildren().add(contentPane);
contentPane.getChildren().add(new Label("First Label"));
contentPane.getChildren().add(new Label("Second Label"));
}
public void setItems(Node...nodes) {
contentPane.getChildren().addAll(nodes);
}
public ObservableList<Node> getItems() {
return contentPane.getChildren();
}
}
FXML
<CustomGroup>
<items>
<Button text="hi"/>
</items>
</CustomGroup>
This FXML adds the new Button inside the VBox.

Related

How to add a javafx shortcut key combinations for buttons

My UI has a adding button and I want to assign a keyboard shortcut combination for that. I have failed to use the setAcceleartor for this purpose.
What is the easiest way to set up keyboard shortcuts in javafx applications?
button declaration in the UI:
<Button fx:id="addButton" alignment="CENTER" minWidth="-Infinity" mnemonicParsing="false" onAction="#addAction" prefHeight="31.0" prefWidth="130.0" text="Add" HBox.hgrow="ALWAYS" />
Controller button binding:
#FXML
private Button addButton;
The method that wants to setOnAction for the shortcut for the button:
public void addAction(ActionEvent event) throws SQLException, ClassNotFoundException {
if (validateInput()) {
String productName = productField.getText();
double unitPrice = Double.parseDouble(priceField.getText());
int quantity = Integer.parseInt(quantityField.getText());
double total = unitPrice * quantity;
ITEMLIST.add(new Item(productName, unitPrice, quantity, total));
calculation();
resetAdd();
productTableView.getSelectionModel().clearSelection();
ObservableList<Product> productsData = ProductDAO.searchProducts();
populateProducts(productsData);
searchField.setText("");
}
}
initialize() method:
#FXML
private void initialize() throws SQLException, ClassNotFoundException, IOException {
setSaveAccelerator(addButton);
}
The code I tried with setAccelerator:
private void setSaveAccelerator(final Button button) {
Scene scene = button.getScene();
if (scene == null) {
throw new IllegalArgumentException("setSaveAccelerator must be called when a button is attached to a scene");
}
scene.getAccelerators().put(
new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN),
new Runnable() {
#FXML public void run() {
button.fire();
}
}
);
}
In your setSaveAccelerator method, instead of directly calling addAction(ActionEvent event), just instruct the button to fire its event to its listeners such as: button.fire(). For example:
private void setSaveAccelerator(Button button) {
if(button==null) {
System.out.println("Button is null! "); // check that the button was injected properly through your fxml
}
Scene scene = button.getScene();
if (scene == null) {
throw new IllegalArgumentException("setSaveAccelerator must be called when a button is attached to a scene");
}
scene.getAccelerators().put(
new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN),
new Runnable() {
#FXML public void run() {
button.fire();
}
}
);
}
EDIT
To also avoid the IllegalArgumentException you must attach the accelerator after the button is attached to a scene. I managed to achieve this by creating a public method in the controller to attach the accelerator after the scene has been set. Then, in the class where the scene is loaded the controller's method can be called which sets up this functionality. See the example below:
In the controller class (in my case MainController):
public void setup() {
setSaveAccelerator(button);
}
In your main class when loading the fxml file:
FXMLLoader loader = new FXMLLoader(MainController.class.getResource("mainFXML.fxml"));
AnchorPane page = (AnchorPane) loader.load();
MainController controller = loader.getController();
Scene scene = new Scene(page);
controller.setup(); // calls the setup method attaching the accelerators
FULL EXAMPLE
Main class:
public class Main extends Application{
public static Stage primaryStage;
#Override
public void start(Stage primaryStage) throws Exception {
Main.primaryStage=primaryStage;
FXMLLoader loader = new FXMLLoader(MainController.class.getResource("mainFXML.fxml"));
AnchorPane page = (AnchorPane) loader.load();
MainController controller = loader.getController();
Scene scene = new Scene(page);
primaryStage.setTitle("Shortcut example");
primaryStage.setScene(scene);
controller.setup();
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Maincontroller:
public class MainController {
#FXML
private ResourceBundle resources;
#FXML
private URL location;
#FXML
private Button button;
#FXML
private AnchorPane rootPane;
#FXML
private TextArea textarea;
#FXML
void action(ActionEvent event) {
textarea.setText("Action fired!!");
}
#FXML
void initialize() {
assert button != null : "fx:id=\"button\" was not injected: check your FXML file 'MainFXML.fxml'.";
assert rootPane != null : "fx:id=\"rootPane\" was not injected: check your FXML file 'MainFXML.fxml'.";
assert textarea != null : "fx:id=\"textarea\" was not injected: check your FXML file 'MainFXML.fxml'.";
}
public void setup() {
setSaveAccelerator(button);
}
private void setSaveAccelerator(Button button) {
if(button==null) {
System.out.println("Button null!!");
}
Scene scene = button.getScene();
if (scene == null) {
throw new IllegalArgumentException("setSaveAccelerator must be called when a button is attached to a scene");
}
scene.getAccelerators().put(
new KeyCodeCombination(KeyCode.S, KeyCombination.SHORTCUT_DOWN),
new Runnable() {
#FXML public void run() {
button.fire();
}
}
);
}
}
MainFXML.fxml
<AnchorPane fx:id="rootPane" prefHeight="408.0" prefWidth="330.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController">
<children>
<Button fx:id="button" layoutX="139.0" layoutY="350.0" mnemonicParsing="false" onAction="#action" text="Button" />
<TextArea fx:id="textarea" layoutX="73.0" layoutY="38.0" prefHeight="200.0" prefWidth="200.0" />
</children>
</AnchorPane>

JavaFX change ComboBox items (outside of initialize() method)

I am playing around with SceneBuilder and come across a few questions about the intialize() method and how to change ComboBox items after it's already been initialized in said method. So basically, after I set the items in initialize, I am not able to change them anymore from another method in the controller.
Here is my code:
public class AppController implements Initializable {
private ObservableList<String> list = FXCollections.observableArrayList();
private MainModel model;
#FXML
private ComboBox<String> cobUsers = new ComboBox<String>();
#Override
public void initialize(URL url, ResourceBundle rb) {
list.add("name1");
list.add("name2");
cobUsers.setItems(list); // this works!
}
public void initModel(MainModel model) {
this.model = model;
}
public void addItems(){
list.add("name3");
list.add("name4");
cobUsers.setItems(list); // this does not work. ComboBox items remain "name1" and "name2"
}
}
public class App extends Application {
private Stage primaryStage;
private AnchorPane rootLayout;
private AppController appController = new AppController();
MainModel model = new MainModel();
#Override
public void start(Stage primaryStage) {
appController.initModel(model);
this.primaryStage = primaryStage;
this.primaryStage.setTitle("App");
initRootLayout();
appController.addItems();
}
/**
* Initializes the root layout.
*/
public void initRootLayout() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("FXMLDocument.fxml"));
rootLayout = (AnchorPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
So guess my question is, how can I access/change my ComboBox later on, after it's been initialized in intialize()?
Thanks! :)
UPDATE 1:
I have changed the initRootLayout() in the App class (see below) and it WORKS now. list now contains 4 items and all of them show up in the ComboBox after calling addItems(). Thanks everyone!
public void initRootLayout() {
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader(); loader.setLocation(getClass().getResource("FXMLDocument.fxml"));
rootLayout = (AnchorPane) loader.load();
AppController controller = loader.<AppController>getController();
controller.addItems();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}

javafx putting textarea value into a hashmap

I would like to make a small java fx app that has just textarea and one button on a stage and that when you type some strings in textarea and press submit it shows on the stage small table with results how many each Word had occurrences.
so my questions is: does map is the best solution for finding the occurrences even though I do not know what will be the key for finding occurrences and how to connect string from text area, to map.
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Word counting");
TextArea txt=new TextArea();
txt.setMaxSize(450, 200);
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
primaryStage.hide();
ShowResults.drugiProzor();
}
});
BorderPane root = new BorderPane();
root.setTop(txt);
HBox hbox=new HBox();
hbox.setPadding(new Insets(20,20,100,180));
hbox.getChildren().add(btn);
root.setBottom(hbox);
Scene scene = new Scene(root, 450, 300);
primaryStage.setTitle("Word counting!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
and the second class is again gui class with table view
public class ShowResults {
static Stage secondaryStage;
public static void drugiProzor() {
secondaryStage=new Stage();
TableView table=new TableView();
TableColumn column1=new TableColumn("Word");
column1.setMinWidth(200);
TableColumn column2=new TableColumn("Number of occurencies");
column2.setMinWidth(200);
table.getColumns().addAll(column1,column2);
StackPane pane=new StackPane();
pane.getChildren().add(table);
Scene scene = new Scene(pane, 450, 300);
secondaryStage.setScene(scene);
secondaryStage.setTitle("Counting words");
secondaryStage.show();
}
}
and third class shoyld be the class where the magic happends something like this:
public class Logic {
public void logic()
}
}
You can just do something like
public Map<String, Long> countWordOccurences(String text) {
return Pattern.compile("\\s+") // regular expression matching 1 or more whitespace
.splitAsStream(text) // split at regular expression and stream words between
// group by the words themselves and count each group:
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
}
Check the Javadocs to see what each step is doing: Pattern, Collectors.groupingBy(), Function, etc.
If you want to count in a case-insensitive way, you can replace Function.identity() with String::toLowerCase
.collect(Collectors.groupingBy(String::toLowerCase, Collectors.counting()));
and if you want to ignore punctuation, you can add
map(s -> s.replaceAll("[^a-zA-Z]",""))
to the pipeline.

How to get Stackpane from Main class in Controller class?

To familiarize myself with the scene builder I added a linechart and two numberaxis as nodes in a stackpane with the scene builder.
The parent node will be loaded in the mainApp.java:
public class CsvCommander 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();
}
public static void main(String[] args) {
launch(args);
}
}
Now, for further operations I want to get the stackpane of the parent in FXMLDocument.fxml, but I don't know how to...
e.g.
StackPane container = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml") or the like.
How can I get my root node or stackpane in the Controller pass?
Put an fx:id on the root element in the FXML file, and then inject it into the controller in the same way as other elements:
FXML file:
<!-- imports etc -->
<StackPane xmlns="..." fx:controller="com.example.MyControllerClass" fx:id="container">
<!-- nodes etc -->
</StackPane>
Controller:
public class MyControllerClass {
#FXML // will be initialized by FXMLLoader
private StackPane container ;
public void initialize() {
// do something with container....
}
}

On Mouse Entered Method to swap Button position

I would like to develop a mouse entered method that swaps the locations of two buttons in real time using FXML and JavaFX which I am unfortunately very new to. Relocate(x,y), get/setLayoutX/Y and below get/setTranslateX/Y all throw IllegalArgumentEceptions with not much more understandable information in the stack trace. What is the preferred Button Property to use in order to get and then set a real-time location swap?
#FXML protected void neinHover (ActionEvent evt){
double jTmpX, jTmpY, nTmpX, nTmpY;
nTmpX = neinButton.getTranslateX();
nTmpY = neinButton.getTranslateY();
jTmpX = jaButton.getTranslateX();
jTmpY = jaButton.getTranslateY();
jaButton.setTranslateX(nTmpX);
jaButton.setTranslateY(nTmpY);
neinButton.setTranslateX(jTmpX);
neinButton.setTranslateY(jTmpY);
}
I supose you want something like this:
FXML:
<fx:root onMouseClicked="#swap" type="Pane" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8">
<children>
<Button fx:id="b1" mnemonicParsing="false" text="1" />
<Button fx:id="b2" layoutX="90.0" mnemonicParsing="false" text="2" />
</children>
</fx:root>
Controller:
public class MockPane extends Pane {
#FXML
private Button b1;
#FXML
private Button b2;
public MockPane() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(
"MockPane.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
#FXML
private void swap() {
double b1x = b1.getLayoutX();
double b1y = b1.getLayoutY();
double b2x = b2.getLayoutX();
double b2y = b2.getLayoutY();
b1.setLayoutX(b2x);
b1.setLayoutY(b2y);
b2.setLayoutX(b1x);
b2.setLayoutY(b1y);
}
}
App:
public class MockApp extends Application {
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) throws Exception {
MockPane root = new MockPane();
Scene scene = new Scene(root, 200, 100);
stage.setScene(scene);
stage.show();
}
}

Resources