I am working through some coursework and am running into an odd issue. I'm working with javafx learning how to build shapes and work with alignment. Anyway my circle object will not respond to setCenterX or setCenterY commands (the radius definition statement does work) in the original definition statements nor in the commands issued by my event handlers which should be redefining these set x and set y values. I cannot figure out why. Please see my code below. When working correctly my code would allow me to move the circle object around the screen with the buttons and event handlers I've created. If I can figure out why the setCenterX and setCenterY don't work, I'm sure I can get the rest. Thanks for your help in advance.
package bravo15;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.scene.Scene;
public class FifteenDotThreeVersionThree extends Application {
#Override // Override the start method in the Application class
public void start(Stage primaryStage) {
Circle circle = new Circle();
circle.setCenterX(300);
circle.setCenterY(300);
circle.setRadius(50);
// Hold four buttons in an HBox
// Define hbox
HBox hBox = new HBox();
hBox.setSpacing(10);
hBox.setAlignment(Pos.CENTER);
// define buttons
Button btLeft = new Button("Left");
Button btRight = new Button("Right");
Button btUp = new Button("Up");
Button btDown = new Button("Down");
// add defined buttons into the hbox
hBox.getChildren().add(btLeft);
hBox.getChildren().add(btRight);
hBox.getChildren().add(btUp);
hBox.getChildren().add(btDown);
// Create and register the handlers for the four buttons
btLeft.setOnAction(e -> circle.setCenterX(circle.getCenterX() - 10));
btRight.setOnAction(e -> circle.setCenterX(circle.getCenterX() + 10));
btUp.setOnAction(e -> circle.setCenterY(circle.getCenterY() + 10));
btDown.setOnAction(e -> circle.setCenterY(circle.getCenterY() - 10));
BorderPane borderPane = new BorderPane();
borderPane.setTop(circle);
borderPane.setBottom(hBox);
BorderPane.setAlignment(hBox, Pos.CENTER);
// Create a scene and place it in the stage
Scene scene = new Scene(borderPane, 200, 200);
primaryStage.setTitle("ControlCircle Version 3"); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
}
/**
* The main method is only needed for the IDE with limited
* JavaFX support. Not needed for running from the command line.
*/
public static void main(String[] args) {
launch(args);
}
}
A BorderPane manages the layout of its components, so it positions the circle for you by setting its layoutX and layoutY properties so that it appears at the top left.
Wrap it in a Pane, which performs no layout, and place the Pane in the top of the border pane:
borderPane.setTop(new Pane(circle));
Note that you have things set up so that it is initially off-screen. You probably want to increase the size of the scene:
Scene scene = new Scene(borderPane, 600, 600);
You can do it this way to shift the circle:
btLeft.setOnAction(e -> circle.setTranslateX(circle.getTranslateX() - 10));
btRight.setOnAction(e -> circle.setTranslateX(circle.getTranslateX() + 10));
btUp.setOnAction(e -> circle.setTranslateY(circle.getTranslateY() - 10));
btDown.setOnAction(e -> circle.setTranslateY(circle.getTranslateY() + 10));
setTranslateX():
Defines the x coordinate of the translation that is added to this
Node's transform. The node's final translation will be computed as
layoutX + translateX, where layoutX establishes the node's stable
position and translateX optionally makes dynamic adjustments to that
position. This variable can be used to alter the location of a node
without disturbing its layoutBounds, which makes it useful for
animating a node's location.
And it looks better with borderPane.setCenter(circle); than borderPane.setTop(circle);.
I have also removed the following lines:
circle.setCenterX(300);
circle.setCenterY(300);
Related
I have a simple FX example with a simple component.
package fxtest;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class App extends Application {
#Override
public void start(Stage stage) {
var bp = new BorderPane();
var r = new Rectangle(0, 0, 200, 200);
r.setFill(Color.GREEN);
var sp = new StackPane(r);
bp.setCenter(sp);
bp.setTop(new XPane());
bp.setBottom(new XPane());
bp.setLeft(new XPane());
bp.setRight(new XPane());
var scene = new Scene(bp, 640, 480);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
package fxtest;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
public class XPane extends Region {
public XPane() {
setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
setMinSize(100, 100);
setPrefSize(100, 100);
widthProperty().addListener((o) -> {
populate();
});
heightProperty().addListener((o) -> {
populate();
});
populate();
}
private void populate() {
ObservableList<Node> children = getChildren();
Rectangle r = new Rectangle(getWidth(), getHeight());
r.setFill(Color.WHITE);
r.setStroke(Color.BLACK);
children.add(r);
Line line = new Line(0, 0, getWidth(), getHeight());
children.add(line);
line = new Line(0, getHeight(), getWidth(), 0);
children.add(line);
}
}
When run, it does what I expect:
When I grow the window, the X's grow.
But when I shrink the window, I get artifacts of the side panels.
I would have thought erasing the backgrounds would have fixed this, but I guess there's some ordering issue. But even still, when you drag the corner, all of the XPanes change size, and they all get repainted, but the artifacts remain.
I tried wrapping the XPanes in to a StackPane, but that didn't do anything (I didn't think it would, but tried it anyway).
How do I remedy this? This is JavaFX 13 on JDK 16 on macOS Big Sur.
Why you get artifacts
I think a different approach should be used rather than fixing the approach you have, but you could fix it if you want.
You are adding new rectangles and lines to your XPane in listeners. Every time the height or width changes, you add a new set of nodes, but the old set of nodes at the old height and widths remains. Eventually, if you resize enough, performance will drop or you will run out of memory or resources, making the program unusable.
A BorderPane paints its children (the center and the XPanes) in the order they were added without clipping, so these old lines will remain and the renderer will paint them over some panes as you resize. Similarly, some panes will paint over some lines because you are building up potentially lots of filled rectangles in the panes and they are partially overlapping lots of lines created.
To fix this, clear() the child node list in your populate() method before you add any new nodes.
private void populate() {
ObservableList<Node> children = getChildren();
children.clear();
// now you can add new nodes...
}
Alternate Solution
Change listeners on widths and heights aren't really the place to add content to a custom region, IMO.
I think that it is best to take advantage of the scene graph and let it handle the repainting and updating of existing nodes after you change the attributes of those nodes, instead of creating new nodes all the time.
Here is an example that subclasses Region and paints fine when a resize occurs.
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
public class XPane extends Region {
public XPane() {
super();
Rectangle border = new Rectangle();
Line topLeftToBottomRight = new Line();
Line bottomLeftToTopRight = new Line();
getChildren().addAll(
border,
topLeftToBottomRight,
bottomLeftToTopRight
);
border.setStroke(Color.BLACK);
border.setFill(Color.WHITE);
border.widthProperty().bind(
widthProperty()
);
border.heightProperty().bind(
heightProperty()
);
topLeftToBottomRight.endXProperty().bind(
widthProperty()
);
topLeftToBottomRight.endYProperty().bind(
heightProperty()
);
bottomLeftToTopRight.startYProperty().bind(
heightProperty()
);
bottomLeftToTopRight.endXProperty().bind(
widthProperty()
);
setMinSize(100, 100);
setPrefSize(100, 100);
}
}
On Region vs Pane
I'm not sure if you should be subclassing Pane or Region, the main difference between the two is that a Pane has a public accessor for a modifiable child list, but a Region does not. So it would depend on what you are trying to do. If it is just drawing X's like the example, then Region is appropriate.
On layoutChildren() vs binding
The Region documentation states:
By default a Region inherits the layout behavior of its superclass,
Parent, which means that it will resize any resizable child nodes to
their preferred size, but will not reposition them. If an application
needs more specific layout behavior, then it should use one of the
Region subclasses: StackPane, HBox, VBox, TilePane, FlowPane,
BorderPane, GridPane, or AnchorPane.
To implement a more custom layout, a Region subclass must override
computePrefWidth, computePrefHeight, and layoutChildren. Note that
layoutChildren is called automatically by the scene graph while
executing a top-down layout pass and it should not be invoked directly
by the region subclass.
Region subclasses which layout their children will position nodes by
setting layoutX/layoutY and do not alter translateX/translateY, which
are reserved for adjustments and animation.
I am not actually doing that here, instead, I am binding in the constructor rather than overriding layoutChildren(). You could implement an alternate solution that operates as the documentation discusses, overriding layoutChildren() rather than using binding, but it is more complicated and less well documented on how to do that.
It is uncommon to subclass Region and override layoutChildren(). Instead, usually, a combination of standard layout Panes will be used and constraints set on the panes and nodes to get the desired layout. This lets the layout engine do a lot of the work such as snapping to pixels, calculating margins and insets, respecting constraints, repositioning content, etc, a lot of which would need to be done manually for a layoutChildren() implementation.
One common approach is to bind the relevant geometric properties to the desired properties of the enclosing container. A related example is examined here, and others are collected here.
The variation below binds the vertices of several Shape instances to the Pane width and height properties. Resize the enclosing stage to see how the BorderPane children conform to entries in the BorderPane Resize Table. The example also adds a red Circle, which stays centered in each child, growing and shrinking in the center to fill the smaller of the width or height. The approach relies on the fluent arithmetic API available to properties that implement NumberExpression or methods defined in Bindings.
c.centerXProperty().bind(widthProperty().divide(2));
c.centerYProperty().bind(heightProperty().divide(2));
NumberBinding diameter = Bindings.min(widthProperty(), heightProperty());
c.radiusProperty().bind(diameter.divide(2));
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.NumberBinding;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
/**
* #see https://stackoverflow.com/q/70311488/230513
*/
public class App extends Application {
#Override
public void start(Stage stage) {
var bp = new BorderPane(new XPane(), new XPane(),
new XPane(), new XPane(), new XPane());
stage.setScene(new Scene(bp, 640, 480));
stage.show();
}
private static class XPane extends Pane {
private final Rectangle r = new Rectangle();
private final Circle c = new Circle(8, Color.RED);
private final Line line1 = new Line();
private final Line line2 = new Line();
public XPane() {
setPrefSize(100, 100);
r.widthProperty().bind(this.widthProperty());
r.heightProperty().bind(this.heightProperty());
r.setFill(Color.WHITE);
r.setStroke(Color.BLACK);
getChildren().add(r);
line1.endXProperty().bind(widthProperty());
line1.endYProperty().bind(heightProperty());
getChildren().add(line1);
line2.startXProperty().bind(widthProperty());
line2.endYProperty().bind(heightProperty());
getChildren().add(line2);
c.centerXProperty().bind(widthProperty().divide(2));
c.centerYProperty().bind(heightProperty().divide(2));
NumberBinding diameter = Bindings.min(widthProperty(), heightProperty());
c.radiusProperty().bind(diameter.divide(2));
getChildren().add(c);
}
}
public static void main(String[] args) {
launch();
}
}
I'm trying to find a solution where a layer can overlap an other layer without pushing it to any direction. Similar like this: https://docs.oracle.com/javase/7/docs/api/javax/swing/JLayeredPane.html but in JavaFX.
It's seems like a very basic feature, but I can't really find a solution.
What I would like to achieve is something like the following:
I would like a root Node, let's say a BorderPane, which in its left there is a (settings) pane and in its center the main content. When the user clicks on a button in the center, the left pane is showing up without pushing the center pane to the right. And that is the problem, because the desired behavior would be to be OVER the centered content not next to it.
toFront and toBack functions at first glance seemed like a possible solution, but it only changes rendering order.
Unfortunately, I don't think the problem can be done with a BorderPane as it can't manage overlapping. But let's hope I'm wrong here. It's not mandatory to achieve this with a BorderPane. It's enough if it works similar that I mentioned in the above section.
Maybe it can be achieved with a SubScene, but I can't really know how.
SubScene documentation: https://docs.oracle.com/javase/8/javafx/api/javafx/scene/SubScene.html
Any help is much appreciated.
Update: Example image
Same as #Nand & #LBald suggestion, I too think a StackPane could be a good choice in this case. Below is a quick demo to show the overlay node with a little fade effect.
import javafx.animation.FadeTransition;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.stream.Stream;
public class OverlayLayout_Demo extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
StackPane root = new StackPane();
Scene scene = new Scene(root, 500, 500);
primaryStage.setScene(scene);
primaryStage.setTitle("Node Overlay Demo");
primaryStage.show();
HBox hBox = new HBox(new Button("One"), new Button("Two"));
hBox.setPadding(new Insets(10));
hBox.setSpacing(10);
StackPane hPane = new StackPane(hBox);
hPane.setMaxHeight(100);
hPane.setVisible(false);
hPane.setStyle("-fx-background-color:#55555550");
VBox vBox = new VBox(new Button("One"), new Button("Two"));
vBox.setPadding(new Insets(10));
vBox.setSpacing(10);
StackPane vPane = new StackPane(vBox);
vPane.setMaxWidth(100);
vPane.setVisible(false);
vPane.setStyle("-fx-background-color:#55555550");
Button left = new Button("Left");
Button top = new Button("Top");
Button right = new Button("Right");
Button bottom = new Button("Bottom");
VBox buttons = new VBox(left, top, right, bottom);
buttons.setStyle("-fx-border-width:2px;-fx-border-color:black;");
buttons.setSpacing(10);
buttons.setAlignment(Pos.CENTER);
StackPane.setMargin(buttons, new Insets(15));
StackPane content = new StackPane(buttons);
content.setOnMouseClicked(e -> {
Node node = vPane.isVisible() ? vPane : hPane;
FadeTransition ft = new FadeTransition(Duration.millis(300), node);
ft.setOnFinished(e1 -> node.setVisible(false));
ft.setFromValue(1.0);
ft.setToValue(0.0);
ft.play();
});
root.getChildren().addAll(content, hPane, vPane);
Stream.of(left, top, right, bottom).forEach(button -> {
button.setOnAction(e -> {
vPane.setVisible(false);
hPane.setVisible(false);
Node node;
switch (button.getText()) {
case "Left":
case "Right":
node = vPane;
StackPane.setAlignment(vPane, button.getText().equals("Left") ? Pos.CENTER_LEFT : Pos.CENTER_RIGHT);
break;
default:
node = hPane;
StackPane.setAlignment(hPane, button.getText().equals("Top") ? Pos.TOP_CENTER : Pos.BOTTOM_CENTER);
}
node.setVisible(true);
FadeTransition ft = new FadeTransition(Duration.millis(300), node);
ft.setFromValue(0.0);
ft.setToValue(1.0);
ft.play();
});
});
}
public static void main(String... args) {
Application.launch(args);
}
}
You could use a simple Pane to take care of the main content and the config overlapping pane, and then adds a listener in the main content that changes the visibility of the config pane.
Pane container = new Pane();
Pane mainContent = ... ;
// you main content pane stuff
Pane config = ... ;
// your config pane stuff
container.getChildren().addAll(mainContent, config); // in this order
mainContent.setOnMouseClicked(e -> config.setVisible( ! config.isVisible()) );
I'm having a problem positioning JavaFX's HBox in a similar manner to Circle.
If using a circle shape it is possible to manually position it such that it is bound to a different node. This is what I've done until now, by having a Pane as the point of reference:
Pane node; //can be dragged around/resized
//...
Circle terminal = new Circle(10);
terminal.setStroke(Color.GREEN);
terminal.setFill(Color.GREEN);
terminal.centerXProperty().bind( node.layoutXProperty() );
terminal.centerYProperty().bind( node.layoutYProperty() );
The pane (node) functions as a graph node and can be dragged around and resized. The circle functions as a port/terminal for edge connections in the graph. Seeing that the node should have more than one the idea is to put the circles into an HBox that is attached/bound to the pane like the circle has until now. This makes it so that manual layout calculations are unnecessary when adding or removing ports, resizing the node, etc. So the code then used was:
Pane node; //can be dragged around/resized
//...
HBox terminalContainer = new HBox();
terminalContainer.layoutXProperty().bind( node.layoutXProperty() );
terminalContainer.layoutYProperty().bind( node.layoutYProperty() );
//... adding circles into HBox as scenegraph children
The only difference is swapping out the HBox for the Circle and using the layoutXProperty() as there is no centerXProperty(). But of course this fails, and the ports appear glued on to the top part of the containing frame, acting strangely. Is there a fix for this? I tried changing the parenting Pane to an anchorPane, this allowed to manually anchor down the HBox in the correct place, but caused issues with the resizing/dragging code.
Minimal example:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Main2 extends Application {
private AnchorPane component;
#Override
public void start(Stage primaryStage) {
component = new AnchorPane();
Scene scene = new Scene(component, 1024, 768);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
//This works, but is hard to maintain
Cell c1 = new Cell();
Cell c2 = new Cell();
Port p1 = new Port(c1);
Port p2 = new Port(c2);
component.getChildren().addAll(c1, c2, p1, p2);
c1.relocate(150, 150);
c2.relocate(550, 550);
//This does not work, even if unbinding circles, but is simpler
HBox pc1 = new HBox();
HBox pc2 = new HBox();
pc1.layoutXProperty().bind( c1.layoutXProperty() );
pc1.layoutYProperty().bind( c1.layoutYProperty() );
pc2.layoutXProperty().bind( c2.layoutXProperty() );
pc2.layoutYProperty().bind( c2.layoutYProperty() );
Port p3 = new Port(c1);
Port p4 = new Port(c2);
pc1.getChildren().add(p3);
pc2.getChildren().add(p4);
component.getChildren().addAll(pc1, pc2);
}
class Cell extends Pane {
public Cell() {
Rectangle view = new Rectangle(50,50);
view.setStroke(Color.DODGERBLUE);
view.setFill(Color.DODGERBLUE);
getChildren().add(view);
}
}
class Port extends Pane {
public Port(Cell owner) {
Circle view = new Circle(10);
view.setStroke(Color.GREEN);
view.setFill(Color.GREEN);
view.centerXProperty().bind( owner.layoutXProperty() );
view.centerYProperty().bind( owner.layoutYProperty() );
getChildren().add(view);
}
}
public static void main(String[] args) {
launch(args);
}
}
Got it to work, was a typo in the code binding the layoutXProperty twice instead of the layoutYProperty facepalm
I need a bunch of widgets, including a TabPane, inside a scrollable and zoomable view, basically this:
ScrollPane[ Group[ widgets .. including TabPane ] ]
The ScrollPane is obviously needed for scrolling, and the Group holds all the widgets and supports zooming.
The initial problem with that approach is that the ScrollPane shows scroll bars based on the original size of the widgets, not based on the actual size.
In the screenshot, note how scrollbars are shown even though the tab pane is much smaller than the viewport, so no scrollbars are needed.
The web site https://pixelduke.wordpress.com/2012/09/16/zooming-inside-a-scrollpane explains how to solve that by adding another nested Group:
ScrollPane[ Group[ Group[ widgets .. including TabPane ] ] ]
The inner Group, as before, holds all the widgets and supports zooming.
The outer Group automatically gets the layout bounds of the zoomed
widgets in the inner group, allowing the ScrollPane to correctly configure the scroll bars.
.. but now the TabPane will fail to properly draw itself.
All you see is the red background of the TabPane:
The complete tab pane only shows up once it's somehow forced to refresh.
The example code toggles the 'side' property of the tab pane when you press 'SPACE'.
Now I have it all: Tab Pane draws OK, inner group can be zoomed, scroll bars appear as soon as the zoomed content no longer fits the viewport. But having to force the Tab Pane refresh is certainly a hack.
Is there a fault in my scene graph?
Is this a bug in the TabPane rendering?
The problem certainly seems limited to the TabPane. When I add other groups, rectangles, buttons, text nodes to the 'widgets' in the inner group, they all render fine. Only the TabPane refuses to show its tabs.
Tried this with both JDK 1.8.0_51 and 1.8.0_73, also tried on Windows, Linux and Mac OS X.
import javafx.application.Application;
import javafx.geometry.Side;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class TabDemo extends Application
{
#Override
public void start(final Stage stage)
{
// TabPane with some tabs
final TabPane tabs = new TabPane();
tabs.setStyle("-fx-background-color: red;");
for (int i=0; i<3; ++i)
{
final Rectangle rect = new Rectangle(i*100, 100, 10+i*100, 20+i*80);
rect.setFill(Color.BLUE);
final Pane content = new Pane(rect);
final Tab tab = new Tab("Tab " + (i+1), content);
tab.setClosable(false);
tabs.getTabs().add(tab);
}
tabs.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
tabs.setPrefSize(400, 300);
final Group widgets = new Group(tabs);
widgets.setScaleX(0.5);
widgets.setScaleY(0.5);
final Group scroll_content = new Group(widgets);
final ScrollPane scroll = new ScrollPane(scroll_content);
final Scene scene = new Scene(scroll);
stage.setTitle("Tab Demo");
stage.setScene(scene);
stage.show();
// Unfortunately, the setup of ScrollPane -> Group -> Group -> TabPane
// breaks the rendering of the TabPane.
// While the red background shows the area occupied by TabPane,
// the actual Tabs are missing..
System.out.println("See anything?");
scene.addEventFilter(KeyEvent.KEY_PRESSED, (KeyEvent event) ->
{
if (event.getCode() == KeyCode.SPACE)
{ // .. until 'side' or 'tabMinWidth' or .. are twiddled to force a refresh
tabs.setSide(Side.BOTTOM);
tabs.setSide(Side.TOP);
System.out.println("See it now?");
}
});
}
public static void main(String[] args)
{
launch(args);
}
}
I am currently working on an assignment where i must print a circle in the center of the primary stage and 4 buttons on the bottom center area of the primary stage which move the circle, up, down, left, and right when clicked. when i run my code, my circle is filled in with the color black. I have set the stroke of the circle to be black but i have not set the circle to be filled black. I know i can just set my circle to be filled white and somewhat solve the problem, but i am wondering if anyone knows why this is happening. Also, i cannot get the Circle and the buttons to print into the same window. I can get either the circle to print by setting the primaryStage to the scene or print the buttons by setting the scene to hBox and then setting the primaryStage to the scene. How should i best change my code so that the buttons and the circle are both displayed?
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.geometry.Pos;
import javafx.event.EventHandler;
import javafx.event.ActionEvent;
public class Btest extends Application {
#Override // Override the start method in the Application class
public void start(Stage primaryStage) {
// Create a border pane
BorderPane pane = new BorderPane();
// create Hbox, set to bottom center
HBox hBox = new HBox();
hBox.setSpacing(10);
hBox.setAlignment(Pos.BOTTOM_CENTER);
Button btLeft = new Button("Left");
Button btDown = new Button("Down");
Button btUp = new Button("Up");
Button btRight = new Button("Right");
hBox.getChildren().addAll(btLeft, btDown, btUp, btRight);
// Lambda's
btLeft.setOnAction((e) -> {
System.out.println("Process Left");
});
btDown.setOnAction((e) -> {
System.out.println("Process Down");
});
btUp.setOnAction(e -> {
System.out.println("Process Up");
});
btRight.setOnAction((e) -> {
System.out.println("Process Right");
});
pane.setCenter(new CenteredCircle("Center"));
// Create a scene and place it in the stage
Scene scene = new Scene(pane, 300, 300);
//set stage and display
primaryStage.setTitle("ShowBorderPane"); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
}
public static void main(String[] args) {
Application.launch(args);
}
}
// create custom class for circle
class CenteredCircle extends StackPane {
public CenteredCircle(String title) {
setPadding(new Insets(11.5, 12.5, 13.5, 14.5));
Circle circle = new Circle();
circle.setStroke(Color.BLACK);
circle.setCenterX(50);
circle.setCenterY(50);
circle.setRadius(50);
getChildren().add(circle);
}
}
"Why is my circle filled Black even though i haven't set it to be filled?"
Because the default color is black. See the doc of Shape.setFill() method:
Defines parameters to fill the interior of an Shape using the settings
of the Paint context. The default value is Color.BLACK for all shapes
except Line, Polyline, and Path. The default value is null for those
shapes.
"... Also, i cannot get the Circle and the buttons to print into the same window."
Put the Hbox to the parent BorderPane, for instance into the bottom:
pane.setBottom( hBox );