简体   繁体   English

Javafx时间轴在通过多个侦听器时停止播放

[英]Javafx Timeline stops playing upon passing through multiple listeners

I'm a beginner who recently picked up JavaFX, I've been trying to make a program that executes an animation on a node upon clicking on it. 我是一个刚接触JavaFX的初学者,我一直在尝试制作一个程序,单击该程序即可在节点上执行动画。 I've tried to see if anyone else has tried something similar but I have not been able to find a similar case to what I've tried to do. 我试图看看是否有人尝试过类似的事情,但是我却找不到与我尝试过类似的案例。

The Timeline animates Panes, which contain a Rectangle and Text, horizontally across the screen, upon finishing the execution the stage will change scenes. 时间轴会在整个屏幕上水平放置一个包含矩形和文本的窗格动画,完成执行后,舞台将更改场景。 When the user clicks a button to go back to the original scene the buttons should perform another animation. 当用户单击按钮以返回到原始场景时,按钮应执行另一个动画。

I've verified that it isn't the scene transition that is causing it to stop and I have also verified that the program is indeed executing .play() on the timeline. 我已经验证不是导致场景停止的场景转换,并且还验证了程序确实在时间轴上执行了.play() The timelines are executed within the custom listeners so the listeners are not an issue as far as I know. 时间轴是在自定义监听器中执行的,因此据我所知,监听器不是问题。

Here is the code sample to recreate the issue I have, run the script and just press on. 这是重新创建问题的代码示例,运行脚本,然后按。 Here you can see that done is being printed to the console which is right underneath Main_Button_Animation.play(); 在这里,您可以看到done已打印到Main_Button_Animation.play();下的控制台中Main_Button_Animation.play(); however, nothing is being animated. 但是,没有任何动画。

import java.util.ArrayList;
import java.util.List;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Duration;

public class main extends Application{
    Scene scene1;

    @Override
    public void start(Stage primaryStage) {

        primaryStage.setTitle("test");
        Main_Screen MainScreen = new Main_Screen(800, 480);

        scene1 = MainScreen.MainScreen(primaryStage);
        MainScreen.getInitiater().Calibration_Listener(() -> {
            MainScreen.getInitiater().Back_Button_Pressed();
        });

        primaryStage.setScene(scene1);
        primaryStage.show();
    }
    public static void main(String[] args) {
        Application.launch(args);
    }
}
//custom listeners
interface Calibration {
    void menu();
}
interface Back {
    void back();
}
class Initiater {
    private List<Calibration> calilist = new ArrayList<Calibration>();;
    private List<Back> Go_Back = new ArrayList<Back>();

    public void Calibration_Listener(Calibration toAdd) {
        calilist.add(toAdd);
    }
    public void Back_Listener(Back toAdd) {
        Go_Back.add(toAdd);
    }

    public void Calibration_Pressed() {
        System.out.println("Calibration Pressed");
        for (Calibration hl : calilist)
            hl.menu();
    }
    public void Back_Button_Pressed() {
        System.out.println("Back Pressed");
        for (Back hl : Go_Back)
            hl.back();
    }
}

//animations and setup
class Main_Screen {
    Initiater initiater;
    private int X;
    private int Y;

    public Main_Screen(int X, int Y) {
        this.initiater = new Initiater();
        this.X = X;
        this.Y = Y;
    }

    private Pane UI_Button(String name, int[] pos, int[] size, Color color) {
        final Text text = new Text(0, 0, name);
        text.setFont(new Font(20));

        final Rectangle outline = new Rectangle(0, 0, size[0], size[1]);
        outline.setFill(color);

        final StackPane stack = new StackPane();
        stack.getChildren().addAll(outline, text);
        stack.setLayoutX(pos[0]);
        stack.setLayoutY(pos[1]);

        return stack;
    }

    public int getX() {
        return this.X;
    }

    public int getY() {
        return this.Y;
    }

    public Initiater getInitiater() {
        return this.initiater;
    }

    public Scene MainScreen(Stage stage) {
        Scene scene = new Scene(new Group(), getX(), getY());
        scene.setFill(Color.WHITE);

        Pane Start = UI_Button("Start", new int[] { getX() - getX() / 4, 0 }, new int[] { getX() / 4, getY() / 4 }, Color.rgb(255, 0, 0));
        Pane Calibration = UI_Button("Callibration", new int[] { 0, ((getY() + (getY() / 8)) / 4) * 0 }, new int[] { getX() / 2, getY() / 4 }, Color.rgb(60, 208, 230));

        System.out.println(Calibration.boundsInLocalProperty());
        ((Group) scene.getRoot()).getChildren().addAll(Calibration, Start);

        final Timeline Main_Button_Animation = new Timeline();
        final KeyFrame Cali_kf = new KeyFrame(Duration.millis(500), new KeyValue(Calibration.translateXProperty(), getX() / 2));
        final KeyFrame Start_kf = new KeyFrame(Duration.millis(500), new KeyValue(Start.translateXProperty(), -getX() / 4));
        Main_Button_Animation.getKeyFrames().addAll(Cali_kf, Start_kf);
        Main_Button_Animation.setRate(-1);
        Main_Button_Animation.jumpTo(Main_Button_Animation.getTotalDuration());
        Main_Button_Animation.play();

        Calibration.setOnMouseClicked(MouseEvent -> {
            Calibration.setDisable(true);
            Start.setDisable(true);
            System.out.println(Calibration.boundsInLocalProperty());
            Main_Button_Animation.setRate(1);
            Main_Button_Animation.jumpTo(Duration.millis(0));
            Main_Button_Animation.play();
            Main_Button_Animation.setOnFinished(event -> {
                Main_Button_Animation.play();
                System.out.println("done");
            });
        });     
        return scene;
    }

}

Edit1 : For clarity, The example above doesn't use any scene transition. Edit1 :为清楚起见,以上示例未使用任何场景转换。 Here is a example with a scene transition. 这是场景过渡的示例。

import java.util.ArrayList;
import java.util.List;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Duration;

public class main extends Application{
    Scene scene1, scene2;

    @Override
    public void start(Stage primaryStage) {

        primaryStage.setTitle("test");
        Main_Screen MainScreen = new Main_Screen(800, 480);

        scene1 = MainScreen.MainScreen(primaryStage);
        MainScreen.getInitiater().Calibration_Listener(() -> {
            primaryStage.setScene(scene2);
            primaryStage.show();
        });

        Label label2 = new Label("This is the second scene");
        Button button2 = new Button("Go to scene 1");
        button2.setOnAction(e -> {
            MainScreen.getInitiater().Back_Button_Pressed();
            primaryStage.setScene(scene1);
            primaryStage.show();
        });
        VBox layout2 = new VBox(20);
        layout2.getChildren().addAll(label2, button2);
        scene2 = new Scene(layout2, 800, 480);

        primaryStage.setScene(scene1);
        primaryStage.show();
    }
    public static void main(String[] args) {
        Application.launch(args);
    }
}
//custom listeners
interface Calibration {
    void menu();
}
interface Back {
    void back();
}
class Initiater {
    private List<Calibration> calilist = new ArrayList<Calibration>();;
    private List<Back> Go_Back = new ArrayList<Back>();

    public void Calibration_Listener(Calibration toAdd) {
        calilist.add(toAdd);
    }
    public void Back_Listener(Back toAdd) {
        Go_Back.add(toAdd);
    }

    public void Calibration_Pressed() {
        System.out.println("Calibration Pressed");
        for (Calibration hl : calilist)
            hl.menu();
    }
    public void Back_Button_Pressed() {
        System.out.println("Back Pressed");
        for (Back hl : Go_Back)
            hl.back();
    }
}

//animations and setup
class Main_Screen {
    Initiater initiater;
    private int X;
    private int Y;

    public Main_Screen(int X, int Y) {
        this.initiater = new Initiater();
        this.X = X;
        this.Y = Y;
    }

    private Pane UI_Button(String name, int[] pos, int[] size, Color color) {
        final Text text = new Text(0, 0, name);
        text.setFont(new Font(20));

        final Rectangle outline = new Rectangle(0, 0, size[0], size[1]);
        outline.setFill(color);

        final StackPane stack = new StackPane();
        stack.getChildren().addAll(outline, text);
        stack.setLayoutX(pos[0]);
        stack.setLayoutY(pos[1]);

        return stack;
    }

    public int getX() {
        return this.X;
    }

    public int getY() {
        return this.Y;
    }

    public Initiater getInitiater() {
        return this.initiater;
    }

    public Scene MainScreen(Stage stage) {
        Scene scene = new Scene(new Group(), getX(), getY());
        scene.setFill(Color.WHITE);

        Pane Start = UI_Button("Start", new int[] { getX() - getX() / 4, 0 }, new int[] { getX() / 4, getY() / 4 }, Color.rgb(255, 0, 0));
        Pane Calibration = UI_Button("Callibration", new int[] { 0, ((getY() + (getY() / 8)) / 4) * 0 }, new int[] { getX() / 2, getY() / 4 }, Color.rgb(60, 208, 230));

        System.out.println(Calibration.boundsInLocalProperty());
        ((Group) scene.getRoot()).getChildren().addAll(Calibration, Start);

        final Timeline Main_Button_Animation = new Timeline();
        final KeyFrame Cali_kf = new KeyFrame(Duration.millis(500), new KeyValue(Calibration.translateXProperty(), getX() / 2));
        final KeyFrame Start_kf = new KeyFrame(Duration.millis(500), new KeyValue(Start.translateXProperty(), -getX() / 4));
        Main_Button_Animation.getKeyFrames().addAll(Cali_kf, Start_kf);
        Main_Button_Animation.setRate(-1);
        Main_Button_Animation.jumpTo(Main_Button_Animation.getTotalDuration());
        Main_Button_Animation.play();

        Calibration.setOnMouseClicked(MouseEvent -> {
            Calibration.setDisable(true);
            Start.setDisable(true);
            System.out.println(Calibration.boundsInLocalProperty());
            Main_Button_Animation.setRate(1);
            Main_Button_Animation.jumpTo(Duration.millis(0));
            Main_Button_Animation.play();
            Main_Button_Animation.setOnFinished(event -> {
                this.initiater.Calibration_Pressed();
                System.out.println("done");
            });
        });
        this.initiater.Back_Listener(() -> {
            Main_Button_Animation.setRate(-1);
            Main_Button_Animation.jumpTo(Main_Button_Animation.getTotalDuration());
            Main_Button_Animation.play();
            Main_Button_Animation.setOnFinished(e -> {
                Calibration.setDisable(false);
                Start.setDisable(false);
            });
        });
        return scene;
    }

}

You only define KeyValue s for the frame at the end of the Timeline animation. 您只能在Timeline动画的末尾为帧定义KeyValue This only allows the Timeline to interpolate between the value at the start of the animation and the target value. 这仅允许Timeline在动画开始时的值和目标值之间进行插值。 Inserting another KeyFrame at the beginning of the animation should fix this issue: 在动画的开头插入另一个KeyFrame应该可以解决此问题:

public Scene MainScreen(Stage stage) {
    final Group root = new Group();

    Scene scene = new Scene(root, getX(), getY());
    scene.setFill(Color.WHITE);

    Pane Start = UI_Button("Start", new int[] { getX() * 3 / 4, 0 }, new int[] { getX() / 4, getY() / 4 }, Color.RED);
    Pane Calibration = UI_Button("Callibration", new int[] { 0, 0 }, new int[] { getX() / 2, getY() / 4 }, Color.rgb(60, 208, 230));

    System.out.println(Calibration.boundsInLocalProperty());
    root.getChildren().addAll(Calibration, Start);

    final Timeline Main_Button_Animation = new Timeline(
            new KeyFrame(Duration.ZERO,
                    new KeyValue(Calibration.translateXProperty(), 0),
                    new KeyValue(Start.translateXProperty(), 0)),
            new KeyFrame(Duration.millis(500),
                    new KeyValue(Calibration.translateXProperty(), getX() / 2),
                    new KeyValue(Start.translateXProperty(), -getX() / 4)));

    Main_Button_Animation.setRate(-1);
    Main_Button_Animation.playFrom(Main_Button_Animation.getTotalDuration());

    Calibration.setOnMouseClicked(MouseEvent -> {
        Calibration.setDisable(true);
        Start.setDisable(true);
        System.out.println(Calibration.boundsInLocalProperty());
        Main_Button_Animation.playFromStart();
        Main_Button_Animation.setOnFinished(event -> {
            this.initiater.Calibration_Pressed();
            System.out.println("done");
        });
    });
    this.initiater.Back_Listener(() -> {
        Main_Button_Animation.setRate(-1);
        Main_Button_Animation.playFrom(Main_Button_Animation.getTotalDuration());
        Main_Button_Animation.setOnFinished(e -> {
            Calibration.setDisable(false);
            Start.setDisable(false);
        });
    });
    return scene;
}

Note that there have been a few additional changes here: 请注意,这里还有一些其他更改:

  • Combining KeyFrame s at the same time to a single one with multiple KeyValue s 同时将KeyFrame与具有多个KeyValue的单个组合
  • Keep a reference to the scene root to avoid the use of the getter/cast 保留对场景根的引用,以避免使用getter / cast
  • Using playFrom and playFromStart instead of jumpTo / play 使用playFromplayFromStart代替jumpTo / play
  • Simplifications of some of the parameters passed to the UI_Button method (rounding could make the result differ by 1 though) 传递给UI_Button方法的某些参数的UI_Button (四舍五入可能使结果相差1)
  • ... ...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM