So I have to make a Zodiac Sign GUI, and we are tasked with having the following:
a Label in the top left, and a TextField in the top right (both with padding)
an exit Button in the center of the GUI, along with a clear and find my sign on either side
and finally, a Label in the bottom center prompting the sign
I am utterly confused on how to have this come out, as I am a novice in JavaFX. I believe I would need a branch node along with the root node in order to get this kind of layout. I do not need assistance in instantiating the button, labels etc., mainly confused with how this layout can even work. The code I have now is the following:
public class ZodiacGUI extends Application {
public static void main(String args[]) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
BorderPane mainPane = new BorderPane();
mainPane.setStyle("-fx-background-color: PINK");
setupControls(mainPane);
Scene scene = new Scene(mainPane);
setStage(primaryStage, scene);
}
public void setStage(Stage primaryStage, Scene scene) {
primaryStage.setWidth(500);
primaryStage.setHeight(200);
primaryStage.setTitle("What is my Zodiac Sign?");
primaryStage.setScene(scene);
primaryStage.show();
}
public void setupControls(BorderPane mainPane) {
Label label = new Label("Enter you birthday formatted as -> mm/dd");
Button exitButton = new Button();
Button findSign = new Button();
Button clear = new Button();
TextField userInput = new TextField();
userInput.setPromptText("Enter birthday");
exitButton.setText("Exit.");
findSign.setText("Find my sign.");
clear.setText("Clear.");
exitButton.setOnAction(e -> System.exit(0));
mainPane.setLeft(label);
mainPane.setRight(userInput);
mainPane.setCenter(exitButton);
mainPane.setCenter(findSign);
mainPane.setCenter(clear);
BorderPane.setAlignment(label, Pos.TOP_LEFT);
BorderPane.setAlignment(userInput, Pos.TOP_RIGHT);
BorderPane.setAlignment(exitButton, Pos.CENTER);
BorderPane.setAlignment(findSign, Pos.CENTER_LEFT);
BorderPane.setAlignment(clear, Pos.CENTER_RIGHT);
}
}
This only outputs one of the buttons out of the three, as I assume it is because the necessary addition of another BorderPane? Here is a drawn out picture of what I would like to come out with:
Just to clarify, I do not need assistance with the handling of finding the zodiac sign, etc. Mainly need assistance on the layout, as it has stumped me for days. Thank you in advance for helping out a novice to JavaFX :).
You have three rows with diffrent number of children. You can use HBox if row have more than one child.
BorderPane mainPane = new BorderPane();
mainPane.setTop(new HBox(topLabel, topField));
mainPane.setCenter(new HBox(centerLabel, centerField, centerButtom));
mainPane.setBottom(bottomCenterButton);
If you need more than 3 rows (top, center, bottom section of BorderPane) you can use VBox, where every child is row like:
HBox row1 new HBox(child1, child2)
HBox row2 new HBox(child1, child2, child3)
HBox row3 new HBox(child1)
HBox row4 new HBox(child1, child2)
VBox pane = new VBox(row1, row2, row3, row4);
You might want to use a GridPane
GridPane grid = new GridPane();
grid.add(label,0,0);
grid.add(userInput,2,0);
grid.add(findSign,0,1);
grid.add(exitButton,1,1);
grid.add(clear,2,1);
or use a VBox with BorderPane to help with the layout/alignment
BorderPane mainPane, centerPane;
mainPane.setLeft(label);
mainPane.setRight(userInput);
centerPane.setLeft(findSign);
centerPane.setRight(clear);
centerPane.setCenter(exitButton);
BorderPane.setAlignment(label, Pos.LEFT);
BorderPane.setAlignment(userInput, Pos.RIGHT);
BorderPane.setAlignment(exitButton, Pos.CENTER);
BorderPane.setAlignment(findSign, Pos.LEFT);
BorderPane.setAlignment(clear, Pos.RIGHT);
VBox items = new VBox();
items.getChildren().addAll(mainPane, centerPane);
Related
I am trying to get 2 elements, a button and a label, to have their own individual alignments in a single HBox in javafx. My code thus far:
Button bt1= new Button("left");
bt1.setAlignment(Pos.BASELINE_LEFT);
Label tst= new Label("right");
tst.setAlignment(Pos.BASELINE_RIGHT);
BorderPane barLayout = new BorderPane();
HBox bottomb = new HBox(20);
barLayout.setBottom(bottomb);
bottomb.getChildren().addAll(bt1, tst);
by default, the hbox shoves both elements to the left, next to each other.
The borderpane layout is right now necessary for my project, but as for right now, is there some way to force the label tst to stay on the far right side of the hbox, and bt1 to stay on the far left?
I can also do css, if -fx-stylesheet stuff works this way.
You need to add the left node to an AnchorPane and make that AnchorPane grow horizontally.
import javafx.application.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;
/**
*
* #author Sedrick
*/
public class JavaFXApplication33 extends Application {
#Override
public void start(Stage primaryStage)
{
BorderPane bp = new BorderPane();
HBox hbox = new HBox();
bp.setBottom(hbox);
Button btnLeft = new Button("Left");
Label lblRight = new Label("Right");
AnchorPane apLeft = new AnchorPane();
HBox.setHgrow(apLeft, Priority.ALWAYS);//Make AnchorPane apLeft grow horizontally
AnchorPane apRight = new AnchorPane();
hbox.getChildren().add(apLeft);
hbox.getChildren().add(apRight);
apLeft.getChildren().add(btnLeft);
apRight.getChildren().add(lblRight);
Scene scene = new Scene(bp, 300, 250);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
When you call setAlignment() on Button or Label it according to JavaDoc:
Specifies how the text and graphic within the Labeled should be
aligned when there is empty space within the Labeled.
So it's just a position of the text inside your Button or Label. But what you need is to wrap your Button or Label inside some container (lets say HBox) and make it fill all available space (HBox.setHgrow(..., Priority.ALWAYS)):
Button bt1= new Button("left");
HBox bt1Box = new HBox(bt1);
HBox.setHgrow(bt1Box, Priority.ALWAYS);
Label tst= new Label("right");
BorderPane barLayout = new BorderPane();
HBox bottomb = new HBox(20);
barLayout.setBottom(bottomb);
bottomb.getChildren().addAll(bt1Box, tst);
I’m going to add two text fields in a row of Java FX GridPane layout. And I want to fill one text field throughout the available space. If I add GridPane directly to the Scene, then it is working fine. But If I add GridPane to another layout like Group (or any other layout) then fill property is not working correctly.
Following code is working as expected.
public void start(final Stage stage) {
GridPane parent = new GridPane();
TextField addEmail = new TextField();
TextField addFirstName = new TextField();
parent.add(addEmail, 0, 0);
parent.add(addFirstName, 1, 0);
ColumnConstraints cons1 = new ColumnConstraints();
cons1.setFillWidth(true);
cons1.setHgrow(Priority.ALWAYS);
parent.getColumnConstraints().add(cons1);
Scene scene = new Scene(parent, 400, 300, Color.WHITE);
stage.setScene(scene);
stage.show();
}
Following one is not working (But I want to make this work).
public void start(final Stage stage) {
GridPane parent = new GridPane();
TextField addEmail = new TextField();
TextField addFirstName = new TextField();
parent.add(addEmail, 0, 0);
parent.add(addFirstName, 1, 0);
ColumnConstraints cons1 = new ColumnConstraints();
cons1.setFillWidth(true);
cons1.setHgrow(Priority.ALWAYS);
parent.getColumnConstraints().add(cons1);
Group group = new Group();
group.getChildren().addAll(parent);
Scene scene = new Scene(group, 400, 300, Color.WHITE);
stage.setScene(scene);
stage.show();
}
I’m new to Java FX hence my approach may completely wrong please direct me to the correct path.
Sorry, but you've chosen the wrong parent.
From the javadoc of Group:
By default, a Group will "auto-size" its managed resizable children to their preferred sizes during the layout pass to ensure that Regions and Controls are sized properly as their state changes.
A Group simply does not modify the size of it's children other than resizing them to the preferred size.
Only if you put the GridPane in a parent that resizes it, like e.g. StackPane, it's size is modified...
I have a Pane and a VBox inside of a StackPane. I added the Pane first and the VBox on top of it. The VBox includes several kes that in turn have Buttons as children. I use the normal Pane as a "canvas" to position Lines on it. The Lines as well as the Buttons need to be interactive. So by clicking on them they shall for example change their color.
But at the moment the Pane and its Line objects are shown but they are covered by the VBox so I can not interact with them but only with the Buttons.
How can I provide that I can interact with the Line as well, though they are in a lower layer of the StackPane?
You can set the pickOnBoundsProperty of your container Nodes (VBox and HBox) to false.
Defines how the picking computation is done for this node when
triggered by a MouseEvent or a contains function call. If pickOnBounds
is true, then picking is computed by intersecting with the bounds of
this node, else picking is computed by intersecting with the geometric
shape of this node.
As result, "transparent" areas of the HBox and the VBox will not register the click event.
Example:
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
Pane pane = new Pane();
pane.setStyle("-fx-background-color:red;");
StackPane sp = new StackPane();
VBox vbox = new VBox();
HBox hbox = new HBox();
hbox.setSpacing(30);
for (int i = 0; i< 5; i++) {
Button b = new Button("Button");
b.setOnAction(e -> System.out.println("Button clicked"));
hbox.getChildren().add(b);
}
vbox.getChildren().add(hbox);
sp.getChildren().addAll(pane, vbox);
Line line = new Line(10, 10, 500, 10);
line.setStrokeWidth(3);
pane.getChildren().add(line);
line.setOnMouseClicked(e -> {
System.out.println("Line Clicked!");
});
// Set pickOnBounds to vbox and hbox
vbox.setPickOnBounds(false);
hbox.setPickOnBounds(false);
root.setCenter(sp);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
They are covered cause the VBOX is in front of the Pane.
First Way:
You can setVisible(false) the VBox so the Pane can be accessible and then setVisible(true) the VBox again.
Second Way:
You can use methods called toBack(); and toFront(); and bring a Node back or front to the hierarchy:
vBox.toBack(); //the the vBox goes back to the hierarchy,it is like zOrder in html
and then use:
vBox.toFront(); //to bring the vBox again in front.
Finally:
You can somewhere provide a ToggleButton that when is pressed the VBox is appearing and when is not pressed the VBox is disappeared.
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
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!