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);
}
}
Related
Is there a way to get the pixel color / alpha transparency of a pixel of a JavaFX Parent or Scene?
For example, how do I get the pixel color of a StackPane at (x,y)?
In Java Swing there is a method, printAll, from which the pixel can be extracted from any component.
However I can't find such a method in JavaFX.
EDIT: #kleopatra asked for a complete reproductive example, so here is it:
package helloworld;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class HelloWorld extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Hello World!");
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Hello World!");
}
});
StackPane root = new StackPane();
root.getChildren().add(btn);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
}
}
How do I get the pixel color of root, say, at x,y?
Here is a function to return the pixel color at any x,y coordinate of a node:
static Color getColor(int x, int y, Node node) {
SnapshotParameters sp = new SnapshotParameters();
sp.setTransform(new Translate(-x, -y));
sp.setViewport(new Rectangle2D(0,0,1,1));
sp.setFill(javafx.scene.paint.Color.TRANSPARENT);
WritableImage image = node.snapshot(sp, null);
return image.getPixelReader().getColor(0, 0);
}
It will also work if Node is replaced by Scene, as both classes share the method snapshot.
This might be a wierd question but is it possible to draw some sort of graphics without a window using javafx?
To clarify I want to write a circle at the bottom left corner of the screen where everything but the circle is the underlying window. So just removing the titlebar is not really enough
Are you looking for a transparent stage - this puts a red circle on the bottom left of the primary monitor. This might help you in the direction you want to go.
public class TransparentStage extends Application {
#Override
public void start(Stage stage) throws Exception {
stage.initStyle(StageStyle.TRANSPARENT);
Circle c = new Circle(30);
c.setFill(Color.RED);
VBox box = new VBox();
box.getChildren().add(c);
final Scene scene = new Scene(box,300, 250);
scene.setFill(null);
stage.setScene(scene);
stage.setX(20);
stage.setY(Screen.getPrimary().getBounds().getHeight() - 100);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Some sort of transparent window like below
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class Main extends Application {
#Override
public void start(Stage stage) {
stage.initStyle(StageStyle.TRANSPARENT);
Text text = new Text("!");
text.setFont(new Font(40));
VBox box = new VBox();
box.getChildren().add(text);
final Scene scene = new Scene(box,300, 250);
scene.setFill(null);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
source : http://www.java2s.com/Code/Java/JavaFX/TRANSPARENTwindow.htm
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);
}
}
I have a project in class where I need to display a traffic light with simply three cirlces. I started with the yellow one, and then attempted to add a red one in some random other place just to see if I could do it, however the yellow one is the only one showing. I can't tell if the red one is somehow underneath the yellow one, but in any case it doesn't make much sense to me as to why the red circle isn't showing.
package tryingGraphicsStuff;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Circle;
import javafx.scene.paint.*;
import javafx.scene.text.*;
import javafx.scene.control.*;
public class TryingGraphicsStuff extends Application{
#Override
public void start(Stage stage) throws Exception {
// create circle
Circle circle = new Circle();
circle.setCenterX(150);
circle.setCenterY(150);
circle.setRadius(50);
circle.setFill(Color.RED);
// place on pane
StackPane p = new StackPane();
p.getChildren().add(circle);
// ensure it stays centered if window resized
//circle.centerXProperty().bind(p.widthProperty().divide(2));
//circle.centerYProperty().bind(p.heightProperty().divide(2));
Circle circleTwo = new Circle();
circleTwo.setCenterX(400);
circleTwo.setCenterY(400);
circleTwo.setRadius(50);
circleTwo.setFill(Color.YELLOW);
// place on pane
p.getChildren().add(circleTwo);
// create scene from pane
Scene scene = new Scene(p, 300, 1000);
// place scene on stage
stage.setTitle("Circle");
stage.setScene(scene);
stage.show();
}
public static void main (String [] args)
{
Application.launch(args);
}
}
A StackPane "lays out its children in a back-to-front stack". (The stack here is in z-coordinates). It is a "layout pane" which actually manages the placement of the child nodes for you. Consequently, the centerX and centerY properties of the circles are ignored, and they appear one on top of the other in the order they are added (so the red one is underneath the yellow one, and the only one you see is the yellow one). By default, the stack pane centers them.
All "layout panes" position the nodes for you. For example, a VBox will position nodes in a vertical stack, with the first one at the top, the second below, and so on. So if you used a VBox instead of a StackPane, the circles would appear one below the other (in the y-direction), but note they would still not respect the centerX and centerY properties.
The Pane class itself does not manage the layout of its child nodes; so if you want to use the coordinates for shape objects, Pane is probably your best option. Group behaves similarly, but takes on the bounds of the union of its child bounds, so it acts like Pane but its local coordinate system is different.
The following demo shows all these options. Again, Pane will be the one that behaves in an intuitive way.
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class CircleLayoutExample extends Application {
#Override
public void start(Stage primaryStage) {
TabPane tabs = new TabPane();
tabs.getTabs().add(createTab(new StackPane()));
tabs.getTabs().add(createTab(new VBox()));
tabs.getTabs().add(createTab(new Pane()));
tabs.getTabs().add(createTab(new Group()));
Scene scene = new Scene(tabs, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private Tab createTab(Pane pane) {
Circle c1 = new Circle(150, 150, 50, Color.RED);
Circle c2 = new Circle(400, 400, 50, Color.YELLOW);
pane.getChildren().addAll(c1, c2);
Tab tab = new Tab(pane.getClass().getSimpleName());
tab.setContent(pane);
return tab ;
}
// annoyingly, Pane and Group do not have a common superclass with a getChildren()
// method, so just reproduce the code...
private Tab createTab(Group pane) {
Circle c1 = new Circle(150, 150, 50, Color.RED);
Circle c2 = new Circle(400, 400, 50, Color.YELLOW);
pane.getChildren().addAll(c1, c2);
Tab tab = new Tab(pane.getClass().getSimpleName());
tab.setContent(pane);
return tab ;
}
public static void main(String[] args) {
launch(args);
}
}
Yeah your both the circles are overlapping.
You can simply use a VBox instead of StackPane. It will solve your issue.
VBox p = new VBox();
As other answers have suggested, using a VBox would help you out the most here, since it will automatically put its children into a vertical row. Here is a brief snippet using an array (so you can make as many circles as you want)
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Circle;
import javafx.scene.paint.*;
public class TryingGraphicsStuff extends Application{
#Override
public void start(Stage stage) throws Exception {
Circle[] circle = new Circle[3]; // create 3 circles
VBox vBox = new VBox(); // vbox will put circles in vertical row
vBox.setAlignment(Pos.CENTER); // center circles
for(int i = 0; i < circle.length; i++){
circle[i] = new Circle(50); // initialize circles with radius of 50
vBox.getChildren().add(circle[i]);
}
circle[0].setFill(Color.RED);
circle[1].setFill(Color.YELLOW);
circle[2].setFill(Color.GREEN);
// add vbox to scene
Scene scene = new Scene(vBox, 300, 800);
stage.setTitle("Circle");
stage.setScene(scene);
stage.show();
}
public static void main (String [] args){
Application.launch(args);
}
}
As always, please understand the code and don't just mindlessly copy and paste. Cheers!
I'm actually a bit confused by the code above. According to your numbers the red one should be the one showing and not the yellow one. Your scene is only 300px wide and you center the yellow circle at 400 which will put it out of view (having a radius of only 50).
Either increase your scene size or move your circle inside your view.
I just started JavaFx this past week and having a problem trying to get a circle setup in a pane to respond to button event handler. I have buttons setup with names left, right, up, down which when pressed should move the circle inside inside the pane. My problem is I can't get the circle to respond to my event handlers at all. I saw another tutorial that incorporated key presses to move the circle and I'm trying something similar but with buttons instead. Any help in getting me in the right direction would be great thanks.
package movingball;
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.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class MovingBall extends Application{
private CirclePane circlePane = new CirclePane();
#Override
public void start(Stage primaryStage) {
StackPane pane = new StackPane();
Circle circle = new Circle(50);
circle.setStroke(Color.BLACK);
circle.setFill(Color.WHITE);
pane.getChildren().add(circle);
HBox hBox = new HBox();
hBox.setSpacing(10);
hBox.setAlignment(Pos.CENTER);
Button btLeft = new Button("Left");
Button btRight = new Button("Right");
Button btUp = new Button("Up");
Button btDown = new Button("Down");
hBox.getChildren().addAll(btLeft, btRight, btUp, btDown);
btLeft.setOnAction(new LeftHandler());
btRight.setOnAction(new RightHandler());
btUp.setOnAction(new UpHandler());
btDown.setOnAction(new DownHandler());
BorderPane borderPane = new BorderPane();
borderPane.setCenter(pane);
borderPane.setBottom(hBox);
BorderPane.setAlignment(hBox, Pos.CENTER);
// Create a scene and place it in the stage
Scene scene = new Scene(borderPane, 500, 350);
primaryStage.setTitle("Move the Ball"); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
}
class LeftHandler implements EventHandler<ActionEvent> {
#Override
public void handle(ActionEvent event) {
circlePane.left();
}
}
class RightHandler implements EventHandler<ActionEvent> {
#Override
public void handle(ActionEvent event) {
circlePane.right();
}
}
class UpHandler implements EventHandler<ActionEvent> {
#Override
public void handle(ActionEvent event) {
circlePane.up();
}
}
class DownHandler implements EventHandler<ActionEvent> {
#Override
public void handle(ActionEvent event) {
circlePane.down();
}
}
class CirclePane extends StackPane {
private Circle circle = new Circle(50);
public CirclePane() {
getChildren().add(circle);
circle.setStroke(Color.BLACK);
circle.setFill(Color.WHITE);
}
public void left() {
circle.setCenterX(circle.getCenterX() - 10);
}
public void right() {
circle.setCenterX(circle.getCenterX() + 10);
}
public void up() {
circle.setCenterY(circle.getCenterY() - 10);
}
public void down() {
circle.setCenterY(circle.getCenterY() + 10);
}
}
public static void main(String[] args) {
launch(args);
}
}
The first issue is that you're moving the CirclePane, but it isn't part of your scene. Remove the pane and circle you create in the start(...) method, and put the circlePane in the center of the BorderPane instead.
The second issue is that a StackPane will center the circle, adjusting its coordinate system to keep it centered as the circle moves. So make CirclePane a subclass of Pane, instead of StackPane. Alternatively, you can call circle.setManaged(false); to prevent the StackPane from positioning the circle.