What I want: I need to generate random 10 circles, with random coordinates and points them in path. Then create a square that needs to move along that path using animation.
What is my problem: I cant create 10 random circles with random coordinates, but I created code for animation.
Here is my code, I created random curved line and square that goes along it. That curved line is just for example, because I don't know how to make circles with random coordinates and points them in path.
Example of my code
final Rectangle rectPath = new Rectangle(0, 0, 40, 40);
rectPath.setArcHeight(10);
rectPath.setArcWidth(10);
rectPath.setFill(Color.ORANGE);
Path path = new Path();
path.getElements().add(new MoveTo(20, 20));
path.getElements().add(new CubicCurveTo(380, 0, 380, 120, 200, 120));
path.getElements().add(new CubicCurveTo(0, 120, 0, 240, 380, 240));
path.getElements().add(new CubicCurveTo(420, 350, 420, 440, 10, 450));
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.millis(4000));
pathTransition.setPath(path);
pathTransition.setNode(rectPath);
pathTransition.setOrientation(PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT);
pathTransition.setCycleCount(5);
pathTransition.setAutoReverse(true);
pathTransition.play();
Group root = new Group();
root.getChildren().add(rectPath);
root.getChildren().add(path);
Scene scene = new Scene(root, 600, 450);
primaryStage.setTitle("Path transition demo");
primaryStage.setScene(scene);
primaryStage.show();
Firstly check my comment regarding the question. It is not something very complex. It is all about learning the concepts and putting them together. Assuming that you might be a beginner, please find the below example for your requirement.
import javafx.animation.PathTransition;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
public class RandomPathTransistionDemo extends Application {
PathTransition pathTransition;
Path path;
SecureRandom random = new SecureRandom();
#Override
public void start(Stage stage) {
VBox root = new VBox();
root.setSpacing(10);
root.setPadding(new Insets(10));
Scene scene = new Scene(root, 600, 600);
stage.setScene(scene);
stage.setTitle("Random Path Transistion");
stage.show();
Pane pane = new Pane();
pane.setPadding(new Insets(10));
pane.setStyle("-fx-border-width:1px;-fx-border-color:black;-fx-background-color:white;");
VBox.setVgrow(pane, Priority.ALWAYS);
Button generateBtn = new Button("Generate circles");
Button animationBtn = new Button("Start Animation");
animationBtn.setDisable(true);
HBox buttons = new HBox(generateBtn, animationBtn);
buttons.setSpacing(15);
root.getChildren().addAll(buttons, new Label("Click generate button as many times as you want !!"),pane);
final Rectangle rectPath = new Rectangle(0, 0, 20, 20);
rectPath.setArcHeight(10);
rectPath.setArcWidth(10);
rectPath.setFill(Color.ORANGE);
path = new Path();
path.setStroke(Color.LIGHTGREEN);
path.setStrokeWidth(2);
generateBtn.setOnAction(e -> {
animationBtn.setDisable(false);
if (pathTransition != null) {
pathTransition.stop();
}
pane.getChildren().clear();
path.getElements().clear();
int width = (int) pane.getWidth() - 20;
int height = (int) pane.getHeight() - 20;
List<Circle> dots = new ArrayList<>();
for (int i = 0; i < 10; i++) {
double x = random.nextInt(width); // Get a random value of x within the pane width
double y = random.nextInt(height);// Get a random value of y within the pane height
// If required include your logic to see if this point is not within the range of other points.
// Create a circle with this random point
Circle dot = new Circle(x, y, 5, Color.RED);
dots.add(dot);
// Also inlcude a path element for this random point.
path.getElements().add(i == 0 ? new MoveTo(x, y) : new LineTo(x, y));
}
// Add all nodes in the pane one after another to have a nice visual.
pane.getChildren().add(path);
pane.getChildren().addAll(dots);
pane.getChildren().add(rectPath);
// Move the rectangle to the start point of the path.
rectPath.setTranslateX(dots.get(0).getCenterX() - 10); // 10 :: half of rectangle width
rectPath.setTranslateY(dots.get(0).getCenterY() - 10); // 10 :: half of rectangle height
});
animationBtn.setOnAction(e -> {
pathTransition = new PathTransition();
pathTransition.setDuration(Duration.millis(4000));
pathTransition.setPath(path);
pathTransition.setNode(rectPath);
pathTransition.setOrientation(PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT);
pathTransition.setAutoReverse(false);
pathTransition.play();
});
}
public static void main(String[] args) {
launch(args);
}
}
Related
I'm awfully new to Javafx and want to try and make a very simple game. I'm trying to plot colors in a circle. The circle has different colors and must be hollow on the inside. I'll show a picture here:
I know I could just print out the image but I want to find out when the player collides with the incorrect color(exactly like how it is done in color switch) so that they lose in that case which I'm assuming wont be possible if i used the picture. How do i go about this?
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Arc;
import javafx.stage.Stage;
public class FourArcs extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Arc arc = new Arc();
arc.setCenterX(100.0f);
arc.setCenterY(100.0f);
arc.setRadiusX(80.0f);
arc.setRadiusY(80.0f);
arc.setStartAngle(0.0f);
arc.setLength(90.0f);
arc.setStroke(Color.BLUE);
arc.setStrokeWidth(10);
arc.setFill(Color.TRANSPARENT);
Arc arc2 = new Arc(100, 100, 80, 80, 90, 90);
arc2.setStroke(Color.PURPLE);
arc2.setStrokeWidth(10);
arc2.setFill(Color.TRANSPARENT);
Arc arc3 = new Arc(100, 100, 80, 80, 180, 90);
arc3.setStroke(Color.YELLOW);
arc3.setStrokeWidth(10);
arc3.setFill(Color.TRANSPARENT);
Arc arc4 = new Arc(100, 100, 80, 80, 270, 90);
arc4.setStroke(Color.RED);
arc4.setStrokeWidth(10);
arc4.setFill(Color.TRANSPARENT);
Group root = new Group(arc, arc2, arc3, arc4);
Scene scene = new Scene(root);
scene.setFill(Color.BLACK);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The above code is based on this question and answer.
Screen capture:
Is possible to switch scene with a slide effect?
i want that when i call the setScene on the stage instance
it change the scene with a slide effect. it's possible?
public class ManyScenes extends Application {
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(final Stage primaryStage) {
primaryStage.setTitle("Slide");
Group root1 = new Group();
Group root2 = new Group();
Scene scene1 = new Scene(root1, 300, 250);
Scene scene2 = new Scene(root2, 300, 250);
Rectangle rectangle2 = new Rectangle(300, 250);
rectangle2.setFill(Color.BLUE);
root2.getChildren().add(rectangle2);
Rectangle rectangle1 = new Rectangle(300, 250);
rectangle1.setFill(Color.RED);
root1.getChildren().add(rectangle1);
primaryStage.setScene(scene1);
primaryStage.show();
// Here i need a slide effect,
// this method is called when a button is pressed.
primaryStage.setScene(scene2);
}
}
You can't apply a transition between two scenes, as it is not possible to have both at the same time on one stage. One solution would be having just one scene and manage all the transitions inside, as in #James_D answer.
But you could also simulate a transition between two scenes. For this you can use two snapshots of both, perform a transition between those and at the end just set the new scene.
This is a very basic working case, where you can go backwards and forward all over again just clicking in the new scene:
#Override
public void start(Stage primaryStage) {
Group root1 = new Group();
Group root2 = new Group();
Scene scene1 = new Scene(root1, 300, 250);
Scene scene2 = new Scene(root2, 300, 250);
Rectangle rectangle2 = new Rectangle(300, 250);
rectangle2.setFill(Color.BLUE);
root2.getChildren().add(rectangle2);
Rectangle rectangle1 = new Rectangle(300, 250);
rectangle1.setFill(Color.RED);
root1.getChildren().add(rectangle1);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene1);
primaryStage.show();
rectangle1.setOnMouseClicked(e->{
// Create snapshots with the last state of the scenes
WritableImage wi = new WritableImage(300, 250);
Image img1 = root1.snapshot(new SnapshotParameters(),wi);
ImageView imgView1= new ImageView(img1);
wi = new WritableImage(300, 250);
Image img2 = root2.snapshot(new SnapshotParameters(),wi);
ImageView imgView2= new ImageView(img2);
// Create new pane with both images
imgView1.setTranslateX(0);
imgView2.setTranslateX(300);
StackPane pane= new StackPane(imgView1,imgView2);
pane.setPrefSize(300,250);
// Replace root1 with new pane
root1.getChildren().setAll(pane);
// create transtition
Timeline timeline = new Timeline();
KeyValue kv = new KeyValue(imgView2.translateXProperty(), 0, Interpolator.EASE_BOTH);
KeyFrame kf = new KeyFrame(Duration.seconds(1), kv);
timeline.getKeyFrames().add(kf);
timeline.setOnFinished(t->{
// remove pane and restore scene 1
root1.getChildren().setAll(rectangle1);
// set scene 2
primaryStage.setScene(scene2);
});
timeline.play();
});
rectangle2.setOnMouseClicked(e->{
// Create snapshots with the last state of the scenes
WritableImage wi = new WritableImage(300, 250);
Image img1 = root1.snapshot(new SnapshotParameters(),wi);
ImageView imgView1= new ImageView(img1);
wi = new WritableImage(300, 250);
Image img2 = root2.snapshot(new SnapshotParameters(),wi);
ImageView imgView2= new ImageView(img2);
// Create new pane with both images
imgView2.setTranslateX(0);
imgView1.setTranslateX(300);
StackPane pane= new StackPane(imgView2,imgView1);
pane.setPrefSize(300,250);
// Replace root2 with new pane
root2.getChildren().setAll(pane);
// create transtition
Timeline timeline = new Timeline();
KeyValue kv = new KeyValue(imgView1.translateXProperty(), 0, Interpolator.EASE_BOTH);
KeyFrame kf = new KeyFrame(Duration.seconds(1), kv);
timeline.getKeyFrames().add(kf);
timeline.setOnFinished(t->{
// remove pane and restore scene 2
root2.getChildren().setAll(rectangle2);
// set scene 1
primaryStage.setScene(scene1);
});
timeline.play();
});
}
For more complex effects have a look at this.
A Stage can contain one and only one Scene, and each Scene has one and only one root. So you need to manage the transition inside the root of a single scene.
Simple example:
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class SlidingViews extends Application {
#Override
public void start(Stage primaryStage) {
Rectangle rectangle1 = new Rectangle(300, 250);
rectangle1.setFill(Color.RED);
Button nextView = new Button("Next");
nextView.setPadding(new Insets(10));
BorderPane view1 = new BorderPane(rectangle1, null, null, nextView, null);
BorderPane.setAlignment(nextView, Pos.CENTER);
Group view2 = new Group();
Rectangle rectangle2 = new Rectangle(300, 250);
rectangle2.setFill(Color.BLUE);
view2.getChildren().add(rectangle2);
StackPane root = new StackPane(view1);
nextView.setOnAction(event -> {
root.getChildren().add(view2);
double width = root.getWidth();
KeyFrame start = new KeyFrame(Duration.ZERO,
new KeyValue(view2.translateXProperty(), width),
new KeyValue(view1.translateXProperty(), 0));
KeyFrame end = new KeyFrame(Duration.seconds(1),
new KeyValue(view2.translateXProperty(), 0),
new KeyValue(view1.translateXProperty(), -width));
Timeline slide = new Timeline(start, end);
slide.setOnFinished(e -> root.getChildren().remove(view1));
slide.play();
});
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
AFAIK it's not possible. Rather than sliding scenes, try creating different layouts inside one scene and slide between them.
I would recommend having a look at the Pagination control.
It switches its content panes with an animation. You can either customize this one to fit your needs or have a look at its skin implementation to get an idea on how to do the animation.
I'm trying to get a car to animate along a curved path. The PathTransition.OrientationType only seems to give the option to keep the node perpendicular to the path instead of parallel.
Is there a way to make this parallel?
Here is a bit of what I have so far:
VBox car = new VBox();
Line track1 = new Line(242, 10, 242, 200);
Line track2 = new Line(258, 10, 258, 200);
Line track3 = new Line(242, 600, 242, 800);
Line track4 = new Line(258, 600, 258, 800);
CubicCurveTo curvePath1 = new CubicCurveTo();
curvePath1.setControlX1(400.0f);
curvePath1.setControlY1(300.0f);
curvePath1.setControlX2(400.0f);
curvePath1.setControlY2(500.0f);
curvePath1.setX(250.0f);
curvePath1.setY(600.0f);
VBox station1 = new VBox();
LoadingPosition stationUp = new LoadingPosition();
LoadingPosition stationDown = new LoadingPosition();
station1.getChildren().addAll(stationUp, stationDown);
station1.setLayoutX(170);
station1.setLayoutY(27);
VBox station2 = new VBox();
LoadingPosition station2Up = new LoadingPosition();
LoadingPosition station2Down = new LoadingPosition();
station2.getChildren().addAll(station2Up, station2Down);
station2.setLayoutX(170);
station2.setLayoutY(712);
//Setting up the path
Path path = new Path();
path.getElements().add(new MoveTo(250f, 70f));
path.getElements().add(new LineTo(250f, 200f));
path.getElements().add(curvePath1);
path.getElements().add(new LineTo(250f, 712f));
//Instantiating PathTransition class
PathTransition pathTransition = new PathTransition();
//Setting duration for the PathTransition
pathTransition.setDuration(Duration.millis(1000));
//Setting Node on which the path transition will be applied
pathTransition.setNode(car);
//setting path for the path transition
pathTransition.setPath(path);
//setting up the cycle count
pathTransition.setCycleCount(10);
//setting auto reverse to be true
pathTransition.setAutoReverse(true);
pathTransition.setOrientation(PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT);
//Playing path transition
pathTransition.play();
//Applying parallel Translation to the circle
ParallelTransition parallelTransition = new ParallelTransition(
car, pathTransition);
//Playing the animation
parallelTransition.play();
//Configuring group and scene
Group root = new Group();
root.getChildren().addAll(station1, station2, track1, track2, track3, track4, curveTrack1, curveTrack2, car, path);
Scene scene = new Scene(root, 1200, 900, Color.LIGHTGRAY);
primaryStage.setScene(scene);
primaryStage.setTitle("Path Transition Example");
primaryStage.show();
}
Orthogonal to path instead of parallel
Altered code from here
imp
ort javafx.animation.PathTransition;
import javafx.animation.PathTransition.OrientationType;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.paint.Color;
import javafx.scene.shape.CubicCurveTo;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.stage.Stage;
import javafx.util.Duration;
public class JavaFXApplication extends Application
{
public static void main(String[] args)
{
Application.launch(args);
}
#Override
public void start(Stage primaryStage)
{
primaryStage.setTitle("PathTransition");
Group root = new Group();
Scene scene = new Scene(root, 800, 300, Color.GREY);
//ImageView car = new ImageView(new Image("http://hajsoftutorial.com/im/smallcar.png"));
Image image = new Image(getClass().getResourceAsStream("car.png"));
ImageView car = new ImageView(image);
car.setFitHeight(40);
car.setPreserveRatio(true);
Path road = new Path();
road.setStrokeWidth(30);
MoveTo moveTo = new MoveTo();
moveTo.setX(150);
moveTo.setY(30);
LineTo line1 = new LineTo();
line1.setX(650);
line1.setY(30);
CubicCurveTo cubicTo = new CubicCurveTo();
cubicTo.setControlX1(800);
cubicTo.setControlY1(30);
cubicTo.setControlX2(800);
cubicTo.setControlY2(270);
cubicTo.setX(650);
cubicTo.setY(270);
LineTo line2 = new LineTo();
line2.setX(150);
line2.setY(270);
CubicCurveTo cubicTo2 = new CubicCurveTo();
cubicTo2.setControlX1(0);
cubicTo2.setControlY1(270);
cubicTo2.setControlX2(0);
cubicTo2.setControlY2(30);
cubicTo2.setX(150);
cubicTo2.setY(30);
road.getElements().addAll(moveTo, line1, cubicTo, line2, cubicTo2);
PathTransition pathTransition = new PathTransition();
pathTransition.setDuration(Duration.millis(10000));
pathTransition.setNode(car);
pathTransition.setPath(road);
pathTransition.setOrientation(OrientationType.ORTHOGONAL_TO_TANGENT);
pathTransition.setCycleCount(Timeline.INDEFINITE);
pathTransition.play();
root.getChildren().addAll(road, car);
primaryStage.setScene(scene);
primaryStage.show();
}
}
I was able to get it working by trying other ways to rotate the car, thanks to Fabian for suggesting.
Sedrick's example also helped narrow down where to focus.
Here's what I added:
car.getTransforms().add(new Rotate(270,totalCarHeight/2,totalCarWidth));
The pivot points were a little unusual, but this made it perfectly centered on the path.
Previously, I tried:
car.setRotate(270);
Which did nothing, leading me to drift away from that idea.
I'm wondering if it's possible to selectively apply parent transforms to a child node within javafx.
To my understanding when a node is tranformed all the children of said node are also transformed.
My question is: Is it possible to disable types of parent transforms within the child so that when the parent is tranformed only a select type of transforms are passed.
Similar to this:
I would like to rotate the group node without rotating the text but I would like the text position to be effected by the rotation.
Hopefully i've explained myself.
Currently I simply apply the opposite rotation to the text when the group is transformed but it's getting to the point where expanding my scene graph is making this more complicated.
In short, no: the transforms of the group are always propagated to all its children.
However, I think here you just want the rotate property of the text to be the negative of the rotate property of the group:
text.rotateProperty().bind(group.rotateProperty().multiply(-1));
Since the rotateProperty defines a rotation about the center of the node, when the group is rotated, it (and all its child nodes) will be rotated about its center, then the binding ensures the text is rotated about its center in the opposite direction.
If you want something more general, it gets a bit tricky. One possible approach is to observe the localToSceneTransform of the parent of the text you want to keep horizontal. That property represents the cumulative transform that maps coordinates in the parent to coordinates in the scene. Then you can take a horizontal line in the local coordinate space in the parent, transform it to the scene, and look at the angle it creates. You then want to rotate the text by the negative of this angle.
Here's a demo:
import java.util.function.Function;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.geometry.Point2D;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Spinner;
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.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class RotateGroupKeepTextAligned extends Application {
#Override
public void start(Stage primaryStage) {
BorderPane root = new BorderPane();
StackPane container = new StackPane();
Group outer = new Group();
Rectangle outerRect = new Rectangle(0, 0, 400, 400);
outerRect.setFill(Color.ANTIQUEWHITE);
outer.getChildren().add(outerRect);
Text outerText = new Text(5, 5, "Outer Group");
Group inner = new Group();
inner.relocate(10, 25);
Text innerText = new Text(5, 5, "Inner Group");
Rectangle rect = new Rectangle(50, 50, 150, 150);
rect.setFill(Color.CORNFLOWERBLUE);
Text fixedAlignmentText = new Text(100, 220, "Horizontal Text");
inner.localToSceneTransformProperty().addListener((obs, oldT, newT) -> {
// figure overall rotation angle of inner:
Point2D leftScene = newT.transform(new Point2D(0, 0));
Point2D rightScene = newT.transform(new Point2D(1, 0));
double angle = Math.toDegrees(Math.atan2(rightScene.getY() - leftScene.getY(), rightScene.getX() - leftScene.getX()));
fixedAlignmentText.setRotate(-angle);
});
outer.setStyle("-fx-background-color: antiquewhite;");
outer.getChildren().addAll(outerText, inner);
inner.setStyle("-fx-background-color: white;");
inner.getChildren().addAll(innerText, rect, fixedAlignmentText);
container.getChildren().add(outer);
root.setCenter(container);
VBox controls = new VBox(5,
makeControls("Outer Group", outer),
makeControls("Inner Group", inner));
root.setBottom(controls);
Scene scene = new Scene(root, 600, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private Node makeControls(String title, Node node) {
GridPane grid = new GridPane();
Label label = new Label(title);
Spinner<Double> rotateSpinner = new Spinner<>(0, 360, 0, 5);
node.rotateProperty().bind(rotateSpinner.getValueFactory().valueProperty());
rotateSpinner.getValueFactory().setWrapAround(true);
Button up = createTranslateButton(node, Node::translateYProperty, -2, "^");
Button down = createTranslateButton(node, Node::translateYProperty, 2, "v");
Button left = createTranslateButton(node, Node::translateXProperty, -2, "<");
Button right = createTranslateButton(node, Node::translateXProperty, 2, ">");
grid.add(label, 0, 0, 3, 1);
grid.add(new Label("Rotation:"), 0, 1, 1 ,2);
grid.add(rotateSpinner, 1, 1, 1, 2);
grid.add(up, 3, 1);
grid.add(down, 3, 2);
grid.add(left, 2, 1, 1, 2);
grid.add(right, 4, 1, 1, 2);
grid.setHgap(5);
grid.setVgap(5);
return grid ;
}
private Button createTranslateButton(Node node, Function<Node, DoubleProperty> property, double delta, String text) {
Button button = new Button(text);
button.setOnAction(e -> {
DoubleProperty prop = property.apply(node);
prop.set(prop.get() + delta);
});
return button ;
}
public static void main(String[] args) {
launch(args);
}
}
Here is an example from Pro JavaFx 8:
package projavafx.reversi.examples;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.effect.DropShadow;
import javafx.scene.effect.InnerShadow;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.RadialGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Ellipse;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import projavafx.reversi.model.Owner;
import projavafx.reversi.model.ReversiModel;
/**
* #author Stephen Chin <steveonjava#gmail.com>
*/
public class BorderLayoutExample extends Application {
TilePane scoreTiles;
TilePane titleTiles;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
BorderPane borderPane = new BorderPane();
borderPane.setTop(createTitle());
borderPane.setCenter(createBackground());
borderPane.setBottom(createScoreBoxes());
Scene scene = new Scene(borderPane, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
// scoreTiles.prefTileWidthProperty().bind(Bindings.selectDouble(scoreTiles.parentProperty(), "width").divide(2));
// titleTiles.prefTileWidthProperty().bind(Bindings.selectDouble(titleTiles.parentProperty(), "width").divide(2));
}
private Node createTitle() {
StackPane left = new StackPane();
left.setStyle("-fx-background-color: black");
Text text = new Text("JavaFX");
text.setFont(Font.font(null, FontWeight.BOLD, 18));
text.setFill(Color.WHITE);
StackPane.setAlignment(text, Pos.CENTER_RIGHT);
left.getChildren().add(text);
Text right = new Text("Reversi");
right.setFont(Font.font(null, FontWeight.BOLD, 18));
titleTiles = new TilePane();
titleTiles.setSnapToPixel(false);
TilePane.setAlignment(right, Pos.CENTER_LEFT);
titleTiles.getChildren().addAll(left, right);
titleTiles.setPrefTileHeight(40);
titleTiles.prefTileWidthProperty().bind(Bindings.selectDouble(titleTiles.parentProperty(), "width").divide(2));
return titleTiles;
}
private Node createBackground() {
Region answer = new Region();
RadialGradient rg = new RadialGradient(225, 0, 0, 0, 1, true, CycleMethod.NO_CYCLE,
new Stop(0.0, Color.WHITE),
new Stop(1.0, Color.GRAY)
);
answer.setBackground(new Background(new BackgroundFill(rg, null, null)));
// answer.setStyle("-fx-background-color: radial-gradient(radius 100%, white, gray)");
return answer;
}
private Node createScoreBoxes() {
scoreTiles = new TilePane(createScore(Owner.BLACK), createScore(Owner.WHITE));
scoreTiles.setSnapToPixel(false);
scoreTiles.setPrefColumns(2);
scoreTiles.prefTileWidthProperty().bind(Bindings.selectDouble(scoreTiles.parentProperty(), "width").divide(2));
return scoreTiles;
}
private Node createScore(Owner owner) {
Region background;
Ellipse piece = new Ellipse(32, 20);
piece.setFill(owner.getColor());
DropShadow pieceEffect = new DropShadow();
pieceEffect.setColor(Color.DODGERBLUE);
pieceEffect.setSpread(.2);
piece.setEffect(pieceEffect);
Text score = new Text();
score.setFont(Font.font(null, FontWeight.BOLD, 100));
score.setFill(owner.getColor());
Text remaining = new Text();
remaining.setFont(Font.font(null, FontWeight.BOLD, 12));
remaining.setFill(owner.getColor());
VBox remainingBox = new VBox(10, piece, remaining);
remainingBox.setAlignment(Pos.CENTER);
FlowPane flowPane = new FlowPane(20, 10, score, remainingBox);
flowPane.setAlignment(Pos.CENTER);
background = new Region();
background.setStyle("-fx-background-color: " + owner.opposite().getColorStyle());
ReversiModel model = ReversiModel.getInstance();
StackPane stack = new StackPane(background, flowPane);
InnerShadow innerShadow = new InnerShadow();
innerShadow.setColor(Color.DODGERBLUE);
innerShadow.setChoke(.5);
background.effectProperty().bind(Bindings.when(model.turn.isEqualTo(owner))
.then(innerShadow)
.otherwise((InnerShadow) null));
DropShadow dropShadow = new DropShadow();
dropShadow.setColor(Color.DODGERBLUE);
dropShadow.setSpread(.2);
piece.effectProperty().bind(Bindings.when(model.turn.isEqualTo(owner))
.then(dropShadow)
.otherwise((DropShadow) null));
score.textProperty().bind(model.getScore(owner).asString());
remaining.textProperty().bind(model.getTurnsRemaining(owner).asString().concat(" turns remaining"));
return stack;
}
}
A warning pops up in the console when running this app:
sept. 20, 2015 11:07:03 AM com.sun.javafx.binding.SelectBinding$SelectBindingHelper getObservableValue
WARNING: Exception while evaluating select-binding [width]
sept. 20, 2015 11:07:03 AM com.sun.javafx.binding.SelectBinding$SelectBindingHelper getObservableValue
WARNING: Exception while evaluating select-binding [width]
What went wrong here?
The problem is the following bit of code in the method createTitle():
titleTiles.prefTileWidthProperty().bind(
Bindings.selectDouble(
titleTiles.parentProperty(), "width").divide(2));
At this moment, the titleTiles have not yet been added to the borderPane, so the value of the parentProperty is null, hence the width property can not be found on it.
Same in createScoreBoxes().
Next time, though, it would be nice, if you cut down your sample code a bit, especially remove references to classes from your project (import projavafx.reversi.model.ReversiModel;), do that one can paste it into his IDE and run it right away.