Pane -> Hbox -> ImageView fit height - java

I have ImageView inside HBox inside Pane, and want ImageView height fit HBox height when resizing stage. Trying the following code
package sample;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
pane.setBackground(new Background(new BackgroundFill(Color.RED, CornerRadii.EMPTY, Insets.EMPTY)));
HBox hBox = new HBox();
hBox.setBackground(new Background(new BackgroundFill(Color.GREEN, CornerRadii.EMPTY, Insets.EMPTY)));
hBox.setPrefHeight(100);
hBox.setPrefWidth(100);
hBox.prefHeightProperty().bind(pane.heightProperty());
ImageView imageView = new ImageView("http://www.calgary.ca/CA/city-manager/scripts/about-us/webparts/images/ourHistory_retina.jpg");
imageView.fitHeightProperty().bind(hBox.heightProperty());
imageView.setPreserveRatio(true);
hBox.getChildren().add(imageView);
pane.getChildren().add(hBox);
primaryStage.setScene(new Scene(pane, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
When starting, ImageView is not fit windows height, it shown in it's original size. And it scales only up when I resize window to make it bigger, then original image size.
Also I see, that hBox.prefHeightProperty().bind(pane.heightProperty()) works perfectly (height of red HBox background behind image is corresponding window height).
So it seems imageView.fitHeightProperty().bind(hBox.heightProperty()) behaves not as I expecting.
How can I make ImageView fit height of HBox, nested in Pane?

In the code you posted, the HBox is actually getting taller than the root Pane that contains it (though it is getting clipped so you only see the portion in side the root). So the binding on the fitHeight is behaving as you want, but the layout with respect to the HBox's preferred height is not doing what you expect. So you need better control over the layout of the HBox.
The layout pane that allows you the most control is the GridPane. So, while it's possible there may be easier ways to do this, using a GridPane as the root and placing the HBox in the only cell at 0,0 allows you to control how the HBox is resized. The only additional thing you need here is to allow the HBox to shrink indefinitely with setMinHeight(0).
I think the following provides the behavior you want:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.RowConstraints;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
GridPane root = new GridPane();
RowConstraints rc = new RowConstraints();
rc.setVgrow(Priority.ALWAYS);
root.getRowConstraints().add(rc);
root.setBackground(new Background(new BackgroundFill(Color.RED, CornerRadii.EMPTY, Insets.EMPTY)));
HBox hBox = new HBox();
hBox.setBackground(new Background(new BackgroundFill(Color.GREEN, CornerRadii.EMPTY, Insets.EMPTY)));
hBox.setMinHeight(0);
ImageView imageView = new ImageView("http://www.calgary.ca/CA/city-manager/scripts/about-us/webparts/images/ourHistory_retina.jpg");
imageView.fitHeightProperty().bind(hBox.heightProperty());
imageView.setPreserveRatio(true);
hBox.getChildren().add(imageView);
root.add(hBox, 0, 0);
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Thanks to James_D, who have suggested the solution. It's possible even remain Pane as root.
The only line to be added is
hBox.setMinHeight(0);
Also odd
hBox.setPrefHeight(100);
hBox.setPrefWidth(100);
should be removed.

Related

Bind TextArea's font size to current Scene's width using JavaFX

I want a TextArea's font-size to increase or decrease depending on the width property of the Scene it's in. But I don't want the font-size to grow beyond 16px or shrink beyond 10px.
I found this older post that has the code for making TextArea's font-size grow and shrink from being binded to the Scene's width property but I'm not sure how to add the conditional Bindings for the functionality I want.
This is the code from that post which suits my needs:
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class FontBind extends Application {
private DoubleProperty fontSize = new SimpleDoubleProperty(10);
private IntegerProperty blues = new SimpleIntegerProperty(50);
#Override
public void start(Stage primaryStage) {
Button btn = new Button("click me, I change color");
btn.setOnAction((evt)->{blues.set(blues.get()+20);});//max?
Label lbl = new Label("I'm a label");
TextArea ta = new TextArea("Lots of text can be typed\nand even number 1234567890");
HBox hbox = new HBox(new Label("I never change"));
VBox child = new VBox(btn, lbl, ta);
VBox root = new VBox(child, hbox);
Scene scene = new Scene(root, 300, 250);
fontSize.bind(scene.widthProperty().add(scene.heightProperty()).divide(50));
child.styleProperty().bind(Bindings.concat("-fx-font-size: ", fontSize.asString(), ";"
,"-fx-base: rgb(100,100,",blues.asString(),");"));
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Any help would be very much appreciated. I really want to learn more about using Bindings but I'm having trouble understanding how to implement the Bindings methods

Why is the canvas not clearing after applying effect by ColorAdjust?

After applying the color adjust effect and redrawing the canvas , the canvas is not being cleared so that i can update the canvas with some other image, the images are overlapping.
I have a Border Pane containing Scroll Pane to adjust large images
The scroll Pane contains the canvas
A function that clears the canvas then adjusts the brightness and applies the effect on canvas.
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Group;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.ScrollPane;
import javafx.scene.effect.ColorAdjust;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class Main extends Application {
Stage primaryStage;
// load the image
Image image = new Image("https://upload.wikimedia.org/wikipedia/commons/thumb/1/14/Gatto_europeo4.jpg/1024px-Gatto_europeo4.jpg");
// the container for the image as a javafx node
Canvas canvas = new Canvas(600,700);
GraphicsContext gc = canvas.getGraphicsContext2D();
#Override
public void start(Stage primaryStage) throws Exception{
this.primaryStage = primaryStage;
primaryStage.setTitle("Test");
BorderPane root = new BorderPane();
// container for image layers
ScrollPane scrollPane = new ScrollPane();
// use scrollpane for image view in case the image is large
scrollPane.setContent(canvas);
gc.drawImage(image,0,0,canvas.getWidth(),canvas.getHeight());
changeBrightness(canvas,image);// function to add effect on canvas
// put scrollpane in scene
root.setCenter(scrollPane);
primaryStage.setScene(new Scene(root, 780.0, 620.0));
primaryStage.show();
}
private void changeBrightness(Canvas canvas, Image image) {
//clearing canvas before changing brightness working
gc.clearRect(0,0,canvas.getWidth(),canvas.getHeight());
ColorAdjust colorAdjust = new ColorAdjust();
colorAdjust.setBrightness(0.3);
gc.setEffect(colorAdjust);
gc.drawImage(image,0,0,canvas.getWidth(),canvas.getHeight());
//trying to clear canvas after changing brightness
gc.clearRect(0,0,canvas.getWidth(),canvas.getHeight());
gc.drawImage(image,0,0,canvas.getWidth()/2,canvas.getHeight()/2);
}
public static void main(String[] args) {
launch(args);
}
}
You have to remove the effect on your Graphics object before calling gc.clearRect(...)
Here the code:
package com.simple.test;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Group;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.ScrollPane;
import javafx.scene.effect.ColorAdjust;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class Main extends Application {
Stage primaryStage;
// load the image
Image image = new Image("https://upload.wikimedia.org/wikipedia/commons/thumb/1/14/Gatto_europeo4.jpg/1024px-Gatto_europeo4.jpg");
// the container for the image as a javafx node
Canvas canvas = new Canvas(600,700);
GraphicsContext gc = canvas.getGraphicsContext2D();
#Override
public void start(Stage primaryStage) throws Exception{
this.primaryStage = primaryStage;
primaryStage.setTitle("Test");
BorderPane root = new BorderPane();
// container for image layers
ScrollPane scrollPane = new ScrollPane();
// use scrollpane for image view in case the image is large
scrollPane.setContent(canvas);
gc.drawImage(image,0,0,canvas.getWidth(),canvas.getHeight());
changeBrightness(canvas,image);// function to add effect on canvas
// put scrollpane in scene
root.setCenter(scrollPane);
primaryStage.setScene(new Scene(root, 780.0, 620.0));
primaryStage.show();
}
private void changeBrightness(Canvas canvas, Image image) {
//clearing canvas before changing brightness working
gc.clearRect(0,0,canvas.getWidth(),canvas.getHeight());
ColorAdjust colorAdjust = new ColorAdjust();
colorAdjust.setBrightness(0.3);
gc.setEffect(colorAdjust);
gc.drawImage(image,0,0,canvas.getWidth(),canvas.getHeight());
// Remove the effect
gc.setEffect(null);
//trying to clear canvas after changing brightness
gc.clearRect(0,0,canvas.getWidth(),canvas.getHeight());
// Now apply again
gc.setEffect(colorAdjust);
// Redraw
gc.drawImage(image,0,0,canvas.getWidth()/2,canvas.getHeight()/2);
// Remove the effect again
gc.setEffect(null);
}
public static void main(String[] args) {
launch(args);
}
}
You may look at this for further information:
JavaFX GraphicsContext clearRect doesn't work with clip mask
Why won't gc.clearRect clear the canvas?
Btw, cat is cute.

ImageView.getLayoutY() wrong value?

I have a little problem I hope you can help me with:
In this Scene, That blue circle is a 128x128 ImageView, this ImageView is in an HBox, and the HBox is in a VBox, I then set the VBox alignment to Pos.CENTER;
Everything's ok, but when I print the layoutY of the ImageView, it says 0 instead of a 61 (Scene's height is 250, so the layoutY should be 125 - 64);
Does someone have an idea?
Thanks.
The layoutX and layoutY properties determine the layout position of a node within its parent: in this case, the layout position of the image in the HBox. Since there is nothing else in the HBox, the image view will just be at (0,0) in the coordinate system of the HBox, so you will just get 0 for the layoutY property.
(Note also that transforms, such as translations, are applied independently of the layout coordinates - if you like to think of it this way, the node is laid out, then transforms are applied which will alter its final position. So transforms do not modify the layoutX and layoutY properties.)
To get the location of a node in the scene, you can use the localToScene transform to convert a point in the node's own coordinate system to a point in the scene's coordinate system. So to get the location of the top left ((0,0)) of the image view in the scene, you can do
image.localToScene(new Point2D(0, 0))
Here is a complete SSCCE (just using a plain Region to stand in for the image view):
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class BoundsInSceneExample extends Application {
#Override
public void start(Stage primaryStage) {
HBox hbox = new HBox();
Node image = createImage();
hbox.getChildren().add(image);
VBox root = new VBox();
root.setAlignment(Pos.CENTER);
root.getChildren().add(hbox);
Scene scene = new Scene(root, 250, 250);
// force the layout, so layout computations are performed:
root.layout();
System.out.printf("Layout coordinates: [%.1f, %.1f]%n", image.getLayoutX(), image.getLayoutY());
Point2D sceneCoords = image.localToScene(new Point2D(0,0));
System.out.printf("Scene coordinates: [%.1f, %.1f]%n", sceneCoords.getX(), sceneCoords.getY());
primaryStage.setScene(scene);
primaryStage.show();
}
private Node createImage() {
Region region = new Region();
region.setMinSize(128, 128);
region.setPrefSize(128, 128);
region.setMaxSize(128, 128);
region.setBackground(new Background(new BackgroundFill(Color.BLUE, CornerRadii.EMPTY, Insets.EMPTY)));
return region ;
}
public static void main(String[] args) {
launch(args);
}
}
Output:
Layout coordinates: [0.0, 0.0]
Scene coordinates: [0.0, 61.0]

Javafx buttons not functioning properly, and pane not resizing

Currently the code below produces a BorderPane with a GridPane in the center and a HBox on the bottom to hold two buttons. The left-most pane in the GridPane contains the text "Name Here". Right now I only want the buttons to move the text "Name Here" up and down but they will not move the text.
I think it has something to do with the particular GridPane node, but I'm not sure. Additionally, I don't know why the left-most GridPane takes up more space relative to the right-most GridPane within the center of the BorderPane.
Any advice will be greatly appreciated, thank you!
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.geometry.Pos;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.Priority;
import javafx.scene.text.Text;
public class differentWindows extends Application {
protected Text name = new Text("Name Here");
protected BorderPane getPane() {
// HBox to hold the up and down buttons
HBox paneForButtons = new HBox(20);
Button btUp = new Button("Up");
Button btDown = new Button("Down");
paneForButtons.getChildren().addAll(btUp, btDown);
paneForButtons.setAlignment(Pos.BOTTOM_LEFT);
// Grid pane to go in center of the border pane, for the name and video
GridPane paneForTextNVideo = new GridPane();
paneForTextNVideo.setAlignment(Pos.CENTER);
paneForTextNVideo.setGridLinesVisible(true);
paneForTextNVideo.add(name, 0, 0);
Text temp = new Text("temp");
paneForTextNVideo.add(temp, 1, 0);
paneForTextNVideo.setHalignment(temp, HPos.CENTER);
paneForTextNVideo.setValignment(temp, VPos.CENTER);
paneForTextNVideo.setHgrow(temp, Priority.ALWAYS);
paneForTextNVideo.setVgrow(temp, Priority.ALWAYS);
paneForTextNVideo.setHalignment(name, HPos.CENTER);
paneForTextNVideo.setValignment(name, VPos.CENTER);
paneForTextNVideo.setHgrow(name, Priority.ALWAYS);
paneForTextNVideo.setVgrow(name, Priority.ALWAYS);
// Border pane to hold all windows
BorderPane pane = new BorderPane();
pane.setBottom(paneForButtons);
pane.setCenter(paneForTextNVideo);
btUp.setOnAction(e -> name.setY(name.getY() - 10));
btDown.setOnAction(e -> name.setY(name.getY() + 10));
return pane;
} // end of the getPane method
#Override
public void start(Stage primaryStage) {
Scene scene = new Scene(getPane(), 450, 200);
primaryStage.setTitle("Assignment #7");
primaryStage.setScene(scene);
primaryStage.show();
} // end of start method
public static void main(String[] args) {
Application.launch(args);
}
} // end of class
Try using setLayoutY instead of setY:
btUp.setOnAction(e -> name.setLayoutY(name.getLayoutY() - 10));
btDown.setOnAction(e -> name.setLayoutY(name.getLayoutY() + 10));
As a sidenote, the Node parent class also has a relocate method for easily changing both the X and Y coordinates:

adding border title in JavaFX [duplicate]

This above picture is result of my code.But I want like the following.
How can I fix it? The following is my code.I read too many sources but they was too complicated. For example,a source that I read, I think this way is very complicated.Maybe there is a easy way to solve this problem.
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.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class Test extends Application {
public BorderPane border = new BorderPane();
public Label name = new Label("Name");
public Label surname = new Label("Surname");
public TextField name1 = new TextField();
public TextField surname1 = new TextField();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage arg0) throws Exception {
Scene scene = new Scene(new VBox(), 300, 200);
arg0.setTitle("Header");
arg0.setResizable(false);
scene.setFill(Color.OLDLACE);
StackPane grid = addStackPane();
border.setMargin(grid, new Insets(12,12,12,12));
border.setCenter(grid);
((VBox) scene.getRoot()).getChildren().add(border);
arg0.setScene(scene);
arg0.show();
}
#SuppressWarnings("static-access")
public StackPane addStackPane() {
StackPane pane = new StackPane();
GridPane grid = new GridPane();
Label title = new Label("Border Title");
title.setStyle("-fx-translate-y: -7");
pane.setAlignment(title, Pos.TOP_LEFT);
grid.setStyle("-fx-content-display: top");
grid.setStyle("-fx-border-insets: 20 15 15 15");
grid.setStyle("-fx-background-color: white");
grid.setStyle("-fx-border-color: black");
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(25, 10, 25, 10));
grid.add(name, 1, 0);
grid.add(name1, 2, 0);
grid.add(surname, 1, 1);
grid.add(surname1, 2, 1);
pane.getChildren().addAll(grid, title);
return pane;
}
}
Thank you all that reads this topic.
Try to set -fx-background-color of your title label to the same color as the borderPane's background. And make sure you set in a css file, because it's not possible to set multiple styles via setStyle() unless you concatenate them:
myComponent.setStyle("-fx-text-fill: white;"+
"-fx-background-color: black;");
Furthermore it is bad practice to use an InlineStyleSheets as it always has a higher priority than a rule specified in a CSS StyleSheet.
(If you change pane.setAlignment(title, Pos.TOP_LEFT) to StackPane.setAlignment(title, Pos.TOP_LEFT) you can remove the "static-acces" warning.)
User jewelsea has made a control to perform this.
The related stackoverflow question is: How to add border to panel of javafx?
And on gitHub: https://gist.github.com/jewelsea/2838292
I have used it with minimal modification and it works like a charm.

Categories

Resources