1

New to java and JavaFX so please bear with me

I need to do a presentation of 5 3d fruit models that show in a continuous loop, 15 seconds apart: fruit1 for 15 seconds then fruit2 for 15 seconds ad so on and so forth.. until fruit5 for 15 seconds and then back to fruit1 and continues until I hit the ESC key which should close the window.

I also understand that it's ideal to change the root group object that makes up the scene instead of changing the scene, so I changed that in my code

I understand that a timeline is needed in order to change something in the scene as it plays out, but I've tried with something similar to what this answer says but I don't get the logic of how to switch the root of the scene every 15seconds

UPDATE:

I gave up on the timeline option and I found the platform.run option as seen on this article which seems to work as I see the window updates iterating from the first fruit in the scenes array to second one but I'm not sure why it only runs once when I need it to run every 15 seconds which means that my scene switcher: nextSceneIndex() should go back and forth between 1 and 0.

UPDATE2:

I went back to the timeline suggestion and I implemented Sedrick's solution and it worked... I can't be happier :)

Here's my working code!


  public void start(Stage stage) throws Exception {

        BorderPane[] scenes = new BorderPane[]{createSceneApple(),createSceneCarrot(),createSceneTomato()};


        Timeline tl = new Timeline();
        tl.setCycleCount(Animation.INDEFINITE);
        KeyFrame kf_fruit = new KeyFrame(Duration.seconds(10),
                new EventHandler<ActionEvent>() {
                    public void handle(ActionEvent event) {

                       if (index==0){
                           root.getChildren().setAll(scenes[0]);
                           index = 1;
                       }else if(index==1){
                           root.getChildren().setAll(scenes[1]);
                           index = 2;
                       }else if(index==2){
                           root.getChildren().setAll(scenes[2]);
                           index = 0;
                       }
                    }  
        });

        tl.getKeyFrames().add(kf_fruit);
        tl.play();

        Scene scene = new Scene(root, windowWidth, windowHeight);
        stage.setScene(scene);
        stage.show();

}


Francisco Cortes
  • 1,121
  • 10
  • 19
  • 2
    Does this answer your question? [JavaFX periodic background task](https://stackoverflow.com/questions/9966136/javafx-periodic-background-task) – SedJ601 Nov 11 '19 at 03:40
  • Hi @Sedrick. I did review that link even before posting although my experience with JavaFX or java in itself for that matter is so limited that I haven't been able to figure out how to change the properties of the scene within the timeline. all the examples for timeline refer to objects within a scene but no the scene itself and that's possibly what makes it confusing to me, assuming that it is in fact possible. thank you though for your recommendation. – Francisco Cortes Nov 12 '19 at 22:22

1 Answers1

1

Maybe you can get some ideas from here. This uses the code from the link I posted above. Timeline is used to loop through a list of Shape and info about that shape.

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;

/**
 * JavaFX App
 */
public class App extends Application {

    @Override
    public void start(Stage stage) {
        List<MyShape> shapes = new ArrayList();
        shapes.add(new MyShape("Circle", "Shape.Circle", "More Circle Info", new Circle(25, Color.BLUE)));
        shapes.add(new MyShape("Rectangle", "Shape.Rectangle", "More Rectangle Info", new Rectangle(100, 50, Color.RED)));
        shapes.add(new MyShape("Line", "Shape.Line", "More Line Info", new Line(0, 0, 100, 100)));


        TextField tf1 = new TextField();
        TextField tf2 = new TextField();
        TextArea ta1 = new TextArea();        
        VBox leftWindow = new VBox(tf1, tf2, ta1);

        StackPane rightWindow = new StackPane(shapes.get(1).getShape());

        AtomicInteger counter = new AtomicInteger();
        Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1), new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                System.out.println(counter.get() % shapes.size());
                MyShape currentShape = shapes.get(counter.getAndIncrement() % shapes.size());
                tf1.setText(currentShape.getName());
                tf2.setText(currentShape.getType());
                ta1.setText(currentShape.getMoreInfo());
                rightWindow.getChildren().set(0, currentShape.getShape());
            }
        }));
        timeline.setCycleCount(Timeline.INDEFINITE);
        timeline.play();

        BorderPane root = new BorderPane();
        root.setLeft(new StackPane(leftWindow));
        root.setRight(rightWindow);

        var scene = new Scene(root, 640, 480);
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }

}

Update: If you only have two scenes, that simplifies some things. You basically need to set the initial view. You then need to switch out the view currently showing every two seconds. (I used two seconds so that you can see the views before they are switched out). I created my own version of createSceneCarrot and createSceneApple since I don't know your implementation.

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;

/**
 * JavaFX App
 */
public class App extends Application {

    @Override
    public void start(Stage stage) {
        BorderPane[] scenes = new BorderPane[]{createSceneApple(),createSceneCarrot()};


        StackPane root = new StackPane(scenes[0]);//Set initial view;      

        Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(2), (ActionEvent event) -> {
            if(root.getChildren().get(0).equals(scenes[0]))//If the first scene is loaded, load the second scene.
            {
                root.getChildren().set(0, scenes[1]);
            }
            else
            {
                root.getChildren().set(0, scenes[0]);
            }
        }));
        timeline.setCycleCount(Timeline.INDEFINITE);
        timeline.play();

        var scene = new Scene(root, 640, 640);
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }

    public BorderPane createSceneApple()
    {
        BorderPane borderPane = new BorderPane();

        TextField tf1 = new TextField("Rectangle 1");
        TextField tf2 = new TextField("Rectangle Color: Blue");
        TextArea ta1 = new TextArea("20x40");        
        VBox leftWindow = new VBox(tf1, tf2, ta1);
        borderPane.setLeft(leftWindow);

        StackPane rightWindow = new StackPane(new Rectangle(20, 40, Color.BLUE));
        borderPane.setRight(rightWindow);

        return  borderPane;
    }

    public BorderPane createSceneCarrot()
    {
        BorderPane borderPane = new BorderPane();

        TextField tf1 = new TextField("Circle 1");
        TextField tf2 = new TextField("Circle Color: Blue");
        TextArea ta1 = new TextArea("Radius: 50");        
        VBox leftWindow = new VBox(tf1, tf2, ta1);
        borderPane.setLeft(leftWindow);

        StackPane rightWindow = new StackPane(new Circle(50, Color.RED));
        borderPane.setRight(rightWindow);

        return  borderPane;
    }
}
SedJ601
  • 12,173
  • 3
  • 41
  • 59
  • thank you again and trust me.. I tried your solution until I gave up.. I'm still not sure how to get the the timeline working.. I'm trying with 2 fruits in an array as opposed to a nice shape class like what you got on your code becuase I thought it would be simplier as I'm am a complete noob, but even using your code and changing the rootPane for one of my scenes array elements. I was getting a blank screen if I was lucky enough to get things compiled. – Francisco Cortes Nov 16 '19 at 07:21
  • Hi @Sedrick, you are right.. I changed the scene's root to a Group object and I took out the scene and stage from the event handler but that gives me a blank window when I run it. the scenes array contains borderpane elements with a left and right component on each and they work. but how do I iterate through the scenes array in the timeline?. a forloop maybe? I'm sorry man, I'm not sure how to create the equivalent to your shape class, that's why I thought of an array. – Francisco Cortes Nov 16 '19 at 17:01
  • research is all I've done for the past 2 weeks.. thanks anyway man. – Francisco Cortes Nov 16 '19 at 20:48
  • 1
    Hey @Sedrick... I went ahead and implemented the if else statement that your propused on the timeline event and boila.. problem solved!!! :).. I'm so happy!!!.. thanks man for not giving up on me! – Francisco Cortes Nov 21 '19 at 00:59