So Im trying to have text on the left and buttons on the right, text should have constant size and buttons should resize to fill the rest of the window.
Here is my result so far:
I dont want my text over buttons, I want them to share the whole window.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
GridPane buttons = new GridPane();
GridPane textGrid = new GridPane();
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
Button button1 = new Button();
Button button2 = new Button();
Button button3 = new Button();
Button button4 = new Button();
Button button5 = new Button();
button1.setText("Button1");
button2.setText("Button4");
button3.setText("Button3");
button4.setText("Button4");
button5.setText("Button5");
TextArea text1 = new TextArea();
text1.setText("Test");
text1.setPrefSize(100, 100);
button1.prefWidthProperty().bind(buttons.widthProperty());
button2.prefWidthProperty().bind(buttons.widthProperty());
button3.prefWidthProperty().bind(buttons.widthProperty());
button4.prefWidthProperty().bind(buttons.widthProperty());
button5.prefWidthProperty().bind(buttons.widthProperty());
button1.prefHeightProperty().bind(buttons.heightProperty());
button2.prefHeightProperty().bind(buttons.heightProperty());
button3.prefHeightProperty().bind(buttons.heightProperty());
button4.prefHeightProperty().bind(buttons.heightProperty());
button5.prefHeightProperty().bind(buttons.heightProperty());
buttons.addColumn(0, button1, button2, button3, button4, button5);
textGrid.addColumn(0, text1);
Scene scene = new Scene(root, 280, 180);
root.getChildren().addAll(buttons, textGrid);
buttons.setAlignment(Pos.TOP_RIGHT);
textGrid.setAlignment(Pos.TOP_LEFT);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
It is usually better to let the layout panes handle the layout management rather than trying to manage the layout through bindings.
Here is a sample:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import java.util.stream.IntStream;
public class Main extends Application {
private static final int N_BUTTONS = 5;
#Override
public void start(Stage stage) {
VBox buttonLayout = new VBox(
10,
IntStream.range(0, N_BUTTONS)
.mapToObj(this::createButton)
.toArray(Button[]::new)
);
HBox.setHgrow(buttonLayout, Priority.ALWAYS);
TextArea textArea = new TextArea("Test");
textArea.setPrefWidth(100);
textArea.setMaxWidth(TextArea.USE_PREF_SIZE);
textArea.setMinWidth(TextArea.USE_PREF_SIZE);
HBox layout = new HBox(10, textArea, buttonLayout);
layout.setPadding(new Insets(10));
Scene scene = new Scene(layout);
stage.setScene(scene);
stage.show();
}
private Button createButton(int i) {
Button button = new Button("Button " + i);
// button.setMaxWidth(Double.MAX_VALUE);
button.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
VBox.setVgrow(button, Priority.ALWAYS);
return button;
}
public static void main(String[] args) {
launch(args);
}
}
Here are a couple of things I would point out based upon the sample:
As the buttons are so similar, create the buttons in a loop rather than individually in code. I use an IntStream range with a map and a toArray, but you could do the same thing with a standard for loop (which may be easier to understand).
Use combinations of standard layout panes to achieve your layout. For example the buttons are vertically spaced, so put them in a VBox, the text and the buttons are horizontal to each other, so use a HBox.
Use constraints on the layouts to massage them into performing the layout you like, for example, HBox.setHgrow(buttonLayout, Priority.ALWAYS); tells the Box to always assign any extra additional space in the Box to the buttonLayout so that the buttons will fill any remaining area.
Set constraints on the individual nodes to size them how you wish, for example the following code establishes a fixed width for the textArea, which will not vary (you could similar code to establish a fixed height if you wished):
textArea.setPrefWidth(100);
textArea.setMaxWidth(TextArea.USE_PREF_SIZE);
textArea.setMinWidth(TextArea.USE_PREF_SIZE);
Some controls will automatically expand themselves beyond their max size, buttons do not by default, to enable this behavior use the following code (if you only wanted the width to expand and not the height then you would only set the maxWidth rather than the maxSize):
button.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
Rather than defining layouts in code as in this example, instead use a tool such as SceneBuilder to create the scene visually and save the layout as an FXML file, so that the layout is separated from your code (similarly place any styling in an external CSS file).
Related
In JavaFX, is there a way to "autofit" elements on a page so they take up the entire thing?
Currently, I'm trying to make the window have two buttons that together take up the entire canvas, but I am not sure how to do that, given that it is possible to stretch the window, etc. I've tried playing around with Button.setPrefSize, but the button size stays the same, it just shows you a window with two outsized buttons, the text of which is not visible.
What I currently have
What I want (but for any window size)
Here's one way (code here but also possible in Scene Builder and FXML):
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.stage.Stage;
public class TestApplication extends Application {
#Override
public void start(Stage stage) throws Exception {
Button button1 = new Button("Button1");
HBox.setHgrow(button1, Priority.SOMETIMES);
button1.setMaxWidth(Double.MAX_VALUE);
button1.setMaxHeight(Double.MAX_VALUE);
Button button2 = new Button("Button2");
HBox.setHgrow(button2, Priority.SOMETIMES);
button2.setMaxWidth(Double.MAX_VALUE);
button2.setMaxHeight(Double.MAX_VALUE);
HBox hBox = new HBox(button1, button2);
AnchorPane.setLeftAnchor(hBox, 0.0);
AnchorPane.setRightAnchor(hBox, 0.0);
AnchorPane.setTopAnchor(hBox, 0.0);
AnchorPane.setBottomAnchor(hBox, 0.0);
AnchorPane rootContainer = new AnchorPane(hBox);
Scene scene = new Scene(rootContainer, 600, 600);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
I have a splitpane with each containing a anchorpane with a tableview (paneA paneB). By clicking on the button "Show" I want to open a new view depending on the selected side of the split pane.
E.G.
Pane A | Pane B
patient 1 | patient a
patient 2 | patient b
(ShowButon)
What I imagine.
private void showButton(ActionEvent e) {
if (is selected paneA){
get selected row
open view conataining information from selected row paneA
else if (is selected paneB) {
get selected row
open view conaining information from selected row paneB
}
}
For a tab view for example you can easily get the selected tab. Now is something like this possible for a splitpane?
I hope it is now more understandable.
Thanks in advance
I do not know of any way to watch which side of a SplitPane has been clicked on, but you can certainly register a listener on the Node you've placed within each side.
The example below creates a very simple interface with a VBox in each of the two SplitPane sides. We simply listen for a click on either VBox and respond accordingly:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.SplitPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class PaneSelectionExample extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
// Simple interface
VBox root = new VBox(5);
root.setPadding(new Insets(10));
root.setAlignment(Pos.CENTER);
SplitPane splitPane = new SplitPane();
VBox.setVgrow(splitPane, Priority.ALWAYS);
// Two VBoxes with Labels
VBox box1 = new VBox() {{
setAlignment(Pos.TOP_CENTER);
getChildren().addAll(
new Label("One"),
new Label("Two"),
new Label("Three")
);
}};
VBox box2 = new VBox() {{
setAlignment(Pos.TOP_CENTER);
getChildren().addAll(
new Label("One"),
new Label("Two"),
new Label("Three")
);
}};
// Now, we'll add an EventListener to each child pane in the SplitPane to determine which
// has been clicked
box1.setOnMouseClicked(event -> System.out.println("Left Pane clicked!"));
box2.setOnMouseClicked(event -> System.out.println("Right Pane clicked!"));
// Add our VBoxes to the SplitPane
splitPane.getItems().addAll(box1, box2);
root.getChildren().add(splitPane);
// Show the Stage
primaryStage.setWidth(300);
primaryStage.setHeight(300);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
Incoming Opinion Alert
While this may solve your immediate question, you may want to revisit your decision to have only one Show button. Is the user going to expect that and understand which details the Show button will present?
It may be a better idea to have a separate Show button in each pane of the SplitPane; that seems more "standard" to me.
.button
{
-fx-background-image: url("mapDefault.png");
-fx-pref-width: 10px;
-fx-pref-height: 10px;
}
My button looks like this, and theoretically, it should fit the image (10x10) exactly. However
It returns that the button is about 30px in height, despite that I changed it. The button is declared like
Button beachButton = new Button();
and does not have any text values in it.
Any possible causes to this increase/min-cap in height?
Any changes in text need to be taken into account, especially custom font. To accommodate for any picture sizes smaller than the font of the item, set the font size equal to 0px as well.
The offset of the icon(or text) from the buttons border may be the problem. A Button with text in default font type & size cannot get smaller in height than 25px (using setPrefHeight() or setStyle(...)). To make the height the same as other controls the padding could be reduced as in the following example:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class SmallerButtons extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
Label label1= new Label ("Label1");
Label label2= new Label ("Label2");
CheckBox checkBox1= new CheckBox ("CheckBox1");
CheckBox checkBox2= new CheckBox ("CheckBox2");
Button button1= new Button ("Button1");
Button button2= new Button ("Button2");
Button button3= new Button ("Button3");
button3.setOnAction(a-> System.out.println(label1.getHeight() + " / " +checkBox1.getHeight() + " / " +button1.getHeight()));
label1.setStyle("-fx-font-size:10");
button1.setPadding(new Insets(0,3,0,3)); // <----- this one works. Standard value is 5.0.
button1.setMaxWidth(200);
button2.setPrefHeight(17); // no effect
button3.setStyle("-fx-pref-height:35px"); // or also setPrefHeight(35)
VBox vb1=new VBox(label1,label2);
VBox vb2=new VBox(checkBox1,checkBox2);
VBox vb3=new VBox(button1, button2, button3);
vb3.setMaxWidth(80);
vb1.setSpacing(1);
vb2.setSpacing(1);
vb3.setSpacing(1);
HBox hb = new HBox(vb1,vb2,vb3);
hb.setSpacing(15);
Scene scene = new Scene(hb);
stage.setOnCloseRequest((e)->System.exit(0));
stage.setScene(scene);
stage.show();
}
}
One catch: With one buttons padding adjusted all other buttons need the padding explicitly set, too.
I have a HBox and two (or more) Button's in it. And I want all the buttons be of the same width. I can't set width of the buttons in pixels because texts are taken from resource bundle for every language (so length of the text is variable). This is the code I tried, but didn't succeed:
Button but1=new Button("Long text");
Button but2=new Button ("Text");
HBox.setHgrow(but1, Priority.ALWAYS);
HBox.setHgrow(but2, Priority.ALWAYS);
HBox hbox=new HBox();
hbox.getChildren().addAll(but1,but2);
Scene scene=new Scene(hbox, 1000, 600);
stage.setScene(scene);
stage.show();
What is my mistake?
You need to set the maxWidth of the Button to Double.MAX_VALUE and also set HBox.setHgrow() to Priority.ALWAYS to make the Button fill the available width in the HBox.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.stage.Stage;
public class Test extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Button but1 = new Button("Long text");
Button but2 = new Button ("Text");
HBox hbox=new HBox();
hbox.getChildren().addAll(but1,but2);
but1.setMaxWidth(Double.MAX_VALUE);
but2.setMaxWidth(Double.MAX_VALUE);
HBox.setHgrow(but1, Priority.ALWAYS);
HBox.setHgrow(but2, Priority.ALWAYS);
Scene scene=new Scene(hbox, 1000, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
Application.launch();
}
}
Set button maximum width to maximum value:
but1.setMaxWidth(Double.MAX_VALUE);
But it will only resize buttons to fill hbox width. If you need buttons with the same width, you should find the longest button and then set its width to other buttons.
but1.setPrefWidth(width);
I have a custom control which is 3 simple buttons. The control handles operations for the application to which it is tied, but it must reside on an external stage for reasons.
The thing that is annoying about it is that between all buttons is a white background.
I've tried all of the following to eliminate it:
Init StageStyle to Transparent
Declare Scene with a transparent background : new Scene(Node, X, Y, Color.TRANSPARENT);
Declare Scene with a NULL background : new Scene(Node, X, Y, null);
Have the control (which extends an HBox) .setStyle("-fx-background-color : rgba(0, 0, 0, 0);");
All of these have failed to eliminate the appearance of the background.
I've done this with Windows Forms, but is it possible to do with JavaFX?
You need to set the transparency of
the stage
the scene
the root class (or in my case the hbox, i prefer root)
This works for me:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
HBox hbox = new HBox();
hbox.setSpacing(12);
Button button1 = new Button("Button 1");
Button button2 = new Button("Button 2");
Button button3 = new Button("Button 3");
hbox.getChildren().addAll(button1, button2, button3);
Scene scene = new Scene(hbox, Color.TRANSPARENT);
// scene.getStylesheets().add( getClass().getResource("application.css").toExternalForm());
scene.getRoot().setStyle("-fx-background-color: transparent");
primaryStage.initStyle(StageStyle.TRANSPARENT);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
That's the direct solution. Of course instead of setting the style directly it's better practice to use a stylesheet application.css:
.root {
-fx-background-color: transparent;
}
Here's a screenshot with the application being over the code: