Java AnimationTimer content not showing - java

I'm trying to learn how JavaFX animations work, so I've tried to create an animation with Earth moving in a circle, based on this tutorial: https://gamedevelopment.tutsplus.com/tutorials/introduction-to-javafx-for-game-development--cms-23835.
For some reason, the scene I want to show is set in the stage, but the graphics or animations aren't. I'm pretty sure this one is trivial, I just can't seem to find the reason. Here's my Main class code:
public class Main extends Application {
private static Stage stage;
private static Window window;
private static Scene scWindow;
#Override
public void start(Stage primaryStage) {
stage = primaryStage;
stage.setTitle("Animation");
showWindow();
stage.show();
}
public static void showWindow() {
window = new Window();
scWindow = new Scene(window, 400, 400);
stage.setScene(scWindow);
}
public static void main(String[] args) {
launch(args);
}
}
and this is the Window class code:
public class Window extends BorderPane {
final long startNanoTime = System.nanoTime();
Image earth = new Image("http://icdn.pro/images/en/g/o/google-earth-icone-8927-128.png");
Image space = new Image("https://space-facts.com/wp-content/uploads/magellanic-clouds.png");
GraphicsContext gc;
public Window() {
Group root = new Group();
Canvas canvas = new Canvas(512, 512);
gc = canvas.getGraphicsContext2D();
root.getChildren().add(canvas);
gc.setFill(Color.BLACK);
Anim a = new Anim();
a.start();
}
private class Anim extends AnimationTimer {
public void handle(long currentNanoTime)
{
double t = (currentNanoTime - startNanoTime) / 1000000000.0;
double x = 232 + 128 * Math.cos(t);
double y = 232 + 128 * Math.sin(t);
gc.drawImage( space, 0, 0 );
gc.drawImage( earth, x, y );
}
}
}
There's no exception thrown or anything, I have no idea what's wrong.
EDIT:
I know that I haven't put anything in the setCenter/Left etc. in the constructor, so actually if it is the reason, how should I put the animation e.g. on the center of the border pane?

You forgot to add root to the scene. Add something like
setCenter(root);
to the constructor of Window.
Or simply use the Canvas as center:
public Window() {
Canvas canvas = new Canvas(512, 512);
gc = canvas.getGraphicsContext2D();
setCenter(canvas);
gc.setFill(Color.BLACK);
Anim a = new Anim();
a.start();
}

Related

Crop JavaFX Image to square or Circle

I need a function.
import javafx.scene.image.Image;
private Image cropImage(Image image) {
// Cut Image to max Circle or square
return Image;
}
The important thing is that I don't want to change the scale of the image but cut the maximum circle or square in the middle of the image.
I have been trying to do this for two days now.
Can you help me with this?
Thank you very much. Rene
Something like this:
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Image img = new Image("https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Osias_Beert_the_Elder_-_Dishes_with_Oysters%2C_Fruit%2C_and_Wine_-_Google_Art_Project.jpg/350px-Osias_Beert_the_Elder_-_Dishes_with_Oysters%2C_Fruit%2C_and_Wine_-_Google_Art_Project.jpg");
Image img2 = crop(img, false);
Image img3 = crop(img, true);
HBox box = new HBox(8, new ImageView(img),new ImageView(img2),new ImageView(img3));
Scene scene = new Scene(box);
stage.setScene(scene);
stage.show();
}
private Image crop(Image img, boolean toCircle) {
double d = Math.min(img.getWidth(),img.getHeight());
double x = (d-img.getWidth())/2;
double y = (d-img.getHeight())/2;
Canvas canvas = new Canvas(d, d);
GraphicsContext g = canvas.getGraphicsContext2D();
if (toCircle) {
g.fillOval(0, 0, d, d);
g.setGlobalBlendMode(BlendMode.SRC_ATOP);
}
g.drawImage(img, x, y);
return canvas.snapshot(null, null);
}
}
You can do that via the setClip method of the ImageView.

JavaFX - resizing simple application causes "flickering" due to canvas clearing/repaint

I've got this dead simple project that basically draws a black circle in the middle of a JavaFX Scene Canvas and grows it every 50ms.
Here's my Controller:
public class PrimaryController {
public StackPane theRootPane;
public CanvasPane theCanvasPane;
int i = 1;
public void initialize() {
theCanvasPane = new CanvasPane(500, 300);
theRootPane.getChildren().addAll(theCanvasPane);
Timeline theTimeline =
new Timeline(new KeyFrame(Duration.millis(50), actionEvent -> UpdateCanvas(i++)));
theTimeline.setCycleCount(Timeline.INDEFINITE);
theTimeline.play();
}
private void UpdateCanvas(int diameter) {
theCanvasPane.DrawCircleAtCenterOfCanvas(diameter);
}
Here's my CanvasPane class:
public class CanvasPane extends Pane {
private final Canvas theCanvas;
private GraphicsContext theGC;
public CanvasPane(double width, double height) {
setWidth(width);
setHeight(height);
theCanvas = new Canvas(width, height);
theGC = theCanvas.getGraphicsContext2D();
getChildren().add(theCanvas);
theCanvas.widthProperty().bind(this.widthProperty());
theCanvas.heightProperty().bind(this.heightProperty());
theCanvas.widthProperty().addListener(observable -> RedrawCanvas());
theCanvas.heightProperty().addListener(observable -> RedrawCanvas());
}
private void RedrawCanvas() {
ClearCanvas();
}
private void ClearCanvas() {
theGC.clearRect(0, 0, theCanvas.widthProperty().doubleValue(), theCanvas.heightProperty().doubleValue());
}
public void DrawCircleAtCenterOfCanvas(int diameter) {
double centreX = theCanvas.widthProperty().doubleValue() / 2;
double centreY = theCanvas.heightProperty().doubleValue() / 2;
theGC.fillOval(centreX - diameter / 2.0, centreY - diameter / 2.0, diameter, diameter);
}
}
Finally, here's my App class and my .fxml
public class App extends Application {
private static Scene scene;
#Override
public void start(Stage stage) throws IOException {
scene = new Scene(loadFXML("primary"));
stage.setScene(scene);
//stage.setResizable(false);
stage.show();
}
private static Parent loadFXML(String fxml) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource(fxml + ".fxml"));
return fxmlLoader.load();
}
public static void main(String[] args) {
launch();
}
}
primary.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.StackPane?>
<StackPane fx:id="theRootPane" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.xxx.PrimaryController" />
It works fine until I resize the window, at which point, the canvas is redrawn by clearing it and then painting the new larger circle on the canvas. This "clearing" of the canvas presents as flicker when the form is resized.
What is a better way of doing this? I'm fudging around with JavaFX after learning Java and getting into UI and animations. I'm thinking canvas is not the way to go...
Any advice would be much appreciated.
It's probably better to update the canvas in a ChangeListener rather than an InvalidationListener, which will result in fewer redraws. Either way you should either:
ensure you redraw the circle when the canvas size changes (with your current code, you clear the canvas as soon as the canvas changes size, but don't redraw the circle until the next keyframe, so you end up with some blank canvases in between):
public class CanvasPane extends Pane {
private final Canvas theCanvas;
private GraphicsContext theGC;
private int currentDiameter ;
public CanvasPane(double width, double height) {
setWidth(width);
setHeight(height);
theCanvas = new Canvas(width, height);
theGC = theCanvas.getGraphicsContext2D();
getChildren().add(theCanvas);
theCanvas.widthProperty().bind(this.widthProperty());
theCanvas.heightProperty().bind(this.heightProperty());
theCanvas.widthProperty().addListener((obs, oldWidth, newWidth) -> redrawCanvas());
theCanvas.heightProperty().addListener((obs, oldHeight, newHeight) -> redrawCanvas());
}
public void increaseDiameter() {
currentDiameter++;
redrawCanvas();
}
private void redrawCanvas() {
clearCanvas();
drawCircleAtCenterOfCanvas();
}
private void clearCanvas() {
theGC.clearRect(0, 0, theCanvas.widthProperty().doubleValue(), theCanvas.heightProperty().doubleValue());
}
public void drawCircleAtCenterOfCanvas() {
currentDiameter = currentDiameter ;
double centreX = theCanvas.widthProperty().doubleValue() / 2;
double centreY = theCanvas.heightProperty().doubleValue() / 2;
theGC.fillOval(centreX - currentDiameter / 2.0, centreY - currentDiameter / 2.0, currentDiameter, currentDiameter);
}
}
and
public class PrimaryController {
#FXML
private StackPane theRootPane;
#FXML
private CanvasPane theCanvasPane;
public void initialize() {
theCanvasPane = new CanvasPane(500, 300);
theRootPane.getChildren().addAll(theCanvasPane);
Timeline theTimeline =
new Timeline(new KeyFrame(Duration.millis(50), actionEvent -> updateCanvas()));
theTimeline.setCycleCount(Timeline.INDEFINITE);
theTimeline.play();
}
private void updateCanvas() {
theCanvasPane.increaseDiameter();
}
}
or (probably better, and far easier for this example) use a Circle instead of a canvas:
public class PrimaryController {
#FXML
private StackPane theRootPane;
private Circle circle ;
public void initialize() {
circle = new Circle();
theRootPane.getChildren().addAll(circle);
Timeline theTimeline =
new Timeline(new KeyFrame(Duration.millis(50), actionEvent -> updateCircle()));
theTimeline.setCycleCount(Timeline.INDEFINITE);
theTimeline.play();
}
private void updateCircle() {
circle.setRadius(circle.getRadius()+0.5);
}
}

Explicit positioning a Pane

I'm trying to create a basic UML diagramming tool with JavaFX.
First I did this:
public class Asd extends Application {
Double WINDOW_HEIGHT = (double) 400;
Double WINDOW_WIDTH = (double) 800;
Double RECTANGLE_DEFAULT_HEIGHT = (double) 50;
Double RECTANGLE_DEFAULT_WIDTH = (double) 100;
#Override
public void start(Stage stage) throws Exception {
final Pane root = new Pane();
Rectangle background = new Rectangle(WINDOW_WIDTH, WINDOW_HEIGHT,
Color.WHITE);
root.getChildren().add(background);
Scene scene = new Scene(root, WINDOW_WIDTH, WINDOW_HEIGHT);
// On window click
root.setOnMousePressed(new EventHandler<MouseEvent>() {
public void handle(MouseEvent mouseEvent) {
Rectangle rect = new Rectangle();
rect.setX(mouseEvent.getX());
rect.setY(mouseEvent.getY());
rect.setWidth(RECTANGLE_DEFAULT_WIDTH);
rect.setHeight(RECTANGLE_DEFAULT_HEIGHT);
rect.setFill(null);
rect.setStroke(Color.BLACK);
root.getChildren().add(rect);
}
});
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
With that, I can draw a rectangle (that would represent a UML Class) in the exact point I click.
Now I want to add text. If instead of
root.getChildren().add(rect);
I do
Text text = new Text("mockText");
StackPane stackPane = new StackPane();
stackPane.getChildren().addAll(rect, text);
root.getChildren().add(stackPane);
the text is placed in the center of the rectangle, but all rectangles are positioned at coordinates (0,0)
So, any way to change the position of each StackPane? Or any alternative to add text to the rectangles directly without using StackPane?
OK, got it. Layout elements have methods setLayoutX & setLayoutY that can be used in cases like this one, so I don't need anymore Rectangle.setX/setY & finally the handle method should be:
public void handle(MouseEvent mouseEvent) {
Rectangle rect = new Rectangle();
rect.setWidth(RECTANGLE_DEFAULT_WIDTH);
rect.setHeight(RECTANGLE_DEFAULT_HEIGHT);
rect.setFill(null);
rect.setStroke(Color.BLACK);
Text text = new Text("mockText");
StackPane stackPane = new StackPane();
stackPane.getChildren().addAll(rect, text);
stackPane.setLayoutX(mouseEvent.getX());
stackPane.setLayoutY(mouseEvent.getY());
root.getChildren().add(stackPane);
}

How to make a node move whenever mouse is move over the scene (in JavaFX)?

I have written a small JavaFX program in which a Rectangle node is made to moved whenever mouse cursor is moved over the scene containing the rectangle. Here is my code:
public class MovedObjectWhenMouseMoved extends Application{
double nodeX;
double currentMousePos;
double oldMousePos = 0.0;
public static void main(String[] arg){
launch(arg);
}
#Override
public void start(Stage stage) throws Exception {
final Rectangle rect = new Rectangle(50, 50, Color.RED);
rect.setX(20);
rect.setY(20);
AnchorPane anchorPane = new AnchorPane();
anchorPane.getChildren().add(rect);
Scene scene = new Scene(anchorPane,500,500,Color.GREEN);
stage.setScene(scene);
stage.show();
scene.setOnMouseMoved(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
currentMousePos = mouseEvent.getX();
if(currentMousePos>oldMousePos){
rect.setX(rect.getX()+1); // Move right
}else if(currentMousePos<oldMousePos){
rect.setX(rect.getX()-1); // Move Left
}
oldMousePos = currentMousePos;
}
});
}
}
But here the problem is that the node speed is not same as the mouse speed.
How can I rectify this problem? Also please let me know if there is any better approach.
Mouse can change position on more then 1 pixel.
Try this code for handle:
currentMousePos = mouseEvent.getX();
double dX = currentMousePosition - oldMousePos;
rect.setX(rect.getX() + dX);
oldMousePos = currentMousePos;

creating undecorated stage in javafx 2.0

I am trying to create a custom stage in javafx 2.0. I want that my stage drops shadow on the screen as dropped by other windows... I tried with following code :
public class ChatWindow {
final private Stage stage = new Stage(StageStyle.UNDECORATED);
private Scene scene;
private Group rg;
private Text t = new Text();
private double initx = 0, inity = 0;
public ChatWindow() {
rg = new Group();
scene = new Scene(rg, 320, 240);
//scene.setFill(null);
scene.setFill(new Color(0, 0, 0, 0));
stage.setScene(scene);
stage.show();
setupStage();
}
private void setupStage() {
Rectangle r = new Rectangle(5, 5, stage.getWidth() - 10, stage.getHeight() - 10);
r.setFill(Color.STEELBLUE);
r.setEffect(new DropShadow());
rg.setOnMousePressed(new EventHandler<MouseEvent>() {
public void handle(MouseEvent me) {
initx = me.getScreenX() - stage.getX();// - me.getSceneX();
inity = me.getScreenY() - stage.getY();
}
});
rg.setOnMouseDragged(new EventHandler<MouseEvent>() {
public void handle(MouseEvent me) {
stage.setX(me.getScreenX() - initx);
stage.setY(me.getScreenY() - inity);
}
});
rg.getChildren().add(r);
rg.getChildren().add(t);
}
public void setVisible() {
stage.show();
}
}
I can see the shadow fall, but actually their is a white background on which its falling.
So, its useless, as on colored screen the defect will be visible, will make it look ugly..
This is the screen shot on white screen :
And this on colored screen:
HOw to resolve this issue?? Please help.
You should set style StageStyle.TRANSPARENT, see next code:
public class ChatWindow extends Application {
#Override
public void start(final Stage stage) throws Exception {
stage.initStyle(StageStyle.TRANSPARENT); // here it is
Group rg = new Group();
Scene scene = new Scene(rg, 320, 240, Color.TRANSPARENT);
stage.setScene(scene);
stage.show();
Rectangle r = new Rectangle(5, 5, stage.getWidth() - 10, stage.getHeight() - 10);
r.setFill(Color.STEELBLUE);
r.setEffect(new DropShadow());
rg.getChildren().add(r);
}
public static void main(String[] args) {
launch();
}
}

Categories

Resources