I have a menu which should be used to navigate through different "views"(layout). The menu should always be visible. The only thing that should change is the displayed content, in dependency of the selected menu item.
I have a very basic example, using two classes, created below:
public class Main extends Application{
static final Pane pane = new Pane();
public void start(Stage stage) {
pane.getChildren().addAll(Menu.getPaneMenu());
Scene scene = new Scene(pane);
stage.setScene(scene);
stage.setMaximized(true);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
public class Menu {
private static final Pane paneMenu = new Pane();
static Pane getPaneMenu() {
Button btn1 = new Button("Menu 1");
btn1.relocate(100, 10);
btn1.setPrefSize(100, 20);
btn1.setOnAction(e -> {
Label lbl = new Label("You clicked Menu1");
lbl.relocate(200, 40);
Main.pane.getChildren().add(lbl);
});
Button btn2 = new Button("Menu 2");
btn2.relocate(300, 10);
btn2.setPrefSize(100, 20);
btn2.setOnAction(e -> {
Label lbl = new Label("You clicked Menu2");
lbl.relocate(200, 40);
Main.pane.getChildren().add(lbl);
});
paneMenu.getChildren().addAll(btn1, btn2);
return paneMenu;
}
}
The challenge I´m facing is, that when clicking a menu, the old menu stays(as you can see, the displayed text overwrites the other):
What would be an efficient way to solve this overlapping?
Here is an example that shows how to load two different Panes in a BorderPane.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Sample extends Application {
#Override public void start(Stage stage) {
BorderPane root = new BorderPane();
Button loadSceneOne = new Button("Load Scene One");
//Event hanlder to load scene one
loadSceneOne.setOnAction((event)->{
root.setCenter(getSceneOne());
});
//Event hanlder to load scene two
Button loadSceneTwo = new Button("Load Scene Two");
loadSceneTwo.setOnAction((event)->{
root.setCenter(getSceneTwo());
});
VBox menu = new VBox();
menu.getChildren().addAll(loadSceneOne, loadSceneTwo);
root.setLeft(menu);
Scene scene = new Scene(root);
stage.setMaximized(true);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
//Scene One
Pane getSceneOne()
{
Pane pane = new Pane();
Label label = new Label("Scene One!");
StackPane stackPane = new StackPane();
stackPane.getChildren().add(label);
pane.getChildren().add(stackPane);
return pane;
}
//Scene Two
Pane getSceneTwo()
{
Pane pane = new Pane();
Label label = new Label("Scene Two!");
StackPane stackPane = new StackPane();
stackPane.getChildren().add(label);
pane.getChildren().add(stackPane);
return pane;
}
}
What about declaring labels as fields of the Menu class (same with buttons)? Then you can add/remove them from the pane. I modified your code a little - it should works in this particular case:
class Menu {
private static final Pane paneMenu = new Pane();
private static final Button btn1 = new Button("Menu 1");
private static final Button btn2 = new Button("Menu 2");
private static final Label firstLabel = new Label("You clicked Menu1");
private static final Label secondLabel = new Label("You clicked Menu2");
static Pane getPaneMenu() {
ObservableList<Node> mainPaneChildren = Main.pane.getChildren();
btn1.relocate(100, 10);
btn1.setPrefSize(100, 20);
btn1.setOnAction(e -> {
firstLabel.relocate(200, 40);
if (mainPaneChildren.contains(secondLabel)) {
mainPaneChildren.remove(secondLabel);
}
if (!mainPaneChildren.contains(firstLabel)) {
mainPaneChildren.add(firstLabel);
}
});
btn2.relocate(300, 10);
btn2.setPrefSize(100, 20);
btn2.setOnAction(e -> {
secondLabel.relocate(200, 40);
if (mainPaneChildren.contains(firstLabel)) {
mainPaneChildren.remove(firstLabel);
}
if (!mainPaneChildren.contains(secondLabel)) {
mainPaneChildren.add(secondLabel);
}
});
paneMenu.getChildren().addAll(btn1, btn2);
return paneMenu;
}
}
If there are going to be more elements than just a Label it would be better if you put them in some kind of a Pane. Than you can add/remove that Pane.
By the way - have a look at Working With Layouts in JavaFX? I don't think that using relocate() is a good practice.
Related
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();
}
}
I would like to create a BorderPane layout in JavaFX with no center pane.
The code I have written so far only implements the left and right borders and is below:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class GUI_Practice extends Application {
#Override
public void start(Stage stage) throws Exception {
String blackBorder = "-fx-border-style: solid; -fx-border-width: 1; -fx-border-color: black";
/* Left column */
Button save = new Button("Save");
Button del = new Button("Delete");
HBox settings = new HBox(save, del);
VBox leftCol = new VBox(settings);
leftCol.setStyle(blackBorder);
/* Right column */
Button calculate = new Button("Calculate");
Button cancel = new Button("Cancel");
HBox runButtons = new HBox(calculate, cancel);
VBox rightCol = new VBox(runButtons);
rightCol.setStyle(blackBorder);
/* Set up borderpane */
BorderPane root = new BorderPane();
root.setPadding(new Insets(15));
root.setLeft(leftCol);
root.setRight(rightCol);
Scene scene = new Scene(root, 800, 600);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
The output it gives is shown in the image below:
However, I want it to look more like this:
Where the left and right columns are equal width and take up the entire width of the window. Additionally, the columns do not change width with the window, so the whitespace in the middle gets bigger as the window gets bigger.
What do I need to change to make the columns fill the width of the window?
(P.S. I'm still learning, so if the solution could avoid FXML (which I don't understand yet), that'd be great)
EDIT: as per #k88's suggestion, my start method now looks like so:
public void start(Stage stage) throws Exception {
String blackBorder = "-fx-border-style: solid; -fx-border-width: 1; -fx-border-color: black";
Button calculate = new Button("Calculate");
Button cancel = new Button("Cancel");
HBox runButtons = new HBox(calculate, cancel);
VBox rightCol = new VBox(runButtons);
rightCol.setStyle(blackBorder);
Button save = new Button("Save");
Button del= new Button("Delete");
HBox settings = new HBox(save, load);
VBox leftCol = new VBox(settings);
leftCol.setStyle(blackBorder);
HBox root = new HBox(leftCol, rightCol);
root.setPadding(new Insets(15));
Scene scene = new Scene(root, 800, 600);
stage.setScene(scene);
stage.show();
}
Giving a window looking like:
There are different ways to get this problem fixed.
If you want to still gain the benefits from BorderPane (like to have top and bottom panes), you can set a HBox/GridPane as the center (without setting left/right).
If you are not bothered about top and bottom layout implementations, then as #k88 suggested, you can use directly HBox or GridPane as your root node.
Using HBox:
HBox.setHGrow(leftCol,Priority.ALWAYS);
HBox.setHGrow(rightCol,Priority.ALWAYS);
HBox root = new HBox();
root.setPadding(new Insets(15));
root.getChildren().addAll(leftCol, rightCol);
Using GridPane:
GridPane root = new GridPane();
ColumnConstraints col1 = new ColumnConstraints();
col1.setPercentWidth(50);
ColumnConstraints col2 = new ColumnConstraints();
col2.setPercentWidth(50);
root.getColumnConstraints().addAll(col1,col2);
root.addRow(0, leftCol,rightCol);
Update: In either cases, if you want your buttons to auto stretch, bind the width of the buttons to its layout. This way you can control the buttons width proportion in the HBox.
Button calculate = new Button("Calculate");
Button cancel = new Button("Cancel");
HBox runButtons = new HBox(calculate, cancel);
calculate.prefWidthProperty().bind(runButtons.widthProperty().divide(2));
cancel.prefWidthProperty().bind(runButtons.widthProperty().divide(2));
Update 2: Please find below a sample demo.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class Sample extends Application {
public void start(Stage stage) throws Exception {
String blackBorder = "-fx-border-style: solid; -fx-border-width: 1; -fx-border-color: black";
Button calculate = new Button("Calculate");
Button cancel = new Button("Cancel");
HBox runButtons = new HBox(calculate, cancel);
calculate.prefWidthProperty().bind(runButtons.widthProperty().divide(2));
cancel.prefWidthProperty().bind(runButtons.widthProperty().divide(2));
VBox rightCol = new VBox(runButtons);
rightCol.setStyle(blackBorder);
Button save = new Button("Save");
Button del = new Button("Delete");
HBox settings = new HBox(save, del);
save.prefWidthProperty().bind(settings.widthProperty().divide(3)); // 1/3
del.prefWidthProperty().bind(settings.widthProperty().divide(3).multiply(2)); // 2/3
VBox leftCol = new VBox(settings);
leftCol.setStyle(blackBorder);
GridPane root = new GridPane();
ColumnConstraints col1 = new ColumnConstraints();
col1.setPercentWidth(50);
ColumnConstraints col2 = new ColumnConstraints();
col2.setPercentWidth(50);
root.getColumnConstraints().addAll(col1,col2);
root.addRow(0, leftCol,rightCol);
Scene scene = new Scene(root, 800, 600);
stage.setScene(scene);
stage.show();
}
public static void main(String... a) {
Application.launch(a);
}
}
vbox2.setPadding(new Insets(3));
vbox2.setSpacing(3);
vbox2.getChildren().addAll( browser1,browser);
HBox.setHgrow(vbox2, Priority.ALWAYS);
hbox.setPadding(new Insets(20));
// StackPane.setMargin(hbox, new Insets(20));
hbox.getChildren().addAll(vbox, vbox2);
root.getChildren().add(hbox);
Scene scene = new Scene(root, 500, 300); // the stack pane is the root node
//scene.setCursor(Cursor.CROSSHAIR);
MenuBar menuBar = new MenuBar();
Menu menu = new Menu("Window");
menu.getItems().add(new MenuItem("browser"));
menu.getItems().add(new MenuItem("img"));
menuBar.getMenus().add(menu);
menuBar.prefWidthProperty().bind(primaryStage.widthProperty());
BorderPane borderPane = new BorderPane();
borderPane.prefHeightProperty().bind(scene.heightProperty());
borderPane.prefWidthProperty().bind(scene.widthProperty());
borderPane.setTop(menuBar);
root.getChildren().add(borderPane);
primaryStage.setScene(scene);
primaryStage.show();
}
here is the code part where is im adding menu bar with border pane but its hang my application as im not able to login or do anythng and i had to add child window also for reference im attaching the image
This is a very basic and minimal example of handling views in the center of a BorderPane by MenuItems:
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.BorderPane;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
// create the menu bar with a menu and its items
MenuBar menuBar = new MenuBar();
Menu mainMenu = new Menu("Window");
MenuItem browserItem = new MenuItem("browser");
MenuItem imageItem = new MenuItem("image");
MenuItem closeItem = new MenuItem("exit");
// create some different contents for the center of the border pane
Label imagePlaceHolder = new Label("IMAGE TO BE SHOWN");
WebView browser = new WebView();
WebEngine browserEngine = browser.getEngine();
// set the actions for the different items
closeItem.setOnAction(action -> {
System.exit(0);
});
imageItem.setOnAction(action -> {
root.setCenter(imagePlaceHolder);
});
browserItem.setOnAction(action -> {
root.setCenter(browser);
browserEngine.load("http://www.google.com");
});
// add items to the menu, then the menu to the menu bar
mainMenu.getItems().addAll(closeItem, browserItem, imageItem);
menuBar.getMenus().add(mainMenu);
// set the scene
Scene scene = new Scene(root,400,400);
root.setTop(menuBar);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
I hope it helps…
EDIT
I just saw your newest edit… In case you really need different windows (scenes or stages), the approach will get more complex. Readers have to get more information about the different windows (like how you create them, handle their contents and more).
I have made an application which uses tabpane. I am able to set tooltip and title of each tab dynamically. But how do I set its contents dynamically. I am sure that I can maintain a list of Node object and add it to tab during iteration, but I feel there are other ways to do it. Here is what I have done so far.
public class Index extends Application {
public static void main(String[] args) {
launch(args);
}
final String[] tabContent={"title1"
,"title2"
,"title3"
,"title4"
,"title5"};
final String[] tabToolTip={"tooltip1"
,"tooltip2"
,"tooltip3"
,"tooltip4"
,"tooltip5"};
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Ipas Utility");
Group root = new Group();
Scene scene = new Scene(root, 1000, 600, Color.ALICEBLUE);
TabPane tabPane = new TabPane();
tabPane.setTooltip(new Tooltip("Hover on each tab for an overview"));
BorderPane borderPane = new BorderPane();
for (int i = 0; i < 5; i++) {
Tab tab = new Tab();
tab.setText(tabContent[i]);
tab.setClosable(false);
tab.setTooltip(new Tooltip(tabToolTip[i]));
HBox hbox = new HBox();
hbox.getChildren().add(new Label(tabContent[i]));
hbox.setAlignment(Pos.CENTER);
tab.setContent(hbox);;
tabPane.getTabs().add(tab);
}
// bind to take available space
borderPane.prefHeightProperty().bind(scene.heightProperty());
borderPane.prefWidthProperty().bind(scene.widthProperty());
borderPane.setCenter(tabPane);
root.getChildren().add(borderPane);
primaryStage.setScene(scene);
primaryStage.show();
}
}
How I can maintain tabcontent in a list/array outside start block?
That's quite okay what you did. Here's a modified version of your code which allows you to add a tab on button click:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class Main extends Application {
private static int tabCount = 0;
#Override
public void start(Stage primaryStage) {
TabPane tabPane = new TabPane();
tabPane.getTabs().add(createTab());
tabPane.getTabs().add(createTab());
tabPane.getTabs().add(createTab());
Button addTabButton = new Button( "Add Tab");
addTabButton.setOnAction(e -> {
tabPane.getTabs().add(createTab());
});
Button logButton = new Button( "Log");
logButton.setOnAction(e -> {
for( Tab tab: tabPane.getTabs()) {
System.out.println( "Tab " + tab.getText() + " has content " + tab.getContent());
}
});
HBox toolbar = new HBox();
HBox.setMargin(addTabButton, new Insets(5,5,5,5));
HBox.setMargin(logButton, new Insets(5,5,5,5));
toolbar.getChildren().addAll( addTabButton, logButton);
BorderPane root = new BorderPane();
root.setTop(toolbar);
root.setCenter(tabPane);
Scene scene = new Scene(root, 640, 480);
primaryStage.setScene(scene);
primaryStage.show();
}
public static Tab createTab() {
tabCount++;
Tab tab = new Tab();
tab.setText("Tab " + tabCount);
tab.setTooltip( new Tooltip( "Tooltip Tab " + tabCount));
Node content = new Label( "Content Tab " + tabCount);
tab.setContent(content);
return tab;
}
public static void main(String[] args) {
launch(args);
}
}
In order to access the tabs you can use getTabs. And in order to change the content, i. e. the node that represents the content, you can use setContent.
The example code shows you how to iterate through the tabs by pressing the log button.
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();
}