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.
Related
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.
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);
}
}
I'm trying to build a javafx trimmer, I've done it, however when I added a setOnMouseClicked on ImageViews the trimmer I made hides these nodes, so the action is not handled.
I've have set Rectangle -50 in order to overlay the trimmer to the ImageView, unfortunately, this make event not working.
Rectangle rectangle = new Rectangle(0, -50, 80, 40);
the flowing controller builds is frame
package app.controller;
import java.io.File;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ScrollPane;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
public class Trimmer2 implements Initializable {
private Node selectedNode;
#FXML
private HBox hboxStream;
#FXML
private HBox hboxStreamTrim;
#FXML
private Label lblTime;
public void initialize(URL arg0, ResourceBundle arg1) {
buidTrimmer();
}
private void buidTrimmer() {
ImageView pic = null;
HBox hbox = new HBox();
DropShadow shadow = new DropShadow();
ClassLoader classLoader = getClass().getClassLoader();
File path = new File(classLoader.getResource("streams/sub_stream").getPath());
File [] files = path.listFiles();
Image[] images = new Image[files.length];
ImageView[] pics = new ImageView[files.length];
for (int i = 0; i < files.length; i++) {
final Image image = images[i] =
new Image(files[i].toURI().toString(), 80, 40, false, false);
pic = pics[i] =
new ImageView(image);
pic.setEffect(shadow);
pic.setOnMouseClicked((MouseEvent me) -> {
System.out.println("do action");
});
hbox.getChildren().add(pics[i]);
}
Rectangle rectangle = new Rectangle(0, -50, 80, 40);
rectangle.setFill(Color.rgb(33, 150, 243, 0.5));
Pane pane = new Pane( rectangle );
makeSelectable(rectangle, pane);
VBox vbox = new VBox();
vbox.setSpacing(10);
vbox.getChildren().addAll(hbox,pane);
ScrollPane scrollPane = new ScrollPane();
scrollPane.setContent(vbox);
scrollPane.setId("my_scrollPane");
hboxStream.getChildren().add(scrollPane);
}
private void makeSelectable(Node node, Pane root) {
node.setOnMouseClicked(event -> {
if (selectedNode != node) {
root.getChildren().removeIf(candidate -> candidate instanceof ResizingControl);
selectedNode = node;
node.toFront();
ResizingControl resizingControl = new ResizingControl(node);
root.getChildren().add(resizingControl);
}
System.out.println("here");
event.consume();
});
}
}
Try to add the Hbox and the rectangle inside your pane, by the way that pane must be empty. hope it helps :)
Rectangle rectangle = new Rectangle(0, 0, imgWidth, imgHeight);
rectangle.setFill(Color.rgb(33, 150, 243, 0.35));
Pane pane = new Pane();
VBox vbox = new VBox();
vbox.getChildren().addAll(pane);
pane.getChildren().addAll(hbox,rectangle);
Mouse events go to the topmost node that is not mouse transparent. Simply set the property to true and the mouse events will be handled at the usual node. One additional thing that can make your life easier is the fact that you can also place nodes inside a layout that are not affected by the layout algorithm of the parent and that do not contribute to the size calculations. Simply set managed to false. This allows you to implement the selection like this:
private Node selectedNode;
#Override
public void start(Stage primaryStage) throws Exception {
HBox container = new HBox();
File directory = new File("someDirectory");
Rectangle selection = new Rectangle(80, 40, Color.rgb(100, 100, 255, 0.5));
selection.setStrokeType(StrokeType.INSIDE);
selection.setStrokeWidth(4);
selection.setStroke(Color.WHITE);
selection.setVisible(false);
selection.setMouseTransparent(true);
selection.setManaged(false);
for (File file : directory.listFiles(f -> f.getName().endsWith(".jpg"))) {
Image image = new Image(file.toURI().toURL().toExternalForm(), 80, 40, false, false);
ImageView imageView = new ImageView(image);
container.getChildren().add(imageView);
imageView.setOnMouseClicked(evt -> {
if (selectedNode == imageView) {
selection.setVisible(false);
selectedNode = null;
} else {
selection.setVisible(true);
selection.setLayoutX(imageView.getLayoutX());
selectedNode = imageView;
}
});
}
container.getChildren().add(selection);
Scene scene = new Scene(new ScrollPane(container), 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
Note: I also removed the use of getResource and that is for a good reason. Resources are not guaranteed to be available as files. E.g. if you use a .jar file, you won't be able to access them via File. Either store outside of the classpath in a directory or make a list of the resources available as text resource to allow you multiple resources without hardcoding every single one of them. (Don't use File with the latter approach.)
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.
I have deployed a JavaFX application in Android using Gluon Plugin. What Im trying to do right now is to show a second Stage, but unfortunately it wont show.
Here's the code of the second Stage:
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Setting {
public Setting(){
Text title = new Text("Settings");
title.setId("setting-title");
title.setFont(Font.font("Arial", (int)(MainApp.HEIGHT * 0.04)));
Label label_url = new Label("URL");
label_url.setFont(Font.font("Arial", (int)(MainApp.HEIGHT * 0.03)));
Label label_style = new Label("Style");
label_style.setFont(Font.font("Arial", (int)(MainApp.HEIGHT * 0.03)));
TextField text_url = new TextField(MainApp.URL);
text_url.setFont(Font.font("Arial", (int)(MainApp.HEIGHT * 0.03)));
ComboBox style_box = new ComboBox();
style_box.setStyle("-fx-font-size:"+(int)(MainApp.HEIGHT * 0.03)+"px;");
ObservableList<String> data = FXCollections.observableArrayList();
data.add("Blue");
data.add("Red");
data.add("Yellow");
style_box.setItems(data);
GridPane grid = new GridPane();
grid.setVgap(5);
grid.setHgap(5);
grid.add(label_url, 0, 0);
grid.add(label_style, 0, 1);
grid.add(text_url, 1, 0);
grid.add(style_box, 1, 1);
VBox root = new VBox();
root.setAlignment(Pos.CENTER);
root.getChildren().addAll(title,grid);
Scene scene = new Scene(root, MainApp.WIDTH * 0.6, MainApp.HEIGHT * 0.4);
if(MainApp.SETTING == null){
MainApp.SETTING = new Stage();
MainApp.SETTING.setScene(scene);
MainApp.SETTING.initOwner(MainApp.MAINSTAGE);
MainApp.SETTING.setTitle("Settings");
}
}
public void show(){
if(!MainApp.SETTING.isShowing()) MainApp.SETTING.show();
}
}
This code works when I tried to run it as a desktop application. Im using Android 4.2.
Using JavaFXPorts you can change scenes and stages while running on Android.
I've modified the Gluon plugin for NetBeans default sample to achieve this, first switching scenes:
private Scene mainScene;
#Override
public void start(Stage stage) {
Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
Label label = new Label("Main Scene");
StackPane root = new StackPane(label);
mainScene = new Scene(root, visualBounds.getWidth(), visualBounds.getHeight());
stage.setScene(mainScene);
stage.show();
label.setOnMouseClicked(e->{
Label labelSettings = new Label("Settings Scene. Click to go back");
StackPane rootSettings = new StackPane(labelSettings);
Scene settingsScene = new Scene(rootSettings, visualBounds.getWidth(), visualBounds.getHeight());
stage.setScene(settingsScene);
labelSettings.setOnMouseClicked(t->stage.setScene(mainScene));
});
}
Now switching stages:
private Scene mainScene;
#Override
public void start(Stage stage) {
Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
Label label = new Label("Main Stage");
StackPane root = new StackPane(label);
root.setStyle("-fx-background-color: aquamarine;");
mainScene = new Scene(root, visualBounds.getWidth(), visualBounds.getHeight());
stage.setScene(mainScene);
stage.setTitle("Main Stage");
stage.show();
label.setOnMouseClicked(e->{
Label labelSettings = new Label("Settings Stage. Click to go back");
StackPane rootSettings = new StackPane(labelSettings);
rootSettings.setStyle("-fx-background-color: burlywood;");
Scene settingsScene = new Scene(rootSettings, visualBounds.getWidth(), visualBounds.getHeight());
Stage stage2 = new Stage();
stage2.setScene(settingsScene);
stage2.show();
labelSettings.setOnMouseClicked(t->stage2.hide());
});
}
And there's another possibility: you can use Gluon Charm, to manage different views.
Have a look at this project to get you started.