JavaFX VBox getChildren().addAll() error - java

I have a small problem, When i want add textField,
button and label to VBOX i have a error :
addAll() in ObservableList cannot be applied to:
in this place: vBox.getChildren().addAll(textField, button, label);
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
BorderPane layout = new BorderPane();
Scene scene = new Scene(layout, 400, 200);
TextField textField = new TextField();
Label label = new Label("Average: 0.0");
Button button = new Button("Przycisk");
button.setOnAction(event -> {
String textFromTextField = textField.getText();
String[] splittedText = textFromTextField.split(",");
double average = 0.0;
for (String s: splittedText) {
average += Double.parseDouble(s);
}
average /= splittedText.length;
label.setText("Average: " + average);
});
VBox vBox = new VBox();
vBox.getChildren().addAll(textField, button, label);
vBox.setAlignment(Pos.CENTER);
vBox.setSpacing(10);
layout.setCenter(vBox);
primaryStage.setScene(scene);
primaryStage.setTitle("App");
primaryStage.show();
}
}

That's because you have imported AWT components java.awt.Label and java.awt.TextField instead of JavaFX components javafx.scene.control.Label and javafx.scene.control.TextField.

Take a good look at your imports!
In the image you can see that you used the TextField and Label from the 'awt' library instead of the 'javafx' library.
Make sure to change that and see if it worked out!
(Ps. Please post the full stacktrace in your question next time!)

Related

How to set an action to occur on pressing of any keyboard key in JavaFX?

So I want to do a title Menu for a video game project for college. I want to display a message press any key to continue... then when the user pressed any key, including the mouse, a method would run to set the stage to the next menu.
I posted all the relevant code bellow but short version is:
BackgroundImangeDisplayPane extends display pane and adds a background image.
TitleCard extends BackgroundImangeDisplayPane adds a VBox and 2 labels to the VBox
I'm using public void start(Stage primaryStage) throws Exception as the main, I set the setOnActionxxx methods here
I have tried using the set on action method on root and Vbox and non of them work... when I click nothing happens... But when I resize the window The root.setOnActionXXX "activates".
If I write the setOnAction methods on the TitleCard class It kind of works but then I cant switch the stage.
I will post the code bellow as well an explanation of the Scene structure its not to complicated:
// this will be the borderpane for every scene it recives a backgund
//images that will be present in every menu
public BackgroundImangeDisplayPane() {
try {
stream = new FileInputStream(imagePath.toString());
Image image = new Image(stream);
ImageView imageView = new ImageView();
imageView.setImage(image);
imageView.setFitWidth(1920);
imageView.setPreserveRatio(true);
this.getChildren().add(imageView);
BackgroundSize backgoundSize = new BackgroundSize(AUTO, AUTO, true, true, true, true);
BackgroundImage backgroundImage = new BackgroundImage(image, NO_REPEAT, NO_REPEAT, CENTER, backgoundSize);
Background background = new Background(backgroundImage);
this.setBackground(background);
} catch (Exception e) {
}
}
//This extends `BackgroundImangeDisplayPane` and places on top of it a A Vbox with two lables: the title and "press any key to continue..."
// it then adds styles to the labels
public class TitleCard extends BackgroundImangeDisplayPane {
Label title = new Label("Boats & Docks"); // lable 1
Label subtitle = new Label("Press any key to continue ..."); label2
public TitleCard(){
super();
VBox vbox = new VBox();
vbox.getChildren().add(title);
vbox.getChildren().add(subtitle);
this.setCenter(vbox);
this.setAlignment(vbox, Pos.CENTER);
vbox.setAlignment(Pos.CENTER);
title.setFont(new Font(170)); // set to Label
title.setTextFill(Color.SNOW);
title.setEffect(new DropShadow());
subtitle.setFont( new Font (30));
}
}
...
//Works as the "main" in javaFX
private Stage primaryStage;
#Override
public void start(Stage primaryStage) throws Exception {
TitleCard root = new TitleCard();
/*BasicMenu menu = new BasicMenu(5);
menu.ButtonSetOnAction(0, e -> changeScene() );
BackgroundImangeWithCustomMenu background = new
BackgroundImangeWithCustomMenu(menu,50,50);
root.setCenter(background);*/
Button b = new Button();
b.setOnAction(e -> changeSceneToLoginMenu());
System.out.println(root.getChildren().get(1).getClass());
root.getChildren().get(1).setFocusTraversable(true);
root.getChildren().get(1).setOnMouseClicked(e -> changeSceneToLoginMenu());
root.getChildren().get(1).setOnKeyPressed(e -> changeSceneToLoginMenu());
root.getChildren().get(0).setOnMouseClicked(e -> changeSceneToLoginMenu());
root.getChildren().get(0).setOnKeyPressed(e -> changeSceneToLoginMenu());
/*
root.setOnMouseClicked(e -> changeSceneToLoginMenu());
root.setOnKeyReleased(e -> changeSceneToLoginMenu());
root.setOnKeyPressed(e -> changeSceneToLoginMenu());
*/
Scene scene = new Scene(root, 1280, 720);
primaryStage.setScene(scene);
primaryStage.show();
this.primaryStage = primaryStage;
}
As proposed by James in comments, when a key is pressed on the scene, navigate to the next scene (or replace the root in the current scene, and remove the key press handler).
scene.setOnKeyPressed(e -> navigateToNextScene());
I managed to find a very simple working solution but I don't fully undestand why it does work. I Noticed that if I set the handler in the same class the node was instanciated the handler would work fine But if I tried to get the node with a method to the main fuction via root.getChildren().get(1) and then cast it to the VBox element the handler would not work.
As a solution I made the VBox a field and wrote a setter method for the VBox event Handler in the TitleCard Class. This fixed the problem.
I marked the code added as solution code with comments
public class TitleCard extends BackgroundImangeDisplayPane {
Label title = new Label("Boats & Docks"); // lable 1
Label subtitle = new Label("Press any key to continue ..."); label2
VBox vbox = new VBox; // solution code
public TitleCard(){
super();
VBox vbox = new VBox();
vbox.getChildren().add(title);
vbox.getChildren().add(subtitle);
this.setCenter(vbox);
this.setAlignment(vbox, Pos.CENTER);
vbox.setAlignment(Pos.CENTER);
title.setFont(new Font(170)); // set to Label
title.setTextFill(Color.SNOW);
title.setEffect(new DropShadow());
subtitle.setFont( new Font (30));
}
// Solution Code
public void setVBoxHandler(EventHandler<? super MouseEvent> value){
vbox.setOnMouseClicked(value);
}
}
Then I set the handler in the start method:
public void start(Stage primaryStage) throws Exception {
TitleCard root = new TitleCard();
VBox vBox =(VBox) root.getChildren().get(1);
root.setVBoxHandler(e->changeSceneToLoginMenu() ); // solution Code
Scene scene = new Scene(root, 1280, 720);
primaryStage.setScene(scene);
primaryStage.show();
this.primaryStage = primaryStage;
}
public void changeSceneToLoginMenu() {
System.out.println("It finally worked");
Scene currentScene = new Scene(new Group(),100,100); // just a demo
primaryStage.setScene(currentScene);
}
notes: The type of value on the method setVBoxHandler(EventHandler<? super MouseEvent> value) will depend on the setOnXXX method used. For example I tested and this soultion also works for buttons just need to change the type to EventHandler<ActionEvent> value.
Some coments on the question posted links on "how to use handlers", this posts used anomimous classes. I belived This way is outdated. I used lambdas in the code the end result is the same but more redable code
Just for reference if future readers are using anomimous classes the solution would be the same just change the way you set up the handler:
// lambdas
setVBoxHandler( e -> System.out.println("Code run if mouse is clicked "));
// anomimous classes
setVBoxHandler(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event){
System.out.println("Code run if mouse is clicked ");
}
});

JavaFX ScrollPane setVvalue() not working properly

I am testing the JavaFX ScrollPane class and realized that it is not working as I expect, I don't know why. I have the following code:
public class Client3 extends Application {
int indexMsg = 0;
Button send;
GridPane root;
ScrollPane msgPane;
GridPane msgPaneContent;
FlowPane writePane;
TextField writeMsg;
Scene scene;
#Override
public void start(Stage primaryStage) {
root = new GridPane();
root.setAlignment(Pos.CENTER);
root.setVgap(10);
root.setPadding(new Insets(10, 10, 10, 10));
msgPane = new ScrollPane();
msgPane.setPrefSize(280, 280);
msgPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
msgPaneContent = new GridPane();
msgPaneContent.setPrefWidth(270);
msgPaneContent.setVgap(10);
writePane = new FlowPane(10, 10);
writePane.setAlignment(Pos.CENTER);
writePane.setPrefWidth(280);
writeMsg = new TextField();
writeMsg.setPrefWidth(150);
writeMsg.setPromptText("Write your message");
writePane.getChildren().add(writeMsg);
GridPane.setConstraints(msgPane, 0, 0);
GridPane.setConstraints(writePane, 0, 1);
msgPane.setContent(msgPaneContent);
root.getChildren().addAll(msgPane, writePane);
writeMsg.setOnAction((ev) -> {
if (!writeMsg.getText().isEmpty()) {
TextArea msg = new TextArea(writeMsg.getText());
msg.setMaxWidth(135);
msg.setPrefRowCount(msg.getLength() / 21 + 1);
msg.setWrapText(true);
GridPane.setConstraints(msg, 0, indexMsg);
indexMsg++;
writeMsg.deleteText(0, writeMsg.getText().length());
msgPaneContent.getChildren().add(msg);
msgPane.setVvalue(1.0);
}
});
scene = new Scene(root, 300, 300);
primaryStage.setTitle("Chat App");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Basically, I have a GridPane as the root with a ScrollPane and a GridPane as its children. The ScrollPane has a children GridPane. There is a TextField with an EventHandler which generates a TextArea inside the GridPane (the ScrollPane's children). Each TextArea object is created in the vertical direction, downwards. I want to set the scrollbar always at its maximum value (setVvalue(1.0)) each time a new TextArea is added. The thing is that it doesn't seem to work as it should because the vertical value is never set to the maximum after handling the event, but it seems to be set to the maximum value that it had before handling it (the bottom of the previous TextArea added).
Any solution for this? Thanks in advance.

How to add a ScrollBar in JavaFx

I'm trying to add a ScrollBar to a HBox. The ScrollBar gets added, but I get no scrolling. How can I make it work?
public class ScrollableItems {
public void scrollableItems(HBox content) {
double height = 180;
ScrollBar sc = new ScrollBar();
content.getChildren().add(sc);
sc.setLayoutX(content.getWidth() - sc.getWidth());
sc.setMin(0);
sc.setOrientation(Orientation.VERTICAL);
sc.setPrefHeight(height);
sc.setMax(height * 2);
sc.valueProperty().addListener(new ChangeListener<Number>() {
public void changed(ObservableValue<? extends Number> ov,
Number old_val, Number new_val) {
content.setLayoutY(-new_val.doubleValue());
}
});
}
}
Add children to a HBox then pass it to scrollableItems(HBox content) above to add a SCrollBar
public HBox mainItemsWrapper() {
HBox scrollabelWrapper = new HBox();
scrollabelWrapper.setMaxHeight(180);
HBox entityDetailViewWrapper = new HBox();
entityDetailViewWrapper.getChildren().addAll(.....);
scrollabelWrapper.getChildren().add(entityDetailViewWrapper);
new ScrollableItems().scrollableItems(scrollabelWrapper);
return scrollabelWrapper;
}
Thank you all.....
I do not really get why you are trying to reinvent the wheel.. you should probably use the ScrollPane instead.
This little example shows how to create a horizontally scrollable HBox with the ScrollPane class:
#Override
public void start(Stage primaryStage) {
HBox hbox = new HBox();
Button b = new Button("add");
b.setOnAction(ev -> hbox.getChildren().add(new Label("Test")));
ScrollPane scrollPane = new ScrollPane(hbox);
scrollPane.setFitToHeight(true);
BorderPane root = new BorderPane(scrollPane);
root.setPadding(new Insets(15));
root.setTop(b);
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
As eckig said, you can wrap you HBox into a ScrollPane.
Additionnaly, you can customize the visual part of the scrollbar in CSS. I found this link useful for the comprehension of the differents parts of a scrollbar: Customize ScrollBar via CSS

Java FX - Removing a node if the user unchecks a checkbox

I am making a GUI using JavaFx, I want a name field to appear if the check box is checked and disappear if it is not checked.
Here is the code I wrote
test1.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Boolean b = test1.isSelected();
log(b);
Label label1 = new Label("Name:");
TextField textField = new TextField();
HBox hb = new HBox();
if (test1.isSelected()) {
hb.getChildren().addAll(label1, textField);
hb.setSpacing(10);
grid.add(hb, 3, 3);
} else {
**hb.getChildren().removeAll(label1 , textField);** <---look at this!
}
}
});
I am removing the node if it is unchecked but it doesn't execute it accordingly. What am I doing wrong?
Incase it is required, here is how I execute my scene.
Scene scene = new Scene(grid, 500, 500);
primaryStage.setScene(scene);
primaryStage.show();

Expanding Accordion with new Titled Pane

I want to refill an Accordion with a new collection of TitledPane, but I've found a problem expanding nodes in the accordion.
I guess that it's an issue of the JavaFX library. Because it also happen with the sample provided in the manual for Accordion and TitledPane from Oracle. I take the original Example 22-5, and change it like this:
public void start(Stage stage) {
stage.setTitle("TitledPane");
Scene scene = new Scene(new Group(), 800, 250);
scene.setFill(Color.GHOSTWHITE);
final Label label = new Label("Initial");
// --- Accordion
final Accordion accordion = new Accordion ();
for (int i = 0; i < imageNames.length; i++) {
images[i] = new
Image(getClass().getResourceAsStream(imageNames[i] + ".jpg"));
pics[i] = new ImageView(images[i]);
tps[i] = new TitledPane(imageNames[i],pics[i]);
}
accordion.getPanes().addAll(tps);
accordion.expandedPaneProperty().addListener(new
ChangeListener<TitledPane>() {
public void changed(ObservableValue<? extends TitledPane> ov,
TitledPane old_val, TitledPane new_val) {
if (new_val != null) {
label.setText("expanded: " +accordion.getExpandedPane().getText());
}
}
});
HBox hbox = new HBox(10);
Button buttonClear = new Button("Clear and refill");
buttonClear.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
for (int i = 0; i < imageNames.length; i++) {
tps[i] = new TitledPane(imageNames[i]+" refill",pics[i]);
}
accordion.getPanes().clear();
accordion.getPanes().addAll(tps);
accordion.getPanes().get(0).setExpanded(true);
}
});
hbox.setPadding(new Insets(20, 0, 0, 20));
hbox.getChildren().setAll(label, accordion, buttonClear);
Group root = (Group)scene.getRoot();
root.getChildren().add(hbox);
stage.setScene(scene);
stage.show();
}
When I change the expanded TitledPane, the label takes its value. And I also added a button, that clear all the TitledPane in the Accordion, create a new ones and set the first TitledPane as Expanded.
So when I press the button, the accordion it's refilled, the label says that the First TitledPane it's expanded but the last one it's the one who seems selected, but without the image. This occurs the second time we press this button, or if we previously select one of the TitledPane.
And this thing it's precisely what I want to do. Clear all the TitledPane and refill it with new TitledPane, and it's giving the same weird result.
How can I clean all the TitledPane, create a new ones and set as expanded the first TitledPane without this side effects that I explain before?
Thanks for reading!

Categories