In JavaFx, How can I wrap the BorderPane by ScrollPane - java

I want to make scroll by wrapping my BorderPane. I tried like this, but it doesn't work. What's my problem?
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Drawing Test");
VBox content = new VBox(5);
ScrollPane scroller = new ScrollPane(content);
scroller.setFitToWidth(true);
BorderPane root = new BorderPane();
initRects(root);
content.getChildren().add(root);
Scene scene = new Scene(new BorderPane(scroller, null, null, null, null), 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
void initRects(BorderPane root){
DrawRect(root, 0);
DrawRect(root, 100);
DrawRect(root, 200);
DrawRect(root, 300);
}
void DrawRect(BorderPane root, double y){
Rectangle rect = new Rectangle(50, 50 + y, 900, 50);
rect.setFill(Color.DODGERBLUE);
root.getChildren().add(rect);
}
}
Here is the result view.

A BorderPane will perform its layout calculations (including the calculations for how large it wants/needs to be) by looking at the nodes placed in the five regions top, right, bottom, left, center.
Since the rectangles are simply added directly to the border pane's list of child nodes, and are not laid out directly by the border pane at all, the border pane does not report any required size. Consequently, the scroll pane wrapping it does not know that it needs to include scroll bars.
If you want to display these rectangles, a plain Pane is a better container for them (they basically manage their own layout, so you don't want a pane that manages layout of the child nodes):
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Drawing Test");
VBox content = new VBox(5);
ScrollPane scroller = new ScrollPane(content);
scroller.setFitToWidth(true);
Pane root = new Pane();
initRects(root);
content.getChildren().add(root);
Scene scene = new Scene(new BorderPane(scroller, null, null, null, null), 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
void initRects(Pane root){
DrawRect(root, 0);
DrawRect(root, 100);
DrawRect(root, 200);
DrawRect(root, 300);
}
void DrawRect(Pane root, double y){
Rectangle rect = new Rectangle(50, 50 + y, 900, 50);
rect.setFill(Color.DODGERBLUE);
root.getChildren().add(rect);
}
}
This now produces the vertical scroll bar, which is (just) required for this content.
Note that the line
scroller.setFitToWidth(true);
forces the content to be the same width as the scroll pane's viewport, so it will effectively prevent any horizontal scroll bar from being displayed (and, in this case, clip the contained pane). If you remove that, you will also see the horizontal scroll bar (which is what I suspect you want):
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Drawing Test");
VBox content = new VBox(5);
ScrollPane scroller = new ScrollPane(content);
// scroller.setFitToWidth(true);
Pane root = new Pane();
initRects(root);
content.getChildren().add(root);
Scene scene = new Scene(new BorderPane(scroller, null, null, null, null), 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
void initRects(Pane root){
DrawRect(root, 0);
DrawRect(root, 100);
DrawRect(root, 200);
DrawRect(root, 300);
}
void DrawRect(Pane root, double y){
Rectangle rect = new Rectangle(50, 50 + y, 900, 50);
rect.setFill(Color.DODGERBLUE);
root.getChildren().add(rect);
}
}

Related

Why are my JavaFX buttons unevenly spaced?

I'm new to JavaFX, trying to build a GUI program that displays a bill for a table at a restaurant when you click on that table. The spacing is off between the table buttons and I'm not sure why.
The GUI class for my program:
package restaurantBillingProgram;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.scene.control.Label;
import javafx.scene.control.Button;
import javafx.geometry.Pos;
public class BillingGUI extends Application {
#Override
public void start(Stage primaryStage) {
// Create grid pane
GridPane pane = new GridPane();
pane.setAlignment(Pos.CENTER);
pane.setHgap(5);
pane.setVgap(5);
// Label
pane.add(new Label("Generate bill"), 1, 0);
// Buttons
Button btT1 = new Button("Table 1");
pane.add(btT1, 0, 1);
btT1.setOnAction(e - > Billing.generateT1());
Button btT2 = new Button("Table 2");
pane.add(btT2, 1, 1);
btT2.setOnAction(e - > Billing.generateT2());
Button btT3 = new Button("Table 3");
pane.add(btT3, 2, 1);
btT3.setOnAction(e - > Billing.generateT3());
// Create scene and place in stage
Scene scene = new Scene(pane, 250, 250);
primaryStage.setTitle("Restaurant Billing Program");
primaryStage.setScene(scene);
primaryStage.show();
}
// Main method
public static void main(String[] args) {
launch(args);
}
}
From the Javadoc:
Row/Column Sizing
By default, rows and columns will be sized to fit their content; a column will be wide enough to accommodate the widest child, ...
The label in row 0 column 1 forces that column to be wider.
You probably want the label to be centered and span all 3 columns.
While doing you layout, use pane.setGridLinesVisible(true). This should only be used during debugging. It can be very useful for situations like your current situation. As #Jim Garrison pointed out, your Label is causing the issue:
Issue:
One way to fix this is to let the Label span all columns and center the Label's text.
Fix:
Key Code:
label.setMaxWidth(Double.MAX_VALUE);
label.setAlignment(Pos.CENTER);
pane.add(label, 0, 0, 3, 1);// Look at the following link to see how this add method works. https://openjfx.io/javadoc/11/javafx.graphics/javafx/scene/layout/GridPane.html#add(javafx.scene.Node,int,int,int,int)
Full Code:
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.scene.control.Label;
import javafx.scene.control.Button;
import javafx.geometry.Pos;
public class BillingGUI extends Application {
#Override
public void start(Stage primaryStage) {
// Create grid pane
GridPane pane = new GridPane();
pane.setAlignment(Pos.CENTER);
pane.setHgap(5);
pane.setVgap(5);
pane.setGridLinesVisible(true);//Use for debugging only!!!!
// Label
Label label = new Label("Generate bill");
label.setMaxWidth(Double.MAX_VALUE);
label.setAlignment(Pos.CENTER);
pane.add(label, 0, 0, 3, 1);
// Buttons
Button btT1 = new Button("Table 1");
pane.add(btT1, 0, 1);
Button btT2 = new Button("Table 2");
pane.add(btT2, 1, 1);
Button btT3 = new Button("Table 3");
pane.add(btT3, 2, 1);
// Create scene and place in stage
Scene scene = new Scene(pane, 250, 250);
primaryStage.setTitle("Restaurant Billing Program");
primaryStage.setScene(scene);
primaryStage.show();
}
// Main method
public static void main(String[] args) {
launch(args);
}
}

How to make left-aligned TextFlow in the center of the window in JavaFX

How to make text in TextFlow justify left, but the TextFlow is in the center of the window?
I try to implement it with VBox, StackPane and BorderPane, but they can only align the text in the center, or make the TextFlow to the left of the window.
The effect I need is similar to IDEA:
But the effect I achieved is like this:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
TextFlow text = new TextFlow(
new Text("Search Everywhere\n"),
new Text("Project View\n"),
new Text("Go to File\n")
);
text.setTextAlignment(TextAlignment.LEFT);
VBox root = new VBox(text);
root.setAlignment(Pos.CENTER);
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}
}
Thanks for c0der tips, I found FlowPane can easily achieve this effect:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
TextFlow text = new TextFlow(
new Text("Search Everywhere\n"),
new Text("Project View\n"),
new Text("Go to File\n")
);
text.setTextAlignment(TextAlignment.LEFT);
FlowPane root = new FlowPane(text);
root.setAlignment(Pos.CENTER);
primaryStage.setScene(new Scene(root, 400, 400));
primaryStage.show();
}
}
I am not sure this is exactly what you are looking for, but should put you in the right direction. I added color so you can see where one control ends and the other continues.
To make this work you need to ensure that your TextFlow doesn't size bigger than what you want, otherwise it will not give you the expected behavior. In this instance I choose 200x200 and you will see it center in the window.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.scene.text.TextFlow;
import javafx.stage.Stage;
public class Sample extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
TextFlow tf = new TextFlow();
tf.setStyle("-fx-background-color: red;");
tf.setTextAlignment(TextAlignment.LEFT);
tf.setMaxSize(200, 200);
StackPane sp = new StackPane(tf);
sp.setStyle("-fx-background-color: blue;");
Text t1 = new Text("This is line one, left justified" + System.lineSeparator());
Text t2 = new Text("This is line two, left justified"+ System.lineSeparator());
Text t3 = new Text("This is line three, left justified"+ System.lineSeparator());
Text t4 = new Text("This is line four, left justified"+ System.lineSeparator());
tf.getChildren().addAll(t1, t2, t3, t4);
Scene scene = new Scene(sp);
primaryStage.setScene(scene);
primaryStage.setWidth(600);
primaryStage.setHeight(600);
primaryStage.show();
}
}

Have three equally sized VBoxes in HBox in javafx

I have three VBoxes in a HBox. I want all of them to always take one third of the HBox and the full height. I've tried HBox.setHgrow(<every VBox>, Priority.ALWAYS) with <every VBox>.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); and that worked fine, but when I added a component to one of the VBoxes, it resized itself and became larger than the other ones.
Any idea how to solve this properly?
Use a GridPane instead of the HBox. You can use a collection of column constraints, each with the percentWidth set to give each column equal width.
SSCCE:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.RowConstraints;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class VBoxInGridPane extends Application {
#Override
public void start(Stage primaryStage) {
VBox box1 = new VBox();
box1.setStyle("-fx-background-color: -fx-background; -fx-background: red ;");
box1.getChildren().add(new Label("Content"));
VBox box2 = new VBox();
box2.setStyle("-fx-background-color: green ;");
VBox box3 = new VBox();
box3.setStyle("-fx-background-color: blue ;");
GridPane root = new GridPane();
root.add(box1, 0, 0);
root.add(box2, 1, 0);
root.add(box3, 2, 0);
for (int i = 0 ; i < 3 ; i++) {
ColumnConstraints cc = new ColumnConstraints();
cc.setPercentWidth(100.0/3.0);
cc.setHgrow(Priority.ALWAYS);
root.getColumnConstraints().add(cc);
}
RowConstraints rc = new RowConstraints();
rc.setVgrow(Priority.ALWAYS);
root.getRowConstraints().add(rc);
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
What you could do is to add your VBox to a StackPane and the StackPane to the HBox. Into the StackPane you also place a placeholder (I usually use a transparent Rectangular) and bind that to the binding maxVBoxWidth. This is a Binding that you have to define yourself:
DoubleBinding maxVBoxBinding = new DoubleBinding() {
{
super.bind(vbox1.widthProperty(),vbox2.widthProperty(), vbox3.widthProperty());
}
#Override
protected double computeValue() {
return Math.max(vbox1.getWidth(), Math.max(vbox2.getWidth(), vbox2.getWidth()));
}
}

Binding Rectangle.widthProperty() to its parent width does not work

I would like to place an HBox with a red rectangle in the middle of a BorderPane, and I would like that rectangle to grow or shrink with its container (the HBox).
This is my code:
public class Test extends Application {
#Override
public void start(Stage primaryStage) {
BorderPane borderPane = new BorderPane();
HBox hBox = new HBox();
hBox.setAlignment(Pos.CENTER);
Rectangle rect = new Rectangle(hBox.getWidth(),50);
rect.setFill(Color.RED);
rect.widthProperty().bind(hBox.widthProperty().subtract(20));
hBox.getChildren().add(rect);
borderPane.setCenter(hBox);
Scene scene = new Scene(borderPane, 900, 600, Color.WHITE);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
But it doesn't work. When I slowly resize my Frame, it works, nevertheless, when I quickly resize my Frame, the rectangle is not in the middle (not the same size too) and we can see the same things when we minimize and maximize the Frame.
I don't understand why it doesn't work.
This is what is happening:
When rect is asked for the preferred/minimum/maximum width during layout, it replies with its current width, which is before resize, because by that time hBox has not been resized yet. As a result, hBox's minimum width is reported to be its current width minus 20. Therefore, when you shrink the window, the hBox will still be resized to its previous width minus 20.
There are a number of ways how to go around this, but a more accurate answer depends on what you are trying to do, and may involve using Region instead of a Rectangle, or overriding layoutChildren method of the rectangle's parent.
Here is a way that is close to what you have now. It defines a resizable rectangle and overrides its minimum width to be 0.0, so it allows the HBox to be downsized.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class RectangleAutosize extends Application {
static class ResizableRectangle extends Rectangle {
ResizableRectangle(double w, double h) {
super(w, h);
}
#Override
public boolean isResizable() {
return true;
}
#Override
public double minWidth(double height) {
return 0.0;
}
}
#Override
public void start(Stage primaryStage) {
BorderPane borderPane = new BorderPane();
HBox hBox = new HBox();
hBox.setAlignment(Pos.CENTER);
Rectangle rect = new ResizableRectangle(hBox.getWidth(),50);
rect.setFill(Color.RED);
rect.widthProperty().bind(hBox.widthProperty().subtract(20));
hBox.getChildren().add(rect);
borderPane.setCenter(hBox);
Scene scene = new Scene(borderPane, 900, 600, Color.WHITE);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

How to align a button into the bottom right corner in JavaFX?

I'm extremely new to JavaFX, and I'm attempting to get a button(specifically scrapeBtn) into the bottom right corner of an application. Here is what I have so far:
package main;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Driver extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Button scrapeBtn = new Button();
scrapeBtn.setText("Scrape!");
scrapeBtn.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
System.out.println("Scrape button pressed.");
}
});
TextField console = new TextField();
GridPane root = new GridPane();
GridPane.setConstraints(scrapeBtn, 2, 2, 1, 1);
root.getChildren().add(scrapeBtn);
root.getChildren().add(console);
Scene scene = new Scene(root, 600, 400);
primaryStage.setTitle("Wiki Scraper");
primaryStage.setScene(scene);
primaryStage.show();
}
}
Any ideas as to how I could accomplish this? Some tips in general to aligning and formatting things with JavaFX would also be really appreciated.
Thanks.
I often use a BorderPane for similar purposes (e.g. a Dialog with some text and controls etc. at the center and one or more buttons at the bottom). Therefore, I use the BorderPane as root and a HBox as "button container" at the bottom. Finally, I set the botton alignment to "RIGHT".
Here an example based on your code:
#Override
public void start(Stage primaryStage) {
// center
VBox vbCenter = new VBox(); // use any container as center pane e.g. VBox
TextField console = new TextField();
vbCenter.getChildren().add(console);
// bottom respectively "button area"
HBox hbButtons = new HBox();
Button scrapeBtn = new Button();
scrapeBtn.setText("Scrape!");
scrapeBtn.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
System.out.println("Scrape button pressed.");
}
});
hbButtons.getChildren().add(scrapeBtn);
hbButtons.setAlignment(Pos.CENTER_RIGHT);
// root
BorderPane root = new BorderPane();
root.setPadding(new Insets(20)); // space between elements and window border
root.setCenter(vbCenter);
root.setBottom(hbButtons);
Scene scene = new Scene(root, 600, 400);
primaryStage.setTitle("Wiki Scraper");
primaryStage.setScene(scene);
primaryStage.show();
}
This code leads to this (after resizing the window a little bit):
You can use two BorderPanes to place a control bottom right
#Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();
BorderPane bottom = new BorderPane();
bottom.setRight(new Button("I am placed bottom right"));
root.setBottom(bottom);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setWidth(400);
primaryStage.setHeight(400);
primaryStage.show();
}

Categories

Resources