How can I draw shapes with delay?
For example at the beginning, draw few rectangles, after 1 second draw fillOval, after 3 seconds draw another oval and so on.
You are describing an animation. For that, you should use the Animation API.
E.g., using a Timeline:
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Ellipse;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class ShapeAnimationExample extends Application {
private final Random rng = new Random();
#Override
public void start(Stage primaryStage) {
List<Rectangle> fewRectangles = new ArrayList<>();
for (int i = 0 ; i < 5 ; i++) {
Rectangle r = new Rectangle(rng.nextInt(300)+50, rng.nextInt(300)+50, rng.nextInt(100)+50, rng.nextInt(100)+50);
r.setFill(randomColor());
fewRectangles.add(r);
}
List<Ellipse> ovals = new ArrayList<>();
for (int i = 0 ; i < 5 ; i++) {
Ellipse e = new Ellipse(rng.nextInt(400)+50, rng.nextInt(400)+50, rng.nextInt(50)+50, rng.nextInt(50)+50);
e.setFill(randomColor());
ovals.add(e);
}
Pane pane = new Pane();
pane.setMinSize(600, 600);
Timeline timeline = new Timeline();
Duration timepoint = Duration.ZERO ;
Duration pause = Duration.seconds(1);
KeyFrame initial = new KeyFrame(timepoint, e -> pane.getChildren().addAll(fewRectangles));
timeline.getKeyFrames().add(initial);
for (Ellipse oval : ovals) {
timepoint = timepoint.add(pause);
KeyFrame keyFrame = new KeyFrame(timepoint, e -> pane.getChildren().add(oval));
timeline.getKeyFrames().add(keyFrame);
}
Scene scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.show();
timeline.play();
}
private Color randomColor() {
return new Color(rng.nextDouble(), rng.nextDouble(), rng.nextDouble(), 1.0);
}
public static void main(String[] args) {
launch(args);
}
}
Related
i was searching for answer and trying so many options. Finally i found way to pass my own javaxf object to GridPane. But I still think there is a better way to this than I am doing.
So here is my code:
Main:
package sample;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.layout.*;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import static java.lang.String.valueOf;
public class Main extends Application {
private My_Rectangle selectedShelf;
Stage window;
GridPane grid;
#Override
public void start(Stage primaryStage) throws Exception {
window = primaryStage;
BackgroundFill background_fill = new BackgroundFill(Color.PINK,
CornerRadii.EMPTY, Insets.EMPTY);
Background background = new Background(background_fill);
grid = new GridPane();
grid.setBackground(background);
grid.setPadding(new Insets(10,10,10,10));
grid.setVgap(10);
grid.setHgap(10);
grid.setAlignment(Pos.CENTER);
for (int i = 0; i<10;i++){
for(int y = 0; y<10;y++){
My_Rectangle rect = new My_Rectangle(grid,i,y,new Text( valueOf(i+y)) );
}
}
Scene scene = new Scene(grid, 400, 400);
window.setScene(scene);
window.show();
}
public static void main(String[] args) {
launch(args);
}
}
package sample;
import javafx.event.EventHandler;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
public class My_Rectangle {
private float wide;
private float height;
private final Rectangle rect;
private boolean selected;
public My_Rectangle( GridPane cheff, int x, int y, Text text) {
wide = 20;
height = 30;
selected = false;
rect = new Rectangle(wide,height);
rect.setFill(Color.GREEN);
text.setTextAlignment(TextAlignment.CENTER);
EventHandler<MouseEvent> clickedHandler = ev -> {
selected = !selected;
this.selected();
ev.consume();
};
rect.setOnMouseClicked(clickedHandler);
cheff.add(rect, x ,y );
cheff.add(text, x ,y );
}
public void selected() {
if (selected) {
rect.setFill(Color.GREEN);
ShelfShowUp.display("KOKOS", 7);
} else {
rect.setFill(Color.HOTPINK);
}
}
}
By better way I mean, that there might be other way than passing GridPane and int x, int y indexes. I still wanna use GridPane.
Thank you for any Help
I am new to this
As #JimD already commented, embedding a reference to the parent of a Node into a node is bad practice. There's nothing special about the Rectangles other than the event handler which toggles the colour. Assuming that you don't want to access the "selected" state of the Rectangles, you can ditch the custom class altogether.
The following code does everything that your sample does, except the call to the [not included] "ShelfShowUp.display("KOKOS", 7)" :
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import static java.lang.String.valueOf;
public class GridPaneCustom extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
GridPane grid = new GridPane();
grid.setBackground(new Background(new BackgroundFill(Color.PINK, CornerRadii.EMPTY, Insets.EMPTY)));
grid.setPadding(new Insets(10));
grid.setVgap(10);
grid.setHgap(10);
grid.setAlignment(Pos.CENTER);
for (int column = 0; column < 10; column++) {
for (int row = 0; row < 10; row++) {
Rectangle rectangle = new Rectangle(20, 30, Color.HOTPINK);
rectangle.setOnMouseClicked(ev -> rectangle.setFill(
rectangle.getFill().equals(Color.HOTPINK) ? Color.GREEN : Color.HOTPINK));
grid.add(rectangle, column, row);
grid.add(new Text(valueOf(column + row)), column, row);
}
}
primaryStage.setScene(new Scene(grid, 400, 400));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I'm want to be able to drag-to-draw a line that ends in the center of each of two nodes (circles). I have two event handlers that I'm thinking listen for new mouse clicks, but whenever I click, nothing happens.
Here is the Start method:
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.input.MouseEvent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.shape.Line;
import javafx.scene.shape.Circle;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.layout.Pane;
#Override
public void start(Stage primaryStage) {
Group root = new Group();
Canvas canvas = new Canvas(500,500);
GraphicsContext gc = canvas.getGraphicsContext2D();
//Draw Circles onto a Pane:
Pane overlay = new Pane();
for (int i = 50; i < xDim; i+=50) {
for (int j = 50; j < yDim; j+=50) {
Circle c1 = new Circle();
c1.setCenterX(i);
c1.setCenterY(j);
c1.setRadius(5);
overlay.getChildren().add(c1);
}
}
drawLine(overlay);
root.getChildren().addAll(canvas,overlay);
primaryStage.setScene(new Scene(root, 500, 500));
primaryStage.show();
}
And here is drawLine():
Line l = new Line();
overlay.addEventHandler(MouseEvent.MOUSE_PRESSED,
new EventHandler<MouseEvent>() {
public void handle(MouseEvent t) {
if (t.getSource() instanceof Circle) {
Circle p = ((Circle) (t.getSource()));
double circleX = p.getCenterX();
double circleY = p.getCenterY();
l.setStartX(circleX);
l.setStartY(circleY);
} else {
Node p = ((Node) (t.getSource()));
double orgTranslateX = p.getTranslateX();
double orgTranslateY = p.getTranslateY();
}
}
});
overlay.addEventHandler(MouseEvent.MOUSE_RELEASED,
new EventHandler<MouseEvent>() {
public void handle(MouseEvent t) {
if (t.getSource() instanceof Circle) {
Circle p = ((Circle) (t.getSource()));
double circleX = p.getCenterX();
double circleY = p.getCenterY();
l.setEndX(circleX);
l.setEndY(circleY);
overlay.getChildren().add(l);
} else{}
}
});
};
You can see draw line has two different event handlers, one for a click and one for release, and the only thing that changes is myLine.setEndX().
Any help would be appreciated! I apologize in advance for any unknown transgressions.
In the application I need to create, I have a series of points which are connected by lines. I need to only see the line part of the graph and hide the points. However, I need to mark certain points by clicking on the appropriate places in the graph. In the code I have written I am either able to hide the points and not able to mark or I am able to mark but not hide the rest of the points. What do I do to hide the points and mark the required points?
package application;
import java.awt.Dimension;
import java.awt.Toolkit;
//import java.beans.EventHandler;
import java.util.ArrayList;
import javafx.event.EventHandler;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.Scene;
import javafx.scene.chart.Axis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.Priority;
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 Main extends Application {
public XYChart.Data<Integer, Integer> k = new XYChart.Data();
public XYChart.Series series;
String pth;
public ObservableList<XYChart.Data<Integer, Integer>> dat = FXCollections.<XYChart.Data<Integer, Integer>>observableArrayList();
public int c = 0;
ArrayList<Integer> myListx = new ArrayList<Integer>();
public void start(Stage stage) {
stage.setTitle("Line Chart Sample");
final NumberAxis xAxis = new NumberAxis();
final NumberAxis yAxis = new NumberAxis();
xAxis.setLabel("Samples");
yAxis.setLabel("Data");
final LineChart<Number, Number> lineChart = new LineChart<Number, Number>(xAxis, yAxis);
lineChart.setCreateSymbols(false);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int width = (int) screenSize.getWidth();
int height = (int) screenSize.getHeight();
StackPane spLineChart = new StackPane();
spLineChart.getChildren().add(lineChart);
while (c < 20) {
k = new XYChart.Data<Integer, Integer>(c++, c * 6);
dat.add(k);
k.setNode(new Node(c));
}
series = new XYChart.Series("IMU Data", dat);
lineChart.getData().addAll(series);
xAxis.setVisible(true);
yAxis.setVisible(true);
spLineChart.setVisible(true);
StackPane spButton = new StackPane();
StackPane sp = new StackPane();
VBox vbox = new VBox();
VBox.setVgrow(spLineChart, Priority.ALWAYS);
vbox.getChildren().addAll(sp, spLineChart, spButton);
Scene scene = new Scene(vbox, width, height - 500);
stage.setScene(scene);
stage.show();
}
class Node extends StackPane {
Node(int priorValue) {
final Circle circle = createData();
setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if (mouseEvent.getButton().equals(MouseButton.PRIMARY)) {
getChildren().setAll(circle);
myListx.add(priorValue);
} else if (mouseEvent.getButton().equals(MouseButton.SECONDARY)) {
getChildren().clear();
myListx.remove(new Integer(priorValue));
}
}
;
});
}
private Circle createData() {
Circle circle = new Circle();
circle.setFill(Color.BLACK);
circle.setStroke(Color.BLACK);
circle.setRadius(4);
return circle;
}
}
public static void main(String[] args) {
launch(args);
}
}
Use this modified Node class. Where on-mouse click event, it toggles the effect as per the existence of its children.
class Node extends StackPane {
Node(int priorValue) {
setOnMouseClicked(event -> {
if (getChildren().isEmpty()) {
final Circle circle = createData();
getChildren().setAll(circle);
} else {
getChildren().clear();
}
myListx.add(priorValue);
});
}
private Circle createData() {
Circle circle = new Circle();
circle.setFill(Color.BLACK);
circle.setStroke(Color.BLACK);
circle.setRadius(4);
return circle;
}
}
Note: You may consider renaming your class Node as it clashes with Node.
I have two circles that I want to turn them around a pivot clockwise if the right key is pressed and counter clockwise if the left key is pressed but my code does not work.
import javafx.animation.*;
import javafx.application.Application;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.geometry.Bounds;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.effect.GaussianBlur;
import javafx.scene.layout.*;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.scene.text.Font;
import javafx.scene.transform.Rotate;
import javafx.stage.Popup;
import javafx.stage.Stage;
import javafx.util.Duration;
import static java.lang.Math.cos;
import static java.lang.Math.sin;
public class Main extends Application {
public static final double CIRCLES_CENTER_X = 600;
public static final double CIRCLES_CENTER_Y = 450;
public static final double CIRCLES_RADIUS = 15;
public static final double CIRCLES_DISTANCE = 300;
public static final double GAME_HEIGHT = 700;
public static final double GAME_WIDTH = 1200;
private Stage primaryStage;
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
primaryStage.setMinWidth(GAME_WIDTH);
primaryStage.setMinHeight(GAME_HEIGHT);
final Scene scene;
BorderPane root = new BorderPane();
scene = new Scene(root, Main.GAME_WIDTH, Main.GAME_HEIGHT);
Circle orangeCircle = new Circle(Main.CIRCLES_CENTER_X + Main.CIRCLES_DISTANCE / 2 * cos(0),
Main.CIRCLES_CENTER_Y + Main.CIRCLES_DISTANCE / 2 * sin(0),
Main.CIRCLES_RADIUS, Color.ORANGE);
Circle yellowCircle = new Circle(Main.CIRCLES_CENTER_X - Main.CIRCLES_DISTANCE / 2 * cos(0),
Main.CIRCLES_CENTER_Y - Main.CIRCLES_DISTANCE / 2 * sin(0),
Main.CIRCLES_RADIUS, Color.YELLOW);
Pane game = new Pane(orangeCircle, yellowCircle);
root.setCenter(game);
SimpleIntegerProperty angle = new SimpleIntegerProperty(0);
root.setOnKeyPressed(ke -> {
if (ke.getCode().toString().equals("RIGHT")) {
angle.set(360);
}
if (ke.getCode().toString().equals("LEFT")) {
angle.set(-360);
}
});
root.setOnKeyReleased(ke -> {
if (ke.getCode().toString().equals("RIGHT")) {
angle.set(0);
}
if (ke.getCode().toString().equals("LEFT")) {
angle.set(0);
}
});
Rotate orangeCircleRotation = new Rotate(0, Main.CIRCLES_CENTER_X, Main.CIRCLES_CENTER_Y);
orangeCircle.getTransforms().add(orangeCircleRotation);
Rotate yellowCircleRotation = new Rotate(0, Main.CIRCLES_CENTER_X, Main.CIRCLES_CENTER_Y);
yellowCircle.getTransforms().add(yellowCircleRotation);
Timeline rotationAnimation = new Timeline();
rotationAnimation.setCycleCount(Timeline.INDEFINITE);
angle.addListener((ov, old_val, new_val) -> {
System.out.println("fk");
rotationAnimation.stop();
while (rotationAnimation.getKeyFrames().size() > 0) {
rotationAnimation.getKeyFrames().remove(0);
}
rotationAnimation.getKeyFrames().add(
new KeyFrame(Duration.millis(2000),
new KeyValue(orangeCircleRotation.angleProperty(), angle.getValue())));
rotationAnimation.getKeyFrames().add(
new KeyFrame(Duration.millis(2000),
new KeyValue(yellowCircleRotation.angleProperty(), angle.getValue())));
rotationAnimation.play();
}
);
primaryStage.setScene(scene);
primaryStage.show();
}
public Stage getPrimaryStage() {
return primaryStage;
}
}
It works almost fine but when i press a key and release it the circles does not stop. they just start turning backward until the last rotation change point and keep repeating that so also when i press the key again it jumps sometimes.(Because the backward turn has reached its end and starts from the beginning)(Hard to explain!you have to see it for yourself!)
Does anyone know how to fix or achieve this?
I wouldn't try to manipulate the key frames while the animation is in progress. Instead you can just pause/play the animation and change the rate. The only "gotcha" here is that it seems the animation ignores the change in rate if it is paused, so you need to call play() before setRate(...).
Here's the modified SSCCE:
import static java.lang.Math.cos;
import static java.lang.Math.sin;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
import javafx.util.Duration;
public class RotatingCircles extends Application {
public static final double CIRCLES_CENTER_X = 600;
public static final double CIRCLES_CENTER_Y = 450;
public static final double CIRCLES_RADIUS = 15;
public static final double CIRCLES_DISTANCE = 300;
public static final double GAME_HEIGHT = 700;
public static final double GAME_WIDTH = 1200;
private Stage primaryStage;
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
primaryStage.setMinWidth(GAME_WIDTH);
primaryStage.setMinHeight(GAME_HEIGHT);
final Scene scene;
BorderPane root = new BorderPane();
scene = new Scene(root, GAME_WIDTH, GAME_HEIGHT);
Circle orangeCircle = new Circle(CIRCLES_CENTER_X + CIRCLES_DISTANCE / 2 * cos(0),
CIRCLES_CENTER_Y + CIRCLES_DISTANCE / 2 * sin(0),
CIRCLES_RADIUS, Color.ORANGE);
Circle yellowCircle = new Circle(CIRCLES_CENTER_X - CIRCLES_DISTANCE / 2 * cos(0),
CIRCLES_CENTER_Y - CIRCLES_DISTANCE / 2 * sin(0),
CIRCLES_RADIUS, Color.YELLOW);
Pane game = new Pane(orangeCircle, yellowCircle);
root.setCenter(game);
Rotate orangeCircleRotation = new Rotate(0, CIRCLES_CENTER_X, CIRCLES_CENTER_Y);
orangeCircle.getTransforms().add(orangeCircleRotation);
Rotate yellowCircleRotation = new Rotate(0, CIRCLES_CENTER_X, CIRCLES_CENTER_Y);
yellowCircle.getTransforms().add(yellowCircleRotation);
Timeline rotationAnimation = new Timeline();
rotationAnimation.setCycleCount(Timeline.INDEFINITE);
rotationAnimation.getKeyFrames().add(new KeyFrame(Duration.seconds(2), new KeyValue(orangeCircleRotation.angleProperty(), 360)));
rotationAnimation.getKeyFrames().add(new KeyFrame(Duration.seconds(2), new KeyValue(yellowCircleRotation.angleProperty(), 360)));
root.setOnKeyPressed(ke -> {
if (ke.getCode() == KeyCode.RIGHT) {
rotationAnimation.play();
rotationAnimation.setRate(1);
} else if (ke.getCode() == KeyCode.LEFT) {
rotationAnimation.play();
rotationAnimation.setRate(-1);
}
});
root.setOnKeyReleased(ke -> {
rotationAnimation.pause();
});
primaryStage.setScene(scene);
primaryStage.show();
root.requestFocus();
}
public Stage getPrimaryStage() {
return primaryStage;
}
}
BTW In this code you really don't need two separate rotations, since they are identical. Just create a single rotation and add it to both circles' transforms lists. It may be different in your real code, of course...
What I want to do is scale an image on a cylinder in Java3D. The code I have so far is shown below. I have no clue how to make the image not stretch over the whole cylinder.
package javafxapplication2;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.Group;
import javafx.scene.PerspectiveCamera;
import javafx.scene.PointLight;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
import javafx.scene.paint.Material;
import javafx.scene.paint.PhongMaterial;
import javafx.scene.shape.Cylinder;
import javafx.scene.shape.MeshView;
import javafx.scene.shape.TriangleMesh;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
public class EarthCylinder extends Application {
double anchorX, anchorY;
private double anchorAngleX = 0;
private double anchorAngleY = 0;
private final DoubleProperty angleX = new SimpleDoubleProperty(0);
private final DoubleProperty angleY = new SimpleDoubleProperty(0);
PerspectiveCamera scenePerspectiveCamera = new PerspectiveCamera(false);
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// Remove this line once dirtyopts bug is fixed for 3D primitive
System.setProperty("prism.dirtyopts", "false");
launch(args);
}
#Override
public void start(Stage primaryStage) {
Image diffuseMap
= new Image(EarthCylinder.class
.getResource("download.jpg")
.toExternalForm());
PhongMaterial material = new PhongMaterial();
material.setDiffuseMap(diffuseMap);
final Cylinder cylinder = new Cylinder(200, 400);
cylinder.setMaterial(material);
final Group parent = new Group(cylinder);
parent.setTranslateX(450);
parent.setTranslateY(450);
parent.setTranslateZ(0);
Rotate xRotate;
Rotate yRotate;
parent.getTransforms().setAll(
xRotate = new Rotate(0, Rotate.X_AXIS),
yRotate = new Rotate(0, Rotate.Y_AXIS));
xRotate.angleProperty().bind(angleX);
yRotate.angleProperty().bind(angleY);
final Group root = new Group();
root.getChildren().add(parent);
final Scene scene = new Scene(root, 900, 900, true);
scene.setFill(Color.BLACK);
scene.setOnMousePressed(event -> {
anchorX = event.getSceneX();
anchorY = event.getSceneY();
anchorAngleX = angleX.get();
anchorAngleY = angleY.get();
});
scene.setOnMouseDragged(event -> {
angleX.set(anchorAngleX - (anchorY - event.getSceneY()));
angleY.set(anchorAngleY + anchorX - event.getSceneX());
});
PointLight pointLight = new PointLight(Color.WHITE);
pointLight.setTranslateX(400);
pointLight.setTranslateY(400);
pointLight.setTranslateZ(-3000);
scene.setCamera(scenePerspectiveCamera);
root.getChildren().addAll(pointLight, scenePerspectiveCamera);
primaryStage.setScene(scene);
primaryStage.show();
}
}
The result of the above code is shown here: