Java FX, weird behavior from Label - javafx

I have managed to fix the below issue by using the Text class instead of Label, however I would still like to understand the behavior.
I am trying to create a pane with an image and some text below that image.
I want both the text and the image to be centered inside of the pane.
I also want the text to stick to the bottom of the pane while the image to stick to the top of the pane.
Below is the rough code that achieves what I want. The problem is, If I run the code, the image comes out a little off center. The label also doesn't appear.
If I resize the stage with my mouse, the layout seems to fix itself. The image becomes fully centered and the text appears. Please explain, is this some kind of a bug?
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import static javafx.beans.binding.Bindings.subtract;
public class TestApp extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
AnchorPane pane = new AnchorPane();
BorderPane borderPane = new BorderPane();
ImageView imageView = new ImageView();
Label label = new Label("some text");
AnchorPane.setBottomAnchor(borderPane, 0.0);
AnchorPane.setTopAnchor(borderPane, 0.0);
AnchorPane.setLeftAnchor(borderPane, 0.0);
AnchorPane.setRightAnchor(borderPane, 0.0);
imageView.setPreserveRatio(true);
pane.getChildren().add(borderPane);
borderPane.setTop(imageView);
borderPane.setBottom(label);
BorderPane.setAlignment(imageView, Pos.CENTER);
BorderPane.setAlignment(label, Pos.CENTER);
imageView.fitHeightProperty().bind(subtract(pane.heightProperty(), label.heightProperty()));
imageView.setImage(new Image("file:C:\\pathToFile.jpg"));
primaryStage.setScene(new Scene(pane, 200, 150));
primaryStage.show();
}
}

Related

JavaFX scale ScrollPane content centered

My goal is to have the ScrollPane with some content inside (vbox with some controls) scalable, so that when the scaled content reaches visible bounds, ScrollPane shows horizontal and vertical scroll bars. But there's a catch - in order to do so, I must wrap my content in a Group and by doing so, the scaling is not from the center but rather from the top left corner (0,0) of the ScrollPane, while without wrapping the content of the Group makes the scaling from the center but no scroll bars show. Here is the test application:
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Slider;
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 ZoomScrollExample extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
Pane root = new Pane();
Scene scene = new Scene(root, 450, 450, Color.WHITE);
stage.setScene(scene);
VBox layout = new VBox();
layout.setMinHeight(400);
layout.setMinWidth(400);
Rectangle r = new Rectangle(200, 200);
r.setStroke(Color.RED);
r.setFill(Color.WHITE);
layout.getChildren().add(r);
layout.setAlignment(Pos.CENTER);
ScrollPane sp = new ScrollPane();
//comment this to get no scroll bars but scaling is centered
Group g = new Group(layout);
sp.setContent(g);
//uncomment this
//sp.setContent(layout);
sp.setPrefSize(450, 450);
Slider s = new Slider();
root.getChildren().add(sp);
root.getChildren().add(s);
s.setMin(100);
s.setMax(200);
// actual scaling
layout.scaleXProperty()
.bind(s.valueProperty().divide(100));
layout.scaleYProperty()
.bind(s.valueProperty().divide(100));
stage.show();
}
}
Is there any (easy) way to have both scroll bars and centered scaling?

How to use modal dialog in this case?

I have a question. I need to make a GridPane with a directory choose that will then lead me to a modal dialog showing photos. I cannot figure how to do the modal dialog that also has to be a GridPane or a HBox...so the question is , how do I get to show a Modal Dialog after selecting the Folder and pressing the "Show" Button... Thanks a lot!
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;
public class FotoView extends Application {
#Override
public void start(Stage primaryStage) {
TextField tf = new TextField();
Button b1 = new Button("Search");
Button b2 = new Button("Show");
DirectoryChooser dc = new DirectoryChooser();
GridPane gp = new GridPane();
gp.add(tf, 0 , 0);
gp.add(b1, 1, 0);
gp.add(b2, 0, 1);
b1.setOnAction(e-> dc.showDialog(primaryStage));
primaryStage.setScene(new Scene(gp)) ;
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
} ```
Below is a quick example where a first window has a button that opens up a DirectoryChooser. Once a directory has been selected a second smaller window opens up with the Modality set to APPLICATION_MODAL. In this second window you could add the image(s) that you load and add them to the GridPane.
import java.io.File;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.stage.DirectoryChooser;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(final String[] args) {
launch(args);
}
#Override
public void start(final Stage aStage) throws Exception {
final HBox root = new HBox();
final Button browseBtn = new Button("Click to open a Directory chooser");
root.getChildren().add(browseBtn);
browseBtn.setOnAction(e -> {
final DirectoryChooser chooser = new DirectoryChooser();
final File dir = chooser.showDialog(aStage);
openNewModalStage(aStage, dir);
});
final Scene scene = new Scene(root, 500, 500);
aStage.setScene(scene);
aStage.show();
}
private void openNewModalStage(final Stage aStage, final File aDirectory) {
final Stage stage = new Stage();
final GridPane grid = new GridPane();
final Scene scene = new Scene(grid);
grid.setStyle("-fx-background-color:black");
grid.setPrefWidth(400);
grid.setPrefHeight(400);
// get your images from 'aDirectory' and add them to your grid pane.
stage.setScene(scene);
// set the new windows Modality.
stage.initModality(Modality.APPLICATION_MODAL);
stage.show();
}
}
This way you would only need the one button and the dialog would show as soon as you've selected a directory. However, if you would still want a Search and Show button then just store the directory as a variable and add a listener on the 'show' button and move the openNewModalStage call to that one and remove the second argument.
Edit:
Also, depending on how many images and exactly what you want to display in the modal window, you might want to reconsider the GridPane and use a TilePane, or an hbox/vbox inside of a scroll pane. It's just a thought but I don't know what you will be doing with the GridPane.

Java9 bug in javafx PieChart labels

import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.chart.PieChart;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
public class Main extends Application {
#Override
public void start(Stage stage) {
try {
Scene pie;
Scene begin;
//pie scene
ObservableList<PieChart.Data> pieChartData=FXCollections.observableArrayList();
PieChart pieChart = new PieChart(pieChartData);
Button btBack = new Button("Back");
pieChart.setTitle("Test");
VBox container = new VBox(20);
container.getChildren().addAll(pieChart,btBack);
container.setAlignment(Pos.CENTER);
BorderPane pane = new BorderPane();
pane.setCenter(container);
pie =new Scene(pane,800,600);
//begin scene
VBox container2 = new VBox(20);
Button btPie = new Button("pie");
container2.getChildren().add(btPie);
BorderPane pane2 = new BorderPane();
container2.setAlignment(Pos.CENTER);
pane2.setCenter(container2);
begin=new Scene(pane2,50,50);
//handler
btPie.setOnAction(e->{
pieChartData.clear();
for(int i=0;i<5;++i)
pieChartData.add(new PieChart.Data(""+i, i));
stage.setScene(pie);
});
btBack.setOnAction(e->stage.setScene(begin));
stage.setScene(begin);
stage.show();
} catch (Exception e) {
e.printStackTrace(); // exception handling: print the error message on the console
}
}
public static void main(String[] args) {
launch(args);
}
}
With the above code, it first shows the stage with a button "pie". Clicking the button shows a pie chart with a button "back". The back button is used to go back to the initial screen.
The problem in this code is that after showing the pie chart for the second time, the pie chart's labels suddenly become crammed.
It can be seen with
1. click pie
2. click back
3. click pie -> problem shown
I can see that there is a problem, but I can't really see the reason. Furthermore, this problem only arise in java9; it works well in java8.
Can anyone find me the reason please?
The suggestion by JKostikiadis in the comment:
... you can fix the bug by adding container.layout(); after the for loop and before the stage.setScene(pie); in order to force the VBox to layout its children
helped me.

Using JavaFX: Is It Possible to Put A GridPane in an Alert Dialog Box?

I have an alert box that is populated with text and for formatting reasons I was wondering if I could somehow put a GridPane inside this Alert box so all the text is spaced out correctly.
If it is not possible to do this with GridPane is there some other way of formatting text I could use?
Yes, you can set any node as content of a dialog box.
alert.getDialogPane().setContent(grid);
Here is a sell/buy alert for frozen orange contracts formatted as grid content.
import javafx.application.Application;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class GridAlert extends Application {
#Override
public void start(Stage stage) {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setHeaderText("Frozen Orange Juice Contracts");
GridPane grid = new GridPane();
grid.addRow(0, new Label("Sell"), new Label("142"));
grid.addRow(1, new Label("Buy"), new Label("29"));
grid.setHgap(30);
ColumnConstraints right = new ColumnConstraints();
right.setHalignment(HPos.RIGHT);
grid.getColumnConstraints().setAll(new ColumnConstraints(), right);
alert.getDialogPane().setContent(grid);
Button showAlert = new Button("Show Alert");
showAlert.setOnAction(event -> alert.showAndWait());
HBox layout = new HBox(10);
layout.getChildren().addAll(
showAlert
);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
alert.initOwner(stage);
alert.initModality(Modality.WINDOW_MODAL);
}
public static void main(String[] args) {
launch();
}
}

Set Graphic to label

I want to set Label to graphic. I tested this code:
private static final ImageView livePerformIcon;
static
{
livePerformIcon = new ImageView(MainApp.class.getResource("/images/Flex.jpg").toExternalForm());
}
final Label label = new Label();
label.setStyle("-fx-background-image: url(\"/images/Flex.jpg\");");
livePerformIcon.setFitHeight(20);
livePerformIcon.setFitWidth(20);
label.setGraphic(livePerformIcon);
But I don't see any image.
The only way that I found to make it work is this:
label.setStyle("-fx-background-image: url(\"/images/Flex.jpg\");");
Is there a way to solve this?
Not sure, but AFAIK controls should be created on the JavaFX Application thread, but you're creating ImageView in a static initializer, which I'm not sure if it's executed on the Application thread.
Besides: Do you really want livePerformIcon to be static???
This one made from the data used in the docs, works perfectly for me
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class LabelWithImages extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Scene scene = new Scene(new Group());
stage.setTitle("Label With Image Sample");
stage.setWidth(400);
stage.setHeight(180);
HBox hbox = new HBox();
//Replace the image you want to put up
Image image = new Image(getClass().getResourceAsStream("a.png"));
Label label = new Label("Demo Label");
label.setGraphic(new ImageView(image));
hbox.setSpacing(10);
hbox.getChildren().add((label));
((Group) scene.getRoot()).getChildren().add(hbox);
stage.setScene(scene);
stage.show();
}
}
Code snippets below will set the value of the property graphic of a label. You can use any of the two. I prefer using javafx css, just to implement the model-view-controller design.
// programmatically, provided with image input stream
label.setGraphic(new ImageView(new Image(getClass().getResourceAsStream("path/to/image.png"))));
// javafx css, provided with image url
.label {
-fx-graphic: url("path/to/image.png");
}

Resources